// 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/customization_document.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/json/json_reader.h" #include "base/logging.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/system/statistics_provider.h" #include "chrome/browser/profiles/profile_manager.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "content/public/browser/browser_thread.h" #include "net/url_request/url_fetcher.h" using content::BrowserThread; // Manifest attributes names. namespace { const char kVersionAttr[] = "version"; const char kDefaultAttr[] = "default"; const char kInitialLocaleAttr[] = "initial_locale"; const char kInitialTimezoneAttr[] = "initial_timezone"; const char kKeyboardLayoutAttr[] = "keyboard_layout"; const char kRegistrationUrlAttr[] = "registration_url"; const char kHwidMapAttr[] = "hwid_map"; const char kHwidMaskAttr[] = "hwid_mask"; const char kSetupContentAttr[] = "setup_content"; const char kHelpPageAttr[] = "help_page"; const char kEulaPageAttr[] = "eula_page"; const char kAppContentAttr[] = "app_content"; const char kInitialStartPageAttr[] = "initial_start_page"; const char kSupportPageAttr[] = "support_page"; const char kAcceptedManifestVersion[] = "1.0"; // Path to OEM partner startup customization manifest. const char kStartupCustomizationManifestPath[] = "/opt/oem/etc/startup_manifest.json"; // URL where to fetch OEM services customization manifest from. const char kServicesCustomizationManifestUrl[] = "file:///opt/oem/etc/services_manifest.json"; // Name of local state option that tracks if services customization has been // applied. const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied"; // Maximum number of retries to fetch file if network is not available. const int kMaxFetchRetries = 3; // Delay between file fetch retries if network is not available. const int kRetriesDelayInSec = 2; } // anonymous namespace namespace chromeos { // CustomizationDocument implementation. --------------------------------------- CustomizationDocument::CustomizationDocument( const std::string& accepted_version) : accepted_version_(accepted_version) {} CustomizationDocument::~CustomizationDocument() {} bool CustomizationDocument::LoadManifestFromFile( const base::FilePath& manifest_path) { std::string manifest; if (!base::ReadFileToString(manifest_path, &manifest)) return false; return LoadManifestFromString(manifest); } bool CustomizationDocument::LoadManifestFromString( const std::string& manifest) { int error_code = 0; std::string error; scoped_ptr root(base::JSONReader::ReadAndReturnError(manifest, base::JSON_ALLOW_TRAILING_COMMAS, &error_code, &error)); if (error_code != base::JSONReader::JSON_NO_ERROR) LOG(ERROR) << error; DCHECK(root.get() != NULL); if (root.get() == NULL) return false; DCHECK(root->GetType() == Value::TYPE_DICTIONARY); if (root->GetType() == Value::TYPE_DICTIONARY) { root_.reset(static_cast(root.release())); std::string result; if (root_->GetString(kVersionAttr, &result) && result == accepted_version_) return true; LOG(ERROR) << "Wrong customization manifest version"; root_.reset(NULL); } return false; } std::string CustomizationDocument::GetLocaleSpecificString( const std::string& locale, const std::string& dictionary_name, const std::string& entry_name) const { DictionaryValue* dictionary_content = NULL; if (!root_.get() || !root_->GetDictionary(dictionary_name, &dictionary_content)) return std::string(); DictionaryValue* locale_dictionary = NULL; if (dictionary_content->GetDictionary(locale, &locale_dictionary)) { std::string result; if (locale_dictionary->GetString(entry_name, &result)) return result; } DictionaryValue* default_dictionary = NULL; if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) { std::string result; if (default_dictionary->GetString(entry_name, &result)) return result; } return std::string(); } // StartupCustomizationDocument implementation. -------------------------------- StartupCustomizationDocument::StartupCustomizationDocument() : CustomizationDocument(kAcceptedManifestVersion) { { // Loading manifest causes us to do blocking IO on UI thread. // Temporarily allow it until we fix http://crosbug.com/11103 base::ThreadRestrictions::ScopedAllowIO allow_io; LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath)); } Init(chromeos::system::StatisticsProvider::GetInstance()); } StartupCustomizationDocument::StartupCustomizationDocument( chromeos::system::StatisticsProvider* statistics_provider, const std::string& manifest) : CustomizationDocument(kAcceptedManifestVersion) { LoadManifestFromString(manifest); Init(statistics_provider); } StartupCustomizationDocument::~StartupCustomizationDocument() {} StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() { return Singleton >::get(); } void StartupCustomizationDocument::Init( chromeos::system::StatisticsProvider* statistics_provider) { if (IsReady()) { root_->GetString(kInitialLocaleAttr, &initial_locale_); root_->GetString(kInitialTimezoneAttr, &initial_timezone_); root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_); root_->GetString(kRegistrationUrlAttr, ®istration_url_); std::string hwid; if (statistics_provider->GetMachineStatistic( chromeos::system::kHardwareClass, &hwid)) { ListValue* hwid_list = NULL; if (root_->GetList(kHwidMapAttr, &hwid_list)) { for (size_t i = 0; i < hwid_list->GetSize(); ++i) { DictionaryValue* hwid_dictionary = NULL; std::string hwid_mask; if (hwid_list->GetDictionary(i, &hwid_dictionary) && hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) { if (MatchPattern(hwid, hwid_mask)) { // If HWID for this machine matches some mask, use HWID specific // settings. std::string result; if (hwid_dictionary->GetString(kInitialLocaleAttr, &result)) initial_locale_ = result; if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result)) initial_timezone_ = result; if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result)) keyboard_layout_ = result; } // Don't break here to allow other entires to be applied if match. } else { LOG(ERROR) << "Syntax error in customization manifest"; } } } } else { LOG(ERROR) << "HWID is missing in machine statistics"; } } // If manifest doesn't exist still apply values from VPD. statistics_provider->GetMachineStatistic(kInitialLocaleAttr, &initial_locale_); statistics_provider->GetMachineStatistic(kInitialTimezoneAttr, &initial_timezone_); statistics_provider->GetMachineStatistic(kKeyboardLayoutAttr, &keyboard_layout_); } std::string StartupCustomizationDocument::GetHelpPage( const std::string& locale) const { return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr); } std::string StartupCustomizationDocument::GetEULAPage( const std::string& locale) const { return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr); } // ServicesCustomizationDocument implementation. ------------------------------- ServicesCustomizationDocument::ServicesCustomizationDocument() : CustomizationDocument(kAcceptedManifestVersion), url_(kServicesCustomizationManifestUrl) { } ServicesCustomizationDocument::ServicesCustomizationDocument( const std::string& manifest) : CustomizationDocument(kAcceptedManifestVersion) { LoadManifestFromString(manifest); } ServicesCustomizationDocument::~ServicesCustomizationDocument() {} // static ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() { return Singleton >::get(); } // static void ServicesCustomizationDocument::RegisterPrefs( PrefRegistrySimple* registry) { registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false); } // static bool ServicesCustomizationDocument::WasApplied() { PrefService* prefs = g_browser_process->local_state(); return prefs->GetBoolean(kServicesCustomizationAppliedPref); } // static void ServicesCustomizationDocument::SetApplied(bool val) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(kServicesCustomizationAppliedPref, val); } void ServicesCustomizationDocument::StartFetching() { if (url_.SchemeIsFile()) { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&ServicesCustomizationDocument::ReadFileInBackground, base::Unretained(this), // this class is a singleton. base::FilePath(url_.path()))); } else { StartFileFetch(); } } void ServicesCustomizationDocument::ReadFileInBackground( const base::FilePath& file) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); std::string manifest; if (base::ReadFileToString(file, &manifest)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( base::IgnoreResult( &ServicesCustomizationDocument::LoadManifestFromString), base::Unretained(this), // this class is a singleton. manifest)); } else { VLOG(1) << "Failed to load services customization manifest from: " << file.value(); } } void ServicesCustomizationDocument::StartFileFetch() { DCHECK(url_.is_valid()); url_fetcher_.reset(net::URLFetcher::Create( url_, net::URLFetcher::GET, this)); url_fetcher_->SetRequestContext( ProfileManager::GetDefaultProfile()->GetRequestContext()); url_fetcher_->Start(); } void ServicesCustomizationDocument::OnURLFetchComplete( const net::URLFetcher* source) { if (source->GetResponseCode() == 200) { std::string data; source->GetResponseAsString(&data); LoadManifestFromString(data); } else { const NetworkState* default_network = NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); if (default_network && default_network->IsConnectedState() && num_retries_ < kMaxFetchRetries) { num_retries_++; retry_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kRetriesDelayInSec), this, &ServicesCustomizationDocument::StartFileFetch); return; } LOG(ERROR) << "URL fetch for services customization failed:" << " response code = " << source->GetResponseCode() << " URL = " << source->GetURL().spec(); } } bool ServicesCustomizationDocument::ApplyCustomization() { // TODO(dpolukhin): apply customized apps, exts and support page. SetApplied(true); return true; } std::string ServicesCustomizationDocument::GetInitialStartPage( const std::string& locale) const { return GetLocaleSpecificString( locale, kAppContentAttr, kInitialStartPageAttr); } std::string ServicesCustomizationDocument::GetSupportPage( const std::string& locale) const { return GetLocaleSpecificString( locale, kAppContentAttr, kSupportPageAttr); } } // namespace chromeos