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.
5 #include "chrome/browser/chromeos/customization_document.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 "chromeos/network/network_state.h"
21 #include "chromeos/network/network_state_handler.h"
22 #include "chromeos/system/statistics_provider.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/url_request/url_fetcher.h"
26 using content::BrowserThread
;
28 // Manifest attributes names.
32 const char kVersionAttr
[] = "version";
33 const char kDefaultAttr
[] = "default";
34 const char kInitialLocaleAttr
[] = "initial_locale";
35 const char kInitialTimezoneAttr
[] = "initial_timezone";
36 const char kKeyboardLayoutAttr
[] = "keyboard_layout";
37 const char kRegistrationUrlAttr
[] = "registration_url";
38 const char kHwidMapAttr
[] = "hwid_map";
39 const char kHwidMaskAttr
[] = "hwid_mask";
40 const char kSetupContentAttr
[] = "setup_content";
41 const char kHelpPageAttr
[] = "help_page";
42 const char kEulaPageAttr
[] = "eula_page";
43 const char kAppContentAttr
[] = "app_content";
44 const char kInitialStartPageAttr
[] = "initial_start_page";
45 const char kSupportPageAttr
[] = "support_page";
47 const char kAcceptedManifestVersion
[] = "1.0";
49 // Path to OEM partner startup customization manifest.
50 const char kStartupCustomizationManifestPath
[] =
51 "/opt/oem/etc/startup_manifest.json";
53 // URL where to fetch OEM services customization manifest from.
54 const char kServicesCustomizationManifestUrl
[] =
55 "file:///opt/oem/etc/services_manifest.json";
57 // Name of local state option that tracks if services customization has been
59 const char kServicesCustomizationAppliedPref
[] = "ServicesCustomizationApplied";
61 // Maximum number of retries to fetch file if network is not available.
62 const int kMaxFetchRetries
= 3;
64 // Delay between file fetch retries if network is not available.
65 const int kRetriesDelayInSec
= 2;
67 } // anonymous namespace
71 // CustomizationDocument implementation. ---------------------------------------
73 CustomizationDocument::CustomizationDocument(
74 const std::string
& accepted_version
)
75 : accepted_version_(accepted_version
) {}
77 CustomizationDocument::~CustomizationDocument() {}
79 bool CustomizationDocument::LoadManifestFromFile(
80 const base::FilePath
& manifest_path
) {
82 if (!base::ReadFileToString(manifest_path
, &manifest
))
84 return LoadManifestFromString(manifest
);
87 bool CustomizationDocument::LoadManifestFromString(
88 const std::string
& manifest
) {
91 scoped_ptr
<base::Value
> root(base::JSONReader::ReadAndReturnError(manifest
,
92 base::JSON_ALLOW_TRAILING_COMMAS
, &error_code
, &error
));
93 if (error_code
!= base::JSONReader::JSON_NO_ERROR
)
95 DCHECK(root
.get() != NULL
);
96 if (root
.get() == NULL
)
98 DCHECK(root
->GetType() == base::Value::TYPE_DICTIONARY
);
99 if (root
->GetType() == base::Value::TYPE_DICTIONARY
) {
100 root_
.reset(static_cast<base::DictionaryValue
*>(root
.release()));
102 if (root_
->GetString(kVersionAttr
, &result
) &&
103 result
== accepted_version_
)
106 LOG(ERROR
) << "Wrong customization manifest version";
112 std::string
CustomizationDocument::GetLocaleSpecificString(
113 const std::string
& locale
,
114 const std::string
& dictionary_name
,
115 const std::string
& entry_name
) const {
116 base::DictionaryValue
* dictionary_content
= NULL
;
118 !root_
->GetDictionary(dictionary_name
, &dictionary_content
))
119 return std::string();
121 base::DictionaryValue
* locale_dictionary
= NULL
;
122 if (dictionary_content
->GetDictionary(locale
, &locale_dictionary
)) {
124 if (locale_dictionary
->GetString(entry_name
, &result
))
128 base::DictionaryValue
* default_dictionary
= NULL
;
129 if (dictionary_content
->GetDictionary(kDefaultAttr
, &default_dictionary
)) {
131 if (default_dictionary
->GetString(entry_name
, &result
))
135 return std::string();
138 // StartupCustomizationDocument implementation. --------------------------------
140 StartupCustomizationDocument::StartupCustomizationDocument()
141 : CustomizationDocument(kAcceptedManifestVersion
) {
143 // Loading manifest causes us to do blocking IO on UI thread.
144 // Temporarily allow it until we fix http://crosbug.com/11103
145 base::ThreadRestrictions::ScopedAllowIO allow_io
;
146 LoadManifestFromFile(base::FilePath(kStartupCustomizationManifestPath
));
148 Init(chromeos::system::StatisticsProvider::GetInstance());
151 StartupCustomizationDocument::StartupCustomizationDocument(
152 chromeos::system::StatisticsProvider
* statistics_provider
,
153 const std::string
& manifest
)
154 : CustomizationDocument(kAcceptedManifestVersion
) {
155 LoadManifestFromString(manifest
);
156 Init(statistics_provider
);
159 StartupCustomizationDocument::~StartupCustomizationDocument() {}
161 StartupCustomizationDocument
* StartupCustomizationDocument::GetInstance() {
162 return Singleton
<StartupCustomizationDocument
,
163 DefaultSingletonTraits
<StartupCustomizationDocument
> >::get();
166 void StartupCustomizationDocument::Init(
167 chromeos::system::StatisticsProvider
* statistics_provider
) {
169 root_
->GetString(kInitialLocaleAttr
, &initial_locale_
);
170 root_
->GetString(kInitialTimezoneAttr
, &initial_timezone_
);
171 root_
->GetString(kKeyboardLayoutAttr
, &keyboard_layout_
);
172 root_
->GetString(kRegistrationUrlAttr
, ®istration_url_
);
175 if (statistics_provider
->GetMachineStatistic(
176 chromeos::system::kHardwareClassKey
, &hwid
)) {
177 base::ListValue
* hwid_list
= NULL
;
178 if (root_
->GetList(kHwidMapAttr
, &hwid_list
)) {
179 for (size_t i
= 0; i
< hwid_list
->GetSize(); ++i
) {
180 base::DictionaryValue
* hwid_dictionary
= NULL
;
181 std::string hwid_mask
;
182 if (hwid_list
->GetDictionary(i
, &hwid_dictionary
) &&
183 hwid_dictionary
->GetString(kHwidMaskAttr
, &hwid_mask
)) {
184 if (MatchPattern(hwid
, hwid_mask
)) {
185 // If HWID for this machine matches some mask, use HWID specific
188 if (hwid_dictionary
->GetString(kInitialLocaleAttr
, &result
))
189 initial_locale_
= result
;
191 if (hwid_dictionary
->GetString(kInitialTimezoneAttr
, &result
))
192 initial_timezone_
= result
;
194 if (hwid_dictionary
->GetString(kKeyboardLayoutAttr
, &result
))
195 keyboard_layout_
= result
;
197 // Don't break here to allow other entires to be applied if match.
199 LOG(ERROR
) << "Syntax error in customization manifest";
204 LOG(ERROR
) << "HWID is missing in machine statistics";
208 // If manifest doesn't exist still apply values from VPD.
209 statistics_provider
->GetMachineStatistic(kInitialLocaleAttr
,
211 statistics_provider
->GetMachineStatistic(kInitialTimezoneAttr
,
213 statistics_provider
->GetMachineStatistic(kKeyboardLayoutAttr
,
217 std::string
StartupCustomizationDocument::GetHelpPage(
218 const std::string
& locale
) const {
219 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kHelpPageAttr
);
222 std::string
StartupCustomizationDocument::GetEULAPage(
223 const std::string
& locale
) const {
224 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kEulaPageAttr
);
227 // ServicesCustomizationDocument implementation. -------------------------------
229 ServicesCustomizationDocument::ServicesCustomizationDocument()
230 : CustomizationDocument(kAcceptedManifestVersion
),
231 url_(kServicesCustomizationManifestUrl
) {
234 ServicesCustomizationDocument::ServicesCustomizationDocument(
235 const std::string
& manifest
)
236 : CustomizationDocument(kAcceptedManifestVersion
) {
237 LoadManifestFromString(manifest
);
240 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
243 ServicesCustomizationDocument
* ServicesCustomizationDocument::GetInstance() {
244 return Singleton
<ServicesCustomizationDocument
,
245 DefaultSingletonTraits
<ServicesCustomizationDocument
> >::get();
249 void ServicesCustomizationDocument::RegisterPrefs(
250 PrefRegistrySimple
* registry
) {
251 registry
->RegisterBooleanPref(kServicesCustomizationAppliedPref
, false);
255 bool ServicesCustomizationDocument::WasApplied() {
256 PrefService
* prefs
= g_browser_process
->local_state();
257 return prefs
->GetBoolean(kServicesCustomizationAppliedPref
);
261 void ServicesCustomizationDocument::SetApplied(bool val
) {
262 PrefService
* prefs
= g_browser_process
->local_state();
263 prefs
->SetBoolean(kServicesCustomizationAppliedPref
, val
);
266 void ServicesCustomizationDocument::StartFetching() {
267 if (url_
.SchemeIsFile()) {
268 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
269 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground
,
270 base::Unretained(this), // this class is a singleton.
271 base::FilePath(url_
.path())));
277 void ServicesCustomizationDocument::ReadFileInBackground(
278 const base::FilePath
& file
) {
279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
281 std::string manifest
;
282 if (base::ReadFileToString(file
, &manifest
)) {
283 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
286 &ServicesCustomizationDocument::LoadManifestFromString
),
287 base::Unretained(this), // this class is a singleton.
290 VLOG(1) << "Failed to load services customization manifest from: "
295 void ServicesCustomizationDocument::StartFileFetch() {
296 DCHECK(url_
.is_valid());
297 url_fetcher_
.reset(net::URLFetcher::Create(
298 url_
, net::URLFetcher::GET
, this));
299 url_fetcher_
->SetRequestContext(g_browser_process
->system_request_context());
300 url_fetcher_
->Start();
303 void ServicesCustomizationDocument::OnURLFetchComplete(
304 const net::URLFetcher
* source
) {
305 if (source
->GetResponseCode() == 200) {
307 source
->GetResponseAsString(&data
);
308 LoadManifestFromString(data
);
310 const NetworkState
* default_network
=
311 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
312 if (default_network
&& default_network
->IsConnectedState() &&
313 num_retries_
< kMaxFetchRetries
) {
315 retry_timer_
.Start(FROM_HERE
,
316 base::TimeDelta::FromSeconds(kRetriesDelayInSec
),
317 this, &ServicesCustomizationDocument::StartFileFetch
);
320 LOG(ERROR
) << "URL fetch for services customization failed:"
321 << " response code = " << source
->GetResponseCode()
322 << " URL = " << source
->GetURL().spec();
326 bool ServicesCustomizationDocument::ApplyCustomization() {
327 // TODO(dpolukhin): apply customized apps, exts and support page.
332 std::string
ServicesCustomizationDocument::GetInitialStartPage(
333 const std::string
& locale
) const {
334 return GetLocaleSpecificString(
335 locale
, kAppContentAttr
, kInitialStartPageAttr
);
338 std::string
ServicesCustomizationDocument::GetSupportPage(
339 const std::string
& locale
) const {
340 return GetLocaleSpecificString(
341 locale
, kAppContentAttr
, kSupportPageAttr
);
344 } // namespace chromeos