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, ¬ification_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