普通文本  |  377行  |  12.46 KB

// 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 DictionaryValue* deal_dict)
    : notification_count_(0),
      localized_strings_(NULL) {
  deal_dict->GetString(kDealIdAttr, &deal_id_);

  // Extract list of deal locales.
  const 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, &notification_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 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 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 ListValue* id_list = NULL;
  if (carrier_dict->GetList(kCarrierIdsAttr, &id_list)) {
    for (size_t i = 0; i < id_list->GetSize(); ++i) {
      const 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 ListValue* deals_list = NULL;
  if (carrier_dict->GetList(kDealsAttr, &deals_list)) {
    for (size_t i = 0; i < deals_list->GetSize(); ++i) {
      const 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(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.
  DictionaryValue* carriers = NULL;
  if (root_.get() && root_->GetDictionary(kCarriersAttr, &carriers)) {
    for (DictionaryValue::Iterator iter(*carriers); !iter.IsAtEnd();
         iter.Advance()) {
      const 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;
        }
      }
    }
  }

  DictionaryValue* initial_locales = NULL;
  if (root_.get() && root_->GetDictionary(kInitialLocalesAttr,
                                          &initial_locales)) {
    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