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