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