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