| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/mobile_config.h" |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/json/json_reader.h" |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/login/startup_utils.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| using content::BrowserThread; |
| |
| namespace { |
| |
| // Config attributes names. |
| const char kAcceptedConfigVersion[] = "1.0"; |
| const char kDefaultAttr[] = "default"; |
| |
| // Carrier config attributes. |
| const char kCarriersAttr[] = "carriers"; |
| const char kCarrierIdsAttr[] = "ids"; |
| const char kCarrierIdAttr[] = "id"; |
| const char kTopUpURLAttr[] = "top_up_url"; |
| const char kShowPortalButtonAttr[] = "show_portal_button"; |
| const char kDealsAttr[] = "deals"; |
| |
| // Carrier deal attributes. |
| const char kDealIdAttr[] = "deal_id"; |
| const char kDealLocalesAttr[] = "locales"; |
| |
| const char kInfoURLAttr[] = "info_url"; |
| const char kNotificationCountAttr[] = "notification_count"; |
| const char kDealExpireDateAttr[] = "expire_date"; |
| const char kLocalizedContentAttr[] = "localized_content"; |
| |
| // Initial locale carrier config attributes. |
| const char kInitialLocalesAttr[] = "initial_locales"; |
| const char kSetupURLAttr[] = "setup_url"; |
| |
| // Local config properties. |
| const char kExcludeDealsAttr[] = "exclude_deals"; |
| |
| // Location of the global carrier config. |
| const char kGlobalCarrierConfigPath[] = |
| "/usr/share/chromeos-assets/mobile/carrier_config.json"; |
| |
| // Location of the local carrier config. |
| const char kLocalCarrierConfigPath[] = |
| "/opt/oem/etc/carrier_config.json"; |
| |
| } // anonymous namespace |
| |
| namespace chromeos { |
| |
| // MobileConfig::CarrierDeal implementation. ----------------------------------- |
| |
| MobileConfig::CarrierDeal::CarrierDeal(const base::DictionaryValue* deal_dict) |
| : notification_count_(0), |
| localized_strings_(NULL) { |
| deal_dict->GetString(kDealIdAttr, &deal_id_); |
| |
| // Extract list of deal locales. |
| const base::ListValue* locale_list = NULL; |
| if (deal_dict->GetList(kDealLocalesAttr, &locale_list)) { |
| for (size_t i = 0; i < locale_list->GetSize(); ++i) { |
| std::string locale; |
| if (locale_list->GetString(i, &locale)) |
| locales_.push_back(locale); |
| } |
| } |
| |
| deal_dict->GetString(kInfoURLAttr, &info_url_); |
| deal_dict->GetInteger(kNotificationCountAttr, ¬ification_count_); |
| std::string date_string; |
| if (deal_dict->GetString(kDealExpireDateAttr, &date_string)) { |
| if (!base::Time::FromString(date_string.c_str(), &expire_date_)) |
| LOG(ERROR) << "Error parsing deal_expire_date: " << date_string; |
| } |
| deal_dict->GetDictionary(kLocalizedContentAttr, &localized_strings_); |
| } |
| |
| MobileConfig::CarrierDeal::~CarrierDeal() { |
| } |
| |
| std::string MobileConfig::CarrierDeal::GetLocalizedString( |
| const std::string& locale, const std::string& id) const { |
| std::string result; |
| if (localized_strings_) { |
| const base::DictionaryValue* locale_dict = NULL; |
| if (localized_strings_->GetDictionary(locale, &locale_dict) && |
| locale_dict->GetString(id, &result)) { |
| return result; |
| } else if (localized_strings_->GetDictionary(kDefaultAttr, &locale_dict) && |
| locale_dict->GetString(id, &result)) { |
| return result; |
| } |
| } |
| return result; |
| } |
| |
| // MobileConfig::Carrier implementation. --------------------------------------- |
| |
| MobileConfig::Carrier::Carrier(const base::DictionaryValue* carrier_dict, |
| const std::string& initial_locale) |
| : show_portal_button_(false) { |
| InitFromDictionary(carrier_dict, initial_locale); |
| } |
| |
| MobileConfig::Carrier::~Carrier() { |
| RemoveDeals(); |
| } |
| |
| const MobileConfig::CarrierDeal* MobileConfig::Carrier::GetDefaultDeal() const { |
| // TODO(nkostylev): Use carrier "default_deal_id" attribute. |
| CarrierDeals::const_iterator iter = deals_.begin(); |
| if (iter != deals_.end()) |
| return GetDeal((*iter).first); |
| else |
| return NULL; |
| } |
| |
| const MobileConfig::CarrierDeal* MobileConfig::Carrier::GetDeal( |
| const std::string& deal_id) const { |
| CarrierDeals::const_iterator iter = deals_.find(deal_id); |
| if (iter != deals_.end()) { |
| CarrierDeal* deal = iter->second; |
| // Make sure that deal is still active, |
| // i.e. if deal expire date is defined, check it. |
| if (!deal->expire_date().is_null() && |
| deal->expire_date() <= base::Time::Now()) { |
| return NULL; |
| } |
| return deal; |
| } else { |
| return NULL; |
| } |
| } |
| |
| void MobileConfig::Carrier::InitFromDictionary( |
| const base::DictionaryValue* carrier_dict, |
| const std::string& initial_locale) { |
| carrier_dict->GetString(kTopUpURLAttr, &top_up_url_); |
| carrier_dict->GetBoolean(kShowPortalButtonAttr, &show_portal_button_); |
| |
| bool exclude_deals = false; |
| if (carrier_dict->GetBoolean(kExcludeDealsAttr, &exclude_deals) && |
| exclude_deals) { |
| RemoveDeals(); |
| } |
| |
| // Extract list of external IDs for this carrier. |
| const base::ListValue* id_list = NULL; |
| if (carrier_dict->GetList(kCarrierIdsAttr, &id_list)) { |
| for (size_t i = 0; i < id_list->GetSize(); ++i) { |
| const base::DictionaryValue* id_dict = NULL; |
| std::string external_id; |
| if (id_list->GetDictionary(i, &id_dict) && |
| id_dict->GetString(kCarrierIdAttr, &external_id)) { |
| external_ids_.push_back(external_id); |
| } |
| } |
| } |
| |
| // Extract list of deals for this carrier. |
| const base::ListValue* deals_list = NULL; |
| if (carrier_dict->GetList(kDealsAttr, &deals_list)) { |
| for (size_t i = 0; i < deals_list->GetSize(); ++i) { |
| const base::DictionaryValue* deal_dict = NULL; |
| if (deals_list->GetDictionary(i, &deal_dict)) { |
| scoped_ptr<CarrierDeal> deal(new CarrierDeal(deal_dict)); |
| // Filter out deals by initial_locale right away. |
| std::vector<std::string>::const_iterator iter = |
| std::find(deal->locales().begin(), |
| deal->locales().end(), |
| initial_locale); |
| if (iter != deal->locales().end()) { |
| const std::string& deal_id = deal->deal_id(); |
| deals_[deal_id] = deal.release(); |
| } |
| } |
| } |
| } |
| } |
| |
| void MobileConfig::Carrier::RemoveDeals() { |
| STLDeleteValues(&deals_); |
| } |
| |
| // MobileConfig::LocaleConfig implementation. ---------------------------------- |
| |
| MobileConfig::LocaleConfig::LocaleConfig(base::DictionaryValue* locale_dict) { |
| InitFromDictionary(locale_dict); |
| } |
| |
| MobileConfig::LocaleConfig::~LocaleConfig() { |
| } |
| |
| void MobileConfig::LocaleConfig::InitFromDictionary( |
| base::DictionaryValue* locale_dict) { |
| locale_dict->GetString(kSetupURLAttr, &setup_url_); |
| } |
| |
| // MobileConfig implementation, public ----------------------------------------- |
| |
| // static |
| MobileConfig* MobileConfig::GetInstance() { |
| return Singleton<MobileConfig, |
| DefaultSingletonTraits<MobileConfig> >::get(); |
| } |
| |
| const MobileConfig::Carrier* MobileConfig::GetCarrier( |
| const std::string& carrier_id) const { |
| CarrierIdMap::const_iterator id_iter = carrier_id_map_.find(carrier_id); |
| std::string internal_id; |
| if (id_iter != carrier_id_map_.end()) |
| internal_id = id_iter->second; |
| else |
| return NULL; |
| Carriers::const_iterator iter = carriers_.find(internal_id); |
| if (iter != carriers_.end()) |
| return iter->second; |
| else |
| return NULL; |
| } |
| |
| const MobileConfig::LocaleConfig* MobileConfig::GetLocaleConfig() const { |
| return locale_config_.get(); |
| } |
| |
| // MobileConfig implementation, protected -------------------------------------- |
| |
| bool MobileConfig::LoadManifestFromString(const std::string& manifest) { |
| if (!CustomizationDocument::LoadManifestFromString(manifest)) |
| return false; |
| |
| // Local config specific attribute. |
| bool exclude_deals = false; |
| if (root_.get() && |
| root_->GetBoolean(kExcludeDealsAttr, &exclude_deals) && |
| exclude_deals) { |
| for (Carriers::iterator iter = carriers_.begin(); |
| iter != carriers_.end(); ++iter) { |
| iter->second->RemoveDeals(); |
| } |
| } |
| |
| // Other parts are optional and are the same among global/local config. |
| base::DictionaryValue* carriers = NULL; |
| if (root_.get() && root_->GetDictionary(kCarriersAttr, &carriers)) { |
| for (base::DictionaryValue::Iterator iter(*carriers); !iter.IsAtEnd(); |
| iter.Advance()) { |
| const base::DictionaryValue* carrier_dict = NULL; |
| if (iter.value().GetAsDictionary(&carrier_dict)) { |
| const std::string& internal_id = iter.key(); |
| Carriers::iterator inner_iter = carriers_.find(internal_id); |
| if (inner_iter != carriers_.end()) { |
| // Carrier already defined i.e. loading from the local config. |
| // New ID mappings in local config is not supported. |
| inner_iter->second->InitFromDictionary(carrier_dict, initial_locale_); |
| } else { |
| Carrier* carrier = new Carrier(carrier_dict, initial_locale_); |
| if (!carrier->external_ids().empty()) { |
| // Map all external IDs to a single internal one. |
| for (std::vector<std::string>::const_iterator |
| i = carrier->external_ids().begin(); |
| i != carrier->external_ids().end(); ++i) { |
| carrier_id_map_[*i] = internal_id; |
| } |
| } else { |
| // Trivial case - using same ID for external/internal one. |
| carrier_id_map_[internal_id] = internal_id; |
| } |
| carriers_[internal_id] = carrier; |
| } |
| } |
| } |
| } |
| |
| base::DictionaryValue* initial_locales = NULL; |
| if (root_.get() && root_->GetDictionary(kInitialLocalesAttr, |
| &initial_locales)) { |
| base::DictionaryValue* locale_config_dict = NULL; |
| // Search for a config based on current initial locale. |
| if (initial_locales->GetDictionary(initial_locale_, |
| &locale_config_dict)) { |
| locale_config_.reset(new LocaleConfig(locale_config_dict)); |
| } |
| } |
| |
| return true; |
| } |
| |
| // MobileConfig implementation, private ---------------------------------------- |
| |
| MobileConfig::MobileConfig() |
| : CustomizationDocument(kAcceptedConfigVersion), |
| initial_locale_(StartupUtils::GetInitialLocale()) { |
| LoadConfig(); |
| } |
| |
| MobileConfig::MobileConfig(const std::string& config, |
| const std::string& initial_locale) |
| : CustomizationDocument(kAcceptedConfigVersion), |
| initial_locale_(initial_locale) { |
| LoadManifestFromString(config); |
| } |
| |
| MobileConfig::~MobileConfig() { |
| STLDeleteValues(&carriers_); |
| } |
| |
| void MobileConfig::LoadConfig() { |
| BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
| base::Bind(&MobileConfig::ReadConfigInBackground, |
| base::Unretained(this), // this class is a singleton. |
| base::FilePath(kGlobalCarrierConfigPath), |
| base::FilePath(kLocalCarrierConfigPath))); |
| } |
| |
| void MobileConfig::ProcessConfig(const std::string& global_config, |
| const std::string& local_config) { |
| // Global config is mandatory, local config is optional. |
| bool global_initialized = false; |
| bool local_initialized = true; |
| scoped_ptr<base::DictionaryValue> global_config_root; |
| |
| if (!global_config.empty()) { |
| global_initialized = LoadManifestFromString(global_config); |
| // Backup global config root as it might be |
| // owerwritten while loading local config. |
| global_config_root.reset(root_.release()); |
| } |
| if (!local_config.empty()) |
| local_initialized = LoadManifestFromString(local_config); |
| |
| // Treat any parser errors as fatal. |
| if (!global_initialized || !local_initialized) { |
| root_.reset(NULL); |
| local_config_root_.reset(NULL); |
| } else { |
| local_config_root_.reset(root_.release()); |
| root_.reset(global_config_root.release()); |
| } |
| } |
| |
| void MobileConfig::ReadConfigInBackground( |
| const base::FilePath& global_config_file, |
| const base::FilePath& local_config_file) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| std::string global_config; |
| std::string local_config; |
| if (!base::ReadFileToString(global_config_file, &global_config)) { |
| VLOG(1) << "Failed to load global mobile config from: " |
| << global_config_file.value(); |
| } |
| if (!base::ReadFileToString(local_config_file, &local_config)) { |
| VLOG(1) << "Failed to load local mobile config from: " |
| << local_config_file.value(); |
| } |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::Bind(&MobileConfig::ProcessConfig, |
| base::Unretained(this), // singleton. |
| global_config, |
| local_config)); |
| } |
| |
| } // namespace chromeos |