Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/mobile_config.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/file_util.h"
     11 #include "base/files/file_path.h"
     12 #include "base/json/json_reader.h"
     13 #include "base/logging.h"
     14 #include "base/stl_util.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/chromeos/login/startup_utils.h"
     18 #include "content/public/browser/browser_thread.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace {
     23 
     24 // Config attributes names.
     25 const char kAcceptedConfigVersion[] = "1.0";
     26 const char kDefaultAttr[] = "default";
     27 
     28 // Carrier config attributes.
     29 const char kCarriersAttr[] = "carriers";
     30 const char kCarrierIdsAttr[] = "ids";
     31 const char kCarrierIdAttr[] = "id";
     32 const char kTopUpURLAttr[] = "top_up_url";
     33 const char kShowPortalButtonAttr[] = "show_portal_button";
     34 const char kDealsAttr[] = "deals";
     35 
     36 // Carrier deal attributes.
     37 const char kDealIdAttr[] = "deal_id";
     38 const char kDealLocalesAttr[] = "locales";
     39 
     40 const char kInfoURLAttr[] = "info_url";
     41 const char kNotificationCountAttr[] = "notification_count";
     42 const char kDealExpireDateAttr[] = "expire_date";
     43 const char kLocalizedContentAttr[] = "localized_content";
     44 
     45 // Initial locale carrier config attributes.
     46 const char kInitialLocalesAttr[] = "initial_locales";
     47 const char kSetupURLAttr[] = "setup_url";
     48 
     49 // Local config properties.
     50 const char kExcludeDealsAttr[] = "exclude_deals";
     51 
     52 // Location of the global carrier config.
     53 const char kGlobalCarrierConfigPath[] =
     54     "/usr/share/chromeos-assets/mobile/carrier_config.json";
     55 
     56 // Location of the local carrier config.
     57 const char kLocalCarrierConfigPath[] =
     58     "/opt/oem/etc/carrier_config.json";
     59 
     60 }  // anonymous namespace
     61 
     62 namespace chromeos {
     63 
     64 // MobileConfig::CarrierDeal implementation. -----------------------------------
     65 
     66 MobileConfig::CarrierDeal::CarrierDeal(const DictionaryValue* deal_dict)
     67     : notification_count_(0),
     68       localized_strings_(NULL) {
     69   deal_dict->GetString(kDealIdAttr, &deal_id_);
     70 
     71   // Extract list of deal locales.
     72   const ListValue* locale_list = NULL;
     73   if (deal_dict->GetList(kDealLocalesAttr, &locale_list)) {
     74     for (size_t i = 0; i < locale_list->GetSize(); ++i) {
     75       std::string locale;
     76       if (locale_list->GetString(i, &locale))
     77         locales_.push_back(locale);
     78     }
     79   }
     80 
     81   deal_dict->GetString(kInfoURLAttr, &info_url_);
     82   deal_dict->GetInteger(kNotificationCountAttr, &notification_count_);
     83   std::string date_string;
     84   if (deal_dict->GetString(kDealExpireDateAttr, &date_string)) {
     85     if (!base::Time::FromString(date_string.c_str(), &expire_date_))
     86       LOG(ERROR) << "Error parsing deal_expire_date: " << date_string;
     87   }
     88   deal_dict->GetDictionary(kLocalizedContentAttr, &localized_strings_);
     89 }
     90 
     91 MobileConfig::CarrierDeal::~CarrierDeal() {
     92 }
     93 
     94 std::string MobileConfig::CarrierDeal::GetLocalizedString(
     95     const std::string& locale, const std::string& id) const {
     96   std::string result;
     97   if (localized_strings_) {
     98     const DictionaryValue* locale_dict = NULL;
     99     if (localized_strings_->GetDictionary(locale, &locale_dict) &&
    100         locale_dict->GetString(id, &result)) {
    101       return result;
    102     } else if (localized_strings_->GetDictionary(kDefaultAttr, &locale_dict) &&
    103                locale_dict->GetString(id, &result)) {
    104       return result;
    105     }
    106   }
    107   return result;
    108 }
    109 
    110 // MobileConfig::Carrier implementation. ---------------------------------------
    111 
    112 MobileConfig::Carrier::Carrier(const DictionaryValue* carrier_dict,
    113                                const std::string& initial_locale)
    114     : show_portal_button_(false) {
    115   InitFromDictionary(carrier_dict, initial_locale);
    116 }
    117 
    118 MobileConfig::Carrier::~Carrier() {
    119   RemoveDeals();
    120 }
    121 
    122 const MobileConfig::CarrierDeal* MobileConfig::Carrier::GetDefaultDeal() const {
    123   // TODO(nkostylev): Use carrier "default_deal_id" attribute.
    124   CarrierDeals::const_iterator iter = deals_.begin();
    125   if (iter != deals_.end())
    126     return GetDeal((*iter).first);
    127   else
    128     return NULL;
    129 }
    130 
    131 const MobileConfig::CarrierDeal* MobileConfig::Carrier::GetDeal(
    132     const std::string& deal_id) const {
    133   CarrierDeals::const_iterator iter = deals_.find(deal_id);
    134   if (iter != deals_.end()) {
    135     CarrierDeal* deal = iter->second;
    136     // Make sure that deal is still active,
    137     // i.e. if deal expire date is defined, check it.
    138     if (!deal->expire_date().is_null() &&
    139         deal->expire_date() <= base::Time::Now()) {
    140       return NULL;
    141     }
    142     return deal;
    143   } else {
    144     return NULL;
    145   }
    146 }
    147 
    148 void MobileConfig::Carrier::InitFromDictionary(
    149     const base::DictionaryValue* carrier_dict,
    150     const std::string& initial_locale) {
    151   carrier_dict->GetString(kTopUpURLAttr, &top_up_url_);
    152   carrier_dict->GetBoolean(kShowPortalButtonAttr, &show_portal_button_);
    153 
    154   bool exclude_deals = false;
    155   if (carrier_dict->GetBoolean(kExcludeDealsAttr, &exclude_deals) &&
    156       exclude_deals) {
    157     RemoveDeals();
    158   }
    159 
    160   // Extract list of external IDs for this carrier.
    161   const ListValue* id_list = NULL;
    162   if (carrier_dict->GetList(kCarrierIdsAttr, &id_list)) {
    163     for (size_t i = 0; i < id_list->GetSize(); ++i) {
    164       const DictionaryValue* id_dict = NULL;
    165       std::string external_id;
    166       if (id_list->GetDictionary(i, &id_dict) &&
    167           id_dict->GetString(kCarrierIdAttr, &external_id)) {
    168         external_ids_.push_back(external_id);
    169       }
    170     }
    171   }
    172 
    173   // Extract list of deals for this carrier.
    174   const ListValue* deals_list = NULL;
    175   if (carrier_dict->GetList(kDealsAttr, &deals_list)) {
    176     for (size_t i = 0; i < deals_list->GetSize(); ++i) {
    177       const DictionaryValue* deal_dict = NULL;
    178       if (deals_list->GetDictionary(i, &deal_dict)) {
    179         scoped_ptr<CarrierDeal> deal(new CarrierDeal(deal_dict));
    180         // Filter out deals by initial_locale right away.
    181         std::vector<std::string>::const_iterator iter =
    182             std::find(deal->locales().begin(),
    183                       deal->locales().end(),
    184                       initial_locale);
    185         if (iter != deal->locales().end()) {
    186           const std::string& deal_id = deal->deal_id();
    187           deals_[deal_id] = deal.release();
    188         }
    189       }
    190     }
    191   }
    192 }
    193 
    194 void MobileConfig::Carrier::RemoveDeals() {
    195   STLDeleteValues(&deals_);
    196 }
    197 
    198 // MobileConfig::LocaleConfig implementation. ----------------------------------
    199 
    200 MobileConfig::LocaleConfig::LocaleConfig(DictionaryValue* locale_dict) {
    201   InitFromDictionary(locale_dict);
    202 }
    203 
    204 MobileConfig::LocaleConfig::~LocaleConfig() {
    205 }
    206 
    207 void MobileConfig::LocaleConfig::InitFromDictionary(
    208     base::DictionaryValue* locale_dict) {
    209   locale_dict->GetString(kSetupURLAttr, &setup_url_);
    210 }
    211 
    212 // MobileConfig implementation, public -----------------------------------------
    213 
    214 // static
    215 MobileConfig* MobileConfig::GetInstance() {
    216   return Singleton<MobileConfig,
    217       DefaultSingletonTraits<MobileConfig> >::get();
    218 }
    219 
    220 const MobileConfig::Carrier* MobileConfig::GetCarrier(
    221     const std::string& carrier_id) const {
    222   CarrierIdMap::const_iterator id_iter = carrier_id_map_.find(carrier_id);
    223   std::string internal_id;
    224   if (id_iter != carrier_id_map_.end())
    225     internal_id = id_iter->second;
    226   else
    227     return NULL;
    228   Carriers::const_iterator iter = carriers_.find(internal_id);
    229   if (iter != carriers_.end())
    230     return iter->second;
    231   else
    232     return NULL;
    233 }
    234 
    235 const MobileConfig::LocaleConfig* MobileConfig::GetLocaleConfig() const {
    236   return locale_config_.get();
    237 }
    238 
    239 // MobileConfig implementation, protected --------------------------------------
    240 
    241 bool MobileConfig::LoadManifestFromString(const std::string& manifest) {
    242   if (!CustomizationDocument::LoadManifestFromString(manifest))
    243     return false;
    244 
    245   // Local config specific attribute.
    246   bool exclude_deals = false;
    247   if (root_.get() &&
    248       root_->GetBoolean(kExcludeDealsAttr, &exclude_deals) &&
    249       exclude_deals) {
    250     for (Carriers::iterator iter = carriers_.begin();
    251          iter != carriers_.end(); ++iter) {
    252       iter->second->RemoveDeals();
    253     }
    254   }
    255 
    256   // Other parts are optional and are the same among global/local config.
    257   DictionaryValue* carriers = NULL;
    258   if (root_.get() && root_->GetDictionary(kCarriersAttr, &carriers)) {
    259     for (DictionaryValue::Iterator iter(*carriers); !iter.IsAtEnd();
    260          iter.Advance()) {
    261       const DictionaryValue* carrier_dict = NULL;
    262       if (iter.value().GetAsDictionary(&carrier_dict)) {
    263         const std::string& internal_id = iter.key();
    264         Carriers::iterator inner_iter = carriers_.find(internal_id);
    265         if (inner_iter != carriers_.end()) {
    266           // Carrier already defined i.e. loading from the local config.
    267           // New ID mappings in local config is not supported.
    268           inner_iter->second->InitFromDictionary(carrier_dict, initial_locale_);
    269         } else {
    270           Carrier* carrier = new Carrier(carrier_dict, initial_locale_);
    271           if (!carrier->external_ids().empty()) {
    272             // Map all external IDs to a single internal one.
    273             for (std::vector<std::string>::const_iterator
    274                  i = carrier->external_ids().begin();
    275                  i != carrier->external_ids().end(); ++i) {
    276               carrier_id_map_[*i] = internal_id;
    277             }
    278           } else {
    279             // Trivial case - using same ID for external/internal one.
    280             carrier_id_map_[internal_id] = internal_id;
    281           }
    282           carriers_[internal_id] = carrier;
    283         }
    284       }
    285     }
    286   }
    287 
    288   DictionaryValue* initial_locales = NULL;
    289   if (root_.get() && root_->GetDictionary(kInitialLocalesAttr,
    290                                           &initial_locales)) {
    291     DictionaryValue* locale_config_dict = NULL;
    292     // Search for a config based on current initial locale.
    293     if (initial_locales->GetDictionary(initial_locale_,
    294                                        &locale_config_dict)) {
    295       locale_config_.reset(new LocaleConfig(locale_config_dict));
    296     }
    297   }
    298 
    299   return true;
    300 }
    301 
    302 // MobileConfig implementation, private ----------------------------------------
    303 
    304 MobileConfig::MobileConfig()
    305     : CustomizationDocument(kAcceptedConfigVersion),
    306       initial_locale_(StartupUtils::GetInitialLocale()) {
    307   LoadConfig();
    308 }
    309 
    310 MobileConfig::MobileConfig(const std::string& config,
    311                            const std::string& initial_locale)
    312     : CustomizationDocument(kAcceptedConfigVersion),
    313       initial_locale_(initial_locale) {
    314   LoadManifestFromString(config);
    315 }
    316 
    317 MobileConfig::~MobileConfig() {
    318   STLDeleteValues(&carriers_);
    319 }
    320 
    321 void MobileConfig::LoadConfig() {
    322   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    323       base::Bind(&MobileConfig::ReadConfigInBackground,
    324                  base::Unretained(this),  // this class is a singleton.
    325                  base::FilePath(kGlobalCarrierConfigPath),
    326                  base::FilePath(kLocalCarrierConfigPath)));
    327 }
    328 
    329 void MobileConfig::ProcessConfig(const std::string& global_config,
    330                                  const std::string& local_config) {
    331   // Global config is mandatory, local config is optional.
    332   bool global_initialized = false;
    333   bool local_initialized = true;
    334   scoped_ptr<base::DictionaryValue> global_config_root;
    335 
    336   if (!global_config.empty()) {
    337     global_initialized = LoadManifestFromString(global_config);
    338     // Backup global config root as it might be
    339     // owerwritten while loading local config.
    340     global_config_root.reset(root_.release());
    341   }
    342   if (!local_config.empty())
    343     local_initialized = LoadManifestFromString(local_config);
    344 
    345   // Treat any parser errors as fatal.
    346   if (!global_initialized || !local_initialized) {
    347     root_.reset(NULL);
    348     local_config_root_.reset(NULL);
    349   } else {
    350     local_config_root_.reset(root_.release());
    351     root_.reset(global_config_root.release());
    352   }
    353 }
    354 
    355 void MobileConfig::ReadConfigInBackground(
    356     const base::FilePath& global_config_file,
    357     const base::FilePath& local_config_file) {
    358   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    359   std::string global_config;
    360   std::string local_config;
    361   if (!base::ReadFileToString(global_config_file, &global_config)) {
    362     VLOG(1) << "Failed to load global mobile config from: "
    363             << global_config_file.value();
    364   }
    365   if (!base::ReadFileToString(local_config_file, &local_config)) {
    366     VLOG(1) << "Failed to load local mobile config from: "
    367             << local_config_file.value();
    368   }
    369   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    370                           base::Bind(&MobileConfig::ProcessConfig,
    371                                      base::Unretained(this),  // singleton.
    372                                      global_config,
    373                                      local_config));
    374 }
    375 
    376 }  // namespace chromeos
    377