customization_document.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 27using content::BrowserThread; 28 29// Manifest attributes names. 30 31namespace { 32 33const char kVersionAttr[] = "version"; 34const char kDefaultAttr[] = "default"; 35const char kInitialLocaleAttr[] = "initial_locale"; 36const char kInitialTimezoneAttr[] = "initial_timezone"; 37const char kKeyboardLayoutAttr[] = "keyboard_layout"; 38const char kRegistrationUrlAttr[] = "registration_url"; 39const char kHwidMapAttr[] = "hwid_map"; 40const char kHwidMaskAttr[] = "hwid_mask"; 41const char kSetupContentAttr[] = "setup_content"; 42const char kHelpPageAttr[] = "help_page"; 43const char kEulaPageAttr[] = "eula_page"; 44const char kAppContentAttr[] = "app_content"; 45const char kInitialStartPageAttr[] = "initial_start_page"; 46const char kSupportPageAttr[] = "support_page"; 47 48const char kAcceptedManifestVersion[] = "1.0"; 49 50// Path to OEM partner startup customization manifest. 51const char kStartupCustomizationManifestPath[] = 52 "/opt/oem/etc/startup_manifest.json"; 53 54// URL where to fetch OEM services customization manifest from. 55const 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. 60const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied"; 61 62// Maximum number of retries to fetch file if network is not available. 63const int kMaxFetchRetries = 3; 64 65// Delay between file fetch retries if network is not available. 66const int kRetriesDelayInSec = 2; 67 68} // anonymous namespace 69 70namespace chromeos { 71 72// CustomizationDocument implementation. --------------------------------------- 73 74CustomizationDocument::CustomizationDocument( 75 const std::string& accepted_version) 76 : accepted_version_(accepted_version) {} 77 78CustomizationDocument::~CustomizationDocument() {} 79 80bool 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 88bool 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 113std::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 141StartupCustomizationDocument::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 152StartupCustomizationDocument::StartupCustomizationDocument( 153 chromeos::system::StatisticsProvider* statistics_provider, 154 const std::string& manifest) 155 : CustomizationDocument(kAcceptedManifestVersion) { 156 LoadManifestFromString(manifest); 157 Init(statistics_provider); 158} 159 160StartupCustomizationDocument::~StartupCustomizationDocument() {} 161 162StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() { 163 return Singleton<StartupCustomizationDocument, 164 DefaultSingletonTraits<StartupCustomizationDocument> >::get(); 165} 166 167void 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, ®istration_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 218std::string StartupCustomizationDocument::GetHelpPage( 219 const std::string& locale) const { 220 return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr); 221} 222 223std::string StartupCustomizationDocument::GetEULAPage( 224 const std::string& locale) const { 225 return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr); 226} 227 228// ServicesCustomizationDocument implementation. ------------------------------- 229 230ServicesCustomizationDocument::ServicesCustomizationDocument() 231 : CustomizationDocument(kAcceptedManifestVersion), 232 url_(kServicesCustomizationManifestUrl) { 233} 234 235ServicesCustomizationDocument::ServicesCustomizationDocument( 236 const std::string& manifest) 237 : CustomizationDocument(kAcceptedManifestVersion) { 238 LoadManifestFromString(manifest); 239} 240 241ServicesCustomizationDocument::~ServicesCustomizationDocument() {} 242 243// static 244ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() { 245 return Singleton<ServicesCustomizationDocument, 246 DefaultSingletonTraits<ServicesCustomizationDocument> >::get(); 247} 248 249// static 250void ServicesCustomizationDocument::RegisterPrefs( 251 PrefRegistrySimple* registry) { 252 registry->RegisterBooleanPref(kServicesCustomizationAppliedPref, false); 253} 254 255// static 256bool ServicesCustomizationDocument::WasApplied() { 257 PrefService* prefs = g_browser_process->local_state(); 258 return prefs->GetBoolean(kServicesCustomizationAppliedPref); 259} 260 261// static 262void ServicesCustomizationDocument::SetApplied(bool val) { 263 PrefService* prefs = g_browser_process->local_state(); 264 prefs->SetBoolean(kServicesCustomizationAppliedPref, val); 265} 266 267void 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 278void 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 296void 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 305void 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 328bool ServicesCustomizationDocument::ApplyCustomization() { 329 // TODO(dpolukhin): apply customized apps, exts and support page. 330 SetApplied(true); 331 return true; 332} 333 334std::string ServicesCustomizationDocument::GetInitialStartPage( 335 const std::string& locale) const { 336 return GetLocaleSpecificString( 337 locale, kAppContentAttr, kInitialStartPageAttr); 338} 339 340std::string ServicesCustomizationDocument::GetSupportPage( 341 const std::string& locale) const { 342 return GetLocaleSpecificString( 343 locale, kAppContentAttr, kSupportPageAttr); 344} 345 346} // namespace chromeos 347