Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2011 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/customization_document.h"
      6 
      7 #include "base/file_path.h"
      8 #include "base/file_util.h"
      9 #include "base/json/json_reader.h"
     10 #include "base/logging.h"
     11 #include "base/string_tokenizer.h"
     12 #include "base/string_util.h"
     13 #include "base/time.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "chrome/browser/browser_process.h"
     16 #include "chrome/browser/chromeos/cros/cros_library.h"
     17 #include "chrome/browser/chromeos/cros/network_library.h"
     18 #include "chrome/browser/chromeos/login/wizard_controller.h"
     19 #include "chrome/browser/chromeos/system_access.h"
     20 #include "chrome/browser/prefs/pref_service.h"
     21 #include "chrome/browser/profiles/profile_manager.h"
     22 #include "content/browser/browser_thread.h"
     23 
     24 // Manifest attributes names.
     25 
     26 namespace {
     27 
     28 const char kVersionAttr[] = "version";
     29 const char kDefaultAttr[] = "default";
     30 const char kInitialLocaleAttr[] = "initial_locale";
     31 const char kInitialTimezoneAttr[] = "initial_timezone";
     32 const char kKeyboardLayoutAttr[] = "keyboard_layout";
     33 const char kRegistrationUrlAttr[] = "registration_url";
     34 const char kHwidMapAttr[] = "hwid_map";
     35 const char kHwidMaskAttr[] = "hwid_mask";
     36 const char kSetupContentAttr[] = "setup_content";
     37 const char kHelpPageAttr[] = "help_page";
     38 const char kEulaPageAttr[] = "eula_page";
     39 const char kAppContentAttr[] = "app_content";
     40 const char kInitialStartPageAttr[] = "initial_start_page";
     41 const char kSupportPageAttr[] = "support_page";
     42 
     43 const char kAcceptedManifestVersion[] = "1.0";
     44 
     45 const char kHwid[] = "hwid";
     46 
     47 // Carrier deals attributes.
     48 const char kCarrierDealsAttr[] = "carrier_deals";
     49 const char kDealLocaleAttr[] = "deal_locale";
     50 const char kTopUpURLAttr[] = "top_up_url";
     51 const char kNotificationCountAttr[] = "notification_count";
     52 const char kDealExpireDateAttr[] = "expire_date";
     53 const char kLocalizedContentAttr[] = "localized_content";
     54 const char kNotificationTextAttr[] = "notification_text";
     55 
     56 // Path to OEM partner startup customization manifest.
     57 const char kStartupCustomizationManifestPath[] =
     58     "/opt/oem/etc/startup_manifest.json";
     59 
     60 // URL where to fetch OEM services customization manifest from.
     61 const char kServicesCustomizationManifestUrl[] =
     62     "file:///opt/oem/etc/services_manifest.json";
     63 
     64 // Name of local state option that tracks if services customization has been
     65 // applied.
     66 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
     67 
     68 // Maximum number of retries to fetch file if network is not available.
     69 const int kMaxFetchRetries = 3;
     70 
     71 // Delay between file fetch retries if network is not available.
     72 const int kRetriesDelayInSec = 2;
     73 
     74 }  // anonymous namespace
     75 
     76 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::ServicesCustomizationDocument);
     77 
     78 namespace chromeos {
     79 
     80 // CustomizationDocument implementation. ---------------------------------------
     81 
     82 bool CustomizationDocument::LoadManifestFromFile(
     83     const FilePath& manifest_path) {
     84   std::string manifest;
     85   if (!file_util::ReadFileToString(manifest_path, &manifest))
     86     return false;
     87   return LoadManifestFromString(manifest);
     88 }
     89 
     90 bool CustomizationDocument::LoadManifestFromString(
     91     const std::string& manifest) {
     92   scoped_ptr<Value> root(base::JSONReader::Read(manifest, true));
     93   DCHECK(root.get() != NULL);
     94   if (root.get() == NULL)
     95     return false;
     96   DCHECK(root->GetType() == Value::TYPE_DICTIONARY);
     97   if (root->GetType() == Value::TYPE_DICTIONARY) {
     98     root_.reset(static_cast<DictionaryValue*>(root.release()));
     99     std::string result;
    100     if (root_->GetString(kVersionAttr, &result) &&
    101         result == kAcceptedManifestVersion)
    102       return true;
    103 
    104     LOG(ERROR) << "Wrong customization manifest version";
    105     root_.reset(NULL);
    106   }
    107   return false;
    108 }
    109 
    110 std::string CustomizationDocument::GetLocaleSpecificString(
    111     const std::string& locale,
    112     const std::string& dictionary_name,
    113     const std::string& entry_name) const {
    114   DictionaryValue* dictionary_content = NULL;
    115   if (!root_.get() ||
    116       !root_->GetDictionary(dictionary_name, &dictionary_content))
    117     return std::string();
    118 
    119   DictionaryValue* locale_dictionary = NULL;
    120   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
    121     std::string result;
    122     if (locale_dictionary->GetString(entry_name, &result))
    123       return result;
    124   }
    125 
    126   DictionaryValue* default_dictionary = NULL;
    127   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
    128     std::string result;
    129     if (default_dictionary->GetString(entry_name, &result))
    130       return result;
    131   }
    132 
    133   return std::string();
    134 }
    135 
    136 // StartupCustomizationDocument implementation. --------------------------------
    137 
    138 StartupCustomizationDocument::StartupCustomizationDocument() {
    139   {
    140     // Loading manifest causes us to do blocking IO on UI thread.
    141     // Temporarily allow it until we fix http://crosbug.com/11103
    142     base::ThreadRestrictions::ScopedAllowIO allow_io;
    143     LoadManifestFromFile(FilePath(kStartupCustomizationManifestPath));
    144   }
    145   Init(SystemAccess::GetInstance());
    146 }
    147 
    148 StartupCustomizationDocument::StartupCustomizationDocument(
    149     SystemAccess* system_access, const std::string& manifest) {
    150   LoadManifestFromString(manifest);
    151   Init(system_access);
    152 }
    153 
    154 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
    155   return Singleton<StartupCustomizationDocument,
    156       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
    157 }
    158 
    159 void StartupCustomizationDocument::Init(SystemAccess* system_access) {
    160   if (!IsReady())
    161     return;
    162 
    163   root_->GetString(kInitialLocaleAttr, &initial_locale_);
    164   root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
    165   root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
    166   root_->GetString(kRegistrationUrlAttr, &registration_url_);
    167 
    168   std::string hwid;
    169   if (system_access->GetMachineStatistic(kHwid, &hwid)) {
    170     ListValue* hwid_list = NULL;
    171     if (root_->GetList(kHwidMapAttr, &hwid_list)) {
    172       for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
    173         DictionaryValue* hwid_dictionary = NULL;
    174         std::string hwid_mask;
    175         if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
    176             hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
    177           if (MatchPattern(hwid, hwid_mask)) {
    178             // If HWID for this machine matches some mask, use HWID specific
    179             // settings.
    180             std::string result;
    181             if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
    182               initial_locale_ = result;
    183 
    184             if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
    185               initial_timezone_ = result;
    186 
    187             if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
    188               keyboard_layout_ = result;
    189           }
    190           // Don't break here to allow other entires to be applied if match.
    191         } else {
    192           LOG(ERROR) << "Syntax error in customization manifest";
    193         }
    194       }
    195     }
    196   } else {
    197     LOG(ERROR) << "HWID is missing in machine statistics";
    198   }
    199 
    200   system_access->GetMachineStatistic(kInitialLocaleAttr, &initial_locale_);
    201   system_access->GetMachineStatistic(kInitialTimezoneAttr, &initial_timezone_);
    202   system_access->GetMachineStatistic(kKeyboardLayoutAttr, &keyboard_layout_);
    203 }
    204 
    205 std::string StartupCustomizationDocument::GetHelpPage(
    206     const std::string& locale) const {
    207   return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr);
    208 }
    209 
    210 std::string StartupCustomizationDocument::GetEULAPage(
    211     const std::string& locale) const {
    212   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
    213 }
    214 
    215 // ServicesCustomizationDocument implementation. -------------------------------
    216 
    217 ServicesCustomizationDocument::CarrierDeal::CarrierDeal(
    218     DictionaryValue* deal_dict)
    219     : notification_count(0),
    220       localized_strings(NULL) {
    221   deal_dict->GetString(kDealLocaleAttr, &deal_locale);
    222   deal_dict->GetString(kTopUpURLAttr, &top_up_url);
    223   deal_dict->GetInteger(kNotificationCountAttr, &notification_count);
    224   std::string date_string;
    225   if (deal_dict->GetString(kDealExpireDateAttr, &date_string)) {
    226     if (!base::Time::FromString(ASCIIToWide(date_string).c_str(), &expire_date))
    227       LOG(ERROR) << "Error parsing deal_expire_date: " << date_string;
    228   }
    229   deal_dict->GetDictionary(kLocalizedContentAttr, &localized_strings);
    230 }
    231 
    232 std::string ServicesCustomizationDocument::CarrierDeal::GetLocalizedString(
    233     const std::string& locale, const std::string& id) const {
    234   std::string result;
    235   if (localized_strings) {
    236     DictionaryValue* locale_dict = NULL;
    237     if (localized_strings->GetDictionary(locale, &locale_dict) &&
    238         locale_dict->GetString(id, &result)) {
    239       return result;
    240     } else if (localized_strings->GetDictionary(kDefaultAttr, &locale_dict) &&
    241                locale_dict->GetString(id, &result)) {
    242       return result;
    243     }
    244   }
    245   return result;
    246 }
    247 
    248 ServicesCustomizationDocument::ServicesCustomizationDocument()
    249   : url_(kServicesCustomizationManifestUrl),
    250     initial_locale_(WizardController::GetInitialLocale()) {
    251 }
    252 
    253 ServicesCustomizationDocument::ServicesCustomizationDocument(
    254     const std::string& manifest, const std::string& initial_locale)
    255     : initial_locale_(initial_locale) {
    256   LoadManifestFromString(manifest);
    257 }
    258 
    259 // static
    260 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
    261   return Singleton<ServicesCustomizationDocument,
    262       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
    263 }
    264 
    265 // static
    266 void ServicesCustomizationDocument::RegisterPrefs(PrefService* local_state) {
    267   local_state->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
    268 }
    269 
    270 // static
    271 bool ServicesCustomizationDocument::WasApplied() {
    272   PrefService* prefs = g_browser_process->local_state();
    273   return prefs->GetBoolean(kServicesCustomizationAppliedPref);
    274 }
    275 
    276 // static
    277 void ServicesCustomizationDocument::SetApplied(bool val) {
    278   PrefService* prefs = g_browser_process->local_state();
    279   prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
    280 }
    281 
    282 void ServicesCustomizationDocument::StartFetching() {
    283   if (url_.SchemeIsFile()) {
    284     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    285         NewRunnableMethod(this,
    286             &ServicesCustomizationDocument::ReadFileInBackground,
    287             FilePath(url_.path())));
    288   } else {
    289     StartFileFetch();
    290   }
    291 }
    292 
    293 void ServicesCustomizationDocument::ReadFileInBackground(const FilePath& file) {
    294   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    295 
    296   std::string manifest;
    297   if (file_util::ReadFileToString(file, &manifest)) {
    298     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    299         NewRunnableMethod(
    300             this,
    301             &ServicesCustomizationDocument::LoadManifestFromString,
    302             manifest));
    303   } else {
    304     VLOG(1) << "Failed to load services customization manifest from: "
    305             << file.value();
    306   }
    307 }
    308 
    309 void ServicesCustomizationDocument::StartFileFetch() {
    310   DCHECK(url_.is_valid());
    311   url_fetcher_.reset(new URLFetcher(url_, URLFetcher::GET, this));
    312   url_fetcher_->set_request_context(
    313       ProfileManager::GetDefaultProfile()->GetRequestContext());
    314   url_fetcher_->Start();
    315 }
    316 
    317 void ServicesCustomizationDocument::OnURLFetchComplete(
    318     const URLFetcher* source,
    319     const GURL& url,
    320     const net::URLRequestStatus& status,
    321     int response_code,
    322     const ResponseCookies& cookies,
    323     const std::string& data) {
    324   if (response_code == 200) {
    325     LoadManifestFromString(data);
    326   } else {
    327     NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary();
    328     if (!network->Connected() && num_retries_ < kMaxFetchRetries) {
    329       num_retries_++;
    330       retry_timer_.Start(base::TimeDelta::FromSeconds(kRetriesDelayInSec),
    331                          this, &ServicesCustomizationDocument::StartFileFetch);
    332       return;
    333     }
    334     LOG(ERROR) << "URL fetch for services customization failed:"
    335                << " response code = " << response_code
    336                << " URL = " << url.spec();
    337   }
    338 }
    339 
    340 bool ServicesCustomizationDocument::ApplyCustomization() {
    341   // TODO(dpolukhin): apply customized apps, exts and support page.
    342   SetApplied(true);
    343   return true;
    344 }
    345 
    346 std::string ServicesCustomizationDocument::GetInitialStartPage(
    347     const std::string& locale) const {
    348   return GetLocaleSpecificString(
    349       locale, kAppContentAttr, kInitialStartPageAttr);
    350 }
    351 
    352 std::string ServicesCustomizationDocument::GetSupportPage(
    353     const std::string& locale) const {
    354   return GetLocaleSpecificString(
    355       locale, kAppContentAttr, kSupportPageAttr);
    356 }
    357 
    358 const ServicesCustomizationDocument::CarrierDeal*
    359 ServicesCustomizationDocument::GetCarrierDeal(const std::string& carrier_id,
    360                                               bool check_restrictions) const {
    361   CarrierDeals::const_iterator iter = carrier_deals_.find(carrier_id);
    362   if (iter != carrier_deals_.end()) {
    363     CarrierDeal* deal = iter->second;
    364     if (check_restrictions) {
    365       // Deal locale has to match initial_locale (= launch country).
    366       if (initial_locale_ != deal->deal_locale)
    367         return NULL;
    368       // Make sure that deal is still active,
    369       // i.e. if deal expire date is defined, check it.
    370       if (!deal->expire_date.is_null() &&
    371           deal->expire_date <= base::Time::Now()) {
    372         return NULL;
    373       }
    374     }
    375     return deal;
    376   } else {
    377     return NULL;
    378   }
    379 }
    380 
    381 bool ServicesCustomizationDocument::LoadManifestFromString(
    382     const std::string& manifest) {
    383   if (!CustomizationDocument::LoadManifestFromString(manifest))
    384     return false;
    385 
    386   DictionaryValue* carriers = NULL;
    387   if (root_.get() && root_->GetDictionary(kCarrierDealsAttr, &carriers)) {
    388     for (DictionaryValue::key_iterator iter = carriers->begin_keys();
    389          iter != carriers->end_keys(); ++iter) {
    390      DictionaryValue* carrier_deal = NULL;
    391      if (carriers->GetDictionary(*iter, &carrier_deal)) {
    392        carrier_deals_[*iter] = new CarrierDeal(carrier_deal);
    393      }
    394    }
    395   }
    396   return true;
    397 }
    398 
    399 }  // namespace chromeos
    400