Home | History | Annotate | Download | only in chromeos
      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/customization_document.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/file_util.h"
     10 #include "base/files/file_path.h"
     11 #include "base/json/json_reader.h"
     12 #include "base/logging.h"
     13 #include "base/prefs/pref_registry_simple.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/time/time.h"
     18 #include "chrome/browser/browser_process.h"
     19 #include "chrome/browser/chromeos/login/wizard_controller.h"
     20 #include "chrome/browser/profiles/profile_manager.h"
     21 #include "chromeos/network/network_state.h"
     22 #include "chromeos/network/network_state_handler.h"
     23 #include "chromeos/system/statistics_provider.h"
     24 #include "content/public/browser/browser_thread.h"
     25 #include "net/url_request/url_fetcher.h"
     26 
     27 using content::BrowserThread;
     28 
     29 // Manifest attributes names.
     30 
     31 namespace {
     32 
     33 const char kVersionAttr[] = "version";
     34 const char kDefaultAttr[] = "default";
     35 const char kInitialLocaleAttr[] = "initial_locale";
     36 const char kInitialTimezoneAttr[] = "initial_timezone";
     37 const char kKeyboardLayoutAttr[] = "keyboard_layout";
     38 const char kRegistrationUrlAttr[] = "registration_url";
     39 const char kHwidMapAttr[] = "hwid_map";
     40 const char kHwidMaskAttr[] = "hwid_mask";
     41 const char kSetupContentAttr[] = "setup_content";
     42 const char kHelpPageAttr[] = "help_page";
     43 const char kEulaPageAttr[] = "eula_page";
     44 const char kAppContentAttr[] = "app_content";
     45 const char kInitialStartPageAttr[] = "initial_start_page";
     46 const char kSupportPageAttr[] = "support_page";
     47 
     48 const char kAcceptedManifestVersion[] = "1.0";
     49 
     50 // Path to OEM partner startup customization manifest.
     51 const char kStartupCustomizationManifestPath[] =
     52     "/opt/oem/etc/startup_manifest.json";
     53 
     54 // URL where to fetch OEM services customization manifest from.
     55 const char kServicesCustomizationManifestUrl[] =
     56     "file:///opt/oem/etc/services_manifest.json";
     57 
     58 // Name of local state option that tracks if services customization has been
     59 // applied.
     60 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
     61 
     62 // Maximum number of retries to fetch file if network is not available.
     63 const int kMaxFetchRetries = 3;
     64 
     65 // Delay between file fetch retries if network is not available.
     66 const int kRetriesDelayInSec = 2;
     67 
     68 }  // anonymous namespace
     69 
     70 namespace chromeos {
     71 
     72 // CustomizationDocument implementation. ---------------------------------------
     73 
     74 CustomizationDocument::CustomizationDocument(
     75     const std::string& accepted_version)
     76     : accepted_version_(accepted_version) {}
     77 
     78 CustomizationDocument::~CustomizationDocument() {}
     79 
     80 bool CustomizationDocument::LoadManifestFromFile(
     81     const base::FilePath& manifest_path) {
     82   std::string manifest;
     83   if (!base::ReadFileToString(manifest_path, &manifest))
     84     return false;
     85   return LoadManifestFromString(manifest);
     86 }
     87 
     88 bool CustomizationDocument::LoadManifestFromString(
     89     const std::string& manifest) {
     90   int error_code = 0;
     91   std::string error;
     92   scoped_ptr<Value> root(base::JSONReader::ReadAndReturnError(manifest,
     93       base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error));
     94   if (error_code != base::JSONReader::JSON_NO_ERROR)
     95     LOG(ERROR) << error;
     96   DCHECK(root.get() != NULL);
     97   if (root.get() == NULL)
     98     return false;
     99   DCHECK(root->GetType() == Value::TYPE_DICTIONARY);
    100   if (root->GetType() == Value::TYPE_DICTIONARY) {
    101     root_.reset(static_cast<DictionaryValue*>(root.release()));
    102     std::string result;
    103     if (root_->GetString(kVersionAttr, &result) &&
    104         result == accepted_version_)
    105       return true;
    106 
    107     LOG(ERROR) << "Wrong customization manifest version";
    108     root_.reset(NULL);
    109   }
    110   return false;
    111 }
    112 
    113 std::string CustomizationDocument::GetLocaleSpecificString(
    114     const std::string& locale,
    115     const std::string& dictionary_name,
    116     const std::string& entry_name) const {
    117   DictionaryValue* dictionary_content = NULL;
    118   if (!root_.get() ||
    119       !root_->GetDictionary(dictionary_name, &dictionary_content))
    120     return std::string();
    121 
    122   DictionaryValue* locale_dictionary = NULL;
    123   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
    124     std::string result;
    125     if (locale_dictionary->GetString(entry_name, &result))
    126       return result;
    127   }
    128 
    129   DictionaryValue* default_dictionary = NULL;
    130   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
    131     std::string result;
    132     if (default_dictionary->GetString(entry_name, &result))
    133       return result;
    134   }
    135 
    136   return std::string();
    137 }
    138 
    139 // StartupCustomizationDocument implementation. --------------------------------
    140 
    141 StartupCustomizationDocument::StartupCustomizationDocument()
    142     : CustomizationDocument(kAcceptedManifestVersion) {
    143   {
    144     // Loading manifest causes us to do blocking IO on UI thread.
    145     // Temporarily allow it until we fix http://crosbug.com/11103
    146     base::ThreadRestrictions::ScopedAllowIO allow_io;
    147     LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath));
    148   }
    149   Init(chromeos::system::StatisticsProvider::GetInstance());
    150 }
    151 
    152 StartupCustomizationDocument::StartupCustomizationDocument(
    153     chromeos::system::StatisticsProvider* statistics_provider,
    154     const std::string& manifest)
    155     : CustomizationDocument(kAcceptedManifestVersion) {
    156   LoadManifestFromString(manifest);
    157   Init(statistics_provider);
    158 }
    159 
    160 StartupCustomizationDocument::~StartupCustomizationDocument() {}
    161 
    162 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
    163   return Singleton<StartupCustomizationDocument,
    164       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
    165 }
    166 
    167 void StartupCustomizationDocument::Init(
    168     chromeos::system::StatisticsProvider* statistics_provider) {
    169   if (IsReady()) {
    170     root_->GetString(kInitialLocaleAttr, &initial_locale_);
    171     root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
    172     root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
    173     root_->GetString(kRegistrationUrlAttr, &registration_url_);
    174 
    175     std::string hwid;
    176     if (statistics_provider->GetMachineStatistic(
    177             chromeos::system::kHardwareClassKey, &hwid)) {
    178       ListValue* hwid_list = NULL;
    179       if (root_->GetList(kHwidMapAttr, &hwid_list)) {
    180         for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
    181           DictionaryValue* hwid_dictionary = NULL;
    182           std::string hwid_mask;
    183           if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
    184               hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
    185             if (MatchPattern(hwid, hwid_mask)) {
    186               // If HWID for this machine matches some mask, use HWID specific
    187               // settings.
    188               std::string result;
    189               if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
    190                 initial_locale_ = result;
    191 
    192               if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
    193                 initial_timezone_ = result;
    194 
    195               if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
    196                 keyboard_layout_ = result;
    197             }
    198             // Don't break here to allow other entires to be applied if match.
    199           } else {
    200             LOG(ERROR) << "Syntax error in customization manifest";
    201           }
    202         }
    203       }
    204     } else {
    205       LOG(ERROR) << "HWID is missing in machine statistics";
    206     }
    207   }
    208 
    209   // If manifest doesn't exist still apply values from VPD.
    210   statistics_provider->GetMachineStatistic(kInitialLocaleAttr,
    211                                            &initial_locale_);
    212   statistics_provider->GetMachineStatistic(kInitialTimezoneAttr,
    213                                            &initial_timezone_);
    214   statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr,
    215                                            &keyboard_layout_);
    216 }
    217 
    218 std::string StartupCustomizationDocument::GetHelpPage(
    219     const std::string& locale) const {
    220   return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr);
    221 }
    222 
    223 std::string StartupCustomizationDocument::GetEULAPage(
    224     const std::string& locale) const {
    225   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
    226 }
    227 
    228 // ServicesCustomizationDocument implementation. -------------------------------
    229 
    230 ServicesCustomizationDocument::ServicesCustomizationDocument()
    231     : CustomizationDocument(kAcceptedManifestVersion),
    232       url_(kServicesCustomizationManifestUrl) {
    233 }
    234 
    235 ServicesCustomizationDocument::ServicesCustomizationDocument(
    236     const std::string& manifest)
    237     : CustomizationDocument(kAcceptedManifestVersion) {
    238   LoadManifestFromString(manifest);
    239 }
    240 
    241 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
    242 
    243 // static
    244 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
    245   return Singleton<ServicesCustomizationDocument,
    246       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
    247 }
    248 
    249 // static
    250 void ServicesCustomizationDocument::RegisterPrefs(
    251     PrefRegistrySimple* registry) {
    252   registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
    253 }
    254 
    255 // static
    256 bool ServicesCustomizationDocument::WasApplied() {
    257   PrefService* prefs = g_browser_process->local_state();
    258   return prefs->GetBoolean(kServicesCustomizationAppliedPref);
    259 }
    260 
    261 // static
    262 void ServicesCustomizationDocument::SetApplied(bool val) {
    263   PrefService* prefs = g_browser_process->local_state();
    264   prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
    265 }
    266 
    267 void ServicesCustomizationDocument::StartFetching() {
    268   if (url_.SchemeIsFile()) {
    269     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    270         base::Bind(&ServicesCustomizationDocument::ReadFileInBackground,
    271                    base::Unretained(this),  // this class is a singleton.
    272                    base::FilePath(url_.path())));
    273   } else {
    274     StartFileFetch();
    275   }
    276 }
    277 
    278 void ServicesCustomizationDocument::ReadFileInBackground(
    279     const base::FilePath& file) {
    280   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    281 
    282   std::string manifest;
    283   if (base::ReadFileToString(file, &manifest)) {
    284     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    285         base::Bind(
    286            base::IgnoreResult(
    287                &ServicesCustomizationDocument::LoadManifestFromString),
    288            base::Unretained(this),  // this class is a singleton.
    289            manifest));
    290   } else {
    291     VLOG(1) << "Failed to load services customization manifest from: "
    292             << file.value();
    293   }
    294 }
    295 
    296 void ServicesCustomizationDocument::StartFileFetch() {
    297   DCHECK(url_.is_valid());
    298   url_fetcher_.reset(net::URLFetcher::Create(
    299       url_, net::URLFetcher::GET, this));
    300   url_fetcher_->SetRequestContext(
    301       ProfileManager::GetDefaultProfile()->GetRequestContext());
    302   url_fetcher_->Start();
    303 }
    304 
    305 void ServicesCustomizationDocument::OnURLFetchComplete(
    306     const net::URLFetcher* source) {
    307   if (source->GetResponseCode() == 200) {
    308     std::string data;
    309     source->GetResponseAsString(&data);
    310     LoadManifestFromString(data);
    311   } else {
    312     const NetworkState* default_network =
    313         NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
    314     if (default_network && default_network->IsConnectedState() &&
    315         num_retries_ < kMaxFetchRetries) {
    316       num_retries_++;
    317       retry_timer_.Start(FROM_HERE,
    318                          base::TimeDelta::FromSeconds(kRetriesDelayInSec),
    319                          this, &ServicesCustomizationDocument::StartFileFetch);
    320       return;
    321     }
    322     LOG(ERROR) << "URL fetch for services customization failed:"
    323                << " response code = " << source->GetResponseCode()
    324                << " URL = " << source->GetURL().spec();
    325   }
    326 }
    327 
    328 bool ServicesCustomizationDocument::ApplyCustomization() {
    329   // TODO(dpolukhin): apply customized apps, exts and support page.
    330   SetApplied(true);
    331   return true;
    332 }
    333 
    334 std::string ServicesCustomizationDocument::GetInitialStartPage(
    335     const std::string& locale) const {
    336   return GetLocaleSpecificString(
    337       locale, kAppContentAttr, kInitialStartPageAttr);
    338 }
    339 
    340 std::string ServicesCustomizationDocument::GetSupportPage(
    341     const std::string& locale) const {
    342   return GetLocaleSpecificString(
    343       locale, kAppContentAttr, kSupportPageAttr);
    344 }
    345 
    346 }  // namespace chromeos
    347