mobile_config.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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
20using content::BrowserThread;
21
22namespace {
23
24// Config attributes names.
25const char kAcceptedConfigVersion[] = "1.0";
26const char kDefaultAttr[] = "default";
27
28// Carrier config attributes.
29const char kCarriersAttr[] = "carriers";
30const char kCarrierIdsAttr[] = "ids";
31const char kCarrierIdAttr[] = "id";
32const char kTopUpURLAttr[] = "top_up_url";
33const char kShowPortalButtonAttr[] = "show_portal_button";
34const char kDealsAttr[] = "deals";
35
36// Carrier deal attributes.
37const char kDealIdAttr[] = "deal_id";
38const char kDealLocalesAttr[] = "locales";
39
40const char kInfoURLAttr[] = "info_url";
41const char kNotificationCountAttr[] = "notification_count";
42const char kDealExpireDateAttr[] = "expire_date";
43const char kLocalizedContentAttr[] = "localized_content";
44
45// Initial locale carrier config attributes.
46const char kInitialLocalesAttr[] = "initial_locales";
47const char kSetupURLAttr[] = "setup_url";
48
49// Local config properties.
50const char kExcludeDealsAttr[] = "exclude_deals";
51
52// Location of the global carrier config.
53const char kGlobalCarrierConfigPath[] =
54    "/usr/share/chromeos-assets/mobile/carrier_config.json";
55
56// Location of the local carrier config.
57const char kLocalCarrierConfigPath[] =
58    "/opt/oem/etc/carrier_config.json";
59
60}  // anonymous namespace
61
62namespace chromeos {
63
64// MobileConfig::CarrierDeal implementation. -----------------------------------
65
66MobileConfig::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
91MobileConfig::CarrierDeal::~CarrierDeal() {
92}
93
94std::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
112MobileConfig::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
118MobileConfig::Carrier::~Carrier() {
119  RemoveDeals();
120}
121
122const 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
131const 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
148void 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
194void MobileConfig::Carrier::RemoveDeals() {
195  STLDeleteValues(&deals_);
196}
197
198// MobileConfig::LocaleConfig implementation. ----------------------------------
199
200MobileConfig::LocaleConfig::LocaleConfig(DictionaryValue* locale_dict) {
201  InitFromDictionary(locale_dict);
202}
203
204MobileConfig::LocaleConfig::~LocaleConfig() {
205}
206
207void MobileConfig::LocaleConfig::InitFromDictionary(
208    base::DictionaryValue* locale_dict) {
209  locale_dict->GetString(kSetupURLAttr, &setup_url_);
210}
211
212// MobileConfig implementation, public -----------------------------------------
213
214// static
215MobileConfig* MobileConfig::GetInstance() {
216  return Singleton<MobileConfig,
217      DefaultSingletonTraits<MobileConfig> >::get();
218}
219
220const 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
235const MobileConfig::LocaleConfig* MobileConfig::GetLocaleConfig() const {
236  return locale_config_.get();
237}
238
239// MobileConfig implementation, protected --------------------------------------
240
241bool 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
304MobileConfig::MobileConfig()
305    : CustomizationDocument(kAcceptedConfigVersion),
306      initial_locale_(StartupUtils::GetInitialLocale()) {
307  LoadConfig();
308}
309
310MobileConfig::MobileConfig(const std::string& config,
311                           const std::string& initial_locale)
312    : CustomizationDocument(kAcceptedConfigVersion),
313      initial_locale_(initial_locale) {
314  LoadManifestFromString(config);
315}
316
317MobileConfig::~MobileConfig() {
318  STLDeleteValues(&carriers_);
319}
320
321void 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
329void 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
355void 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