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_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chromeos/login/wizard_controller.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"
27 using content::BrowserThread
;
29 // Manifest attributes names.
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";
48 const char kAcceptedManifestVersion
[] = "1.0";
50 // Path to OEM partner startup customization manifest.
51 const char kStartupCustomizationManifestPath
[] =
52 "/opt/oem/etc/startup_manifest.json";
54 // URL where to fetch OEM services customization manifest from.
55 const char kServicesCustomizationManifestUrl
[] =
56 "file:///opt/oem/etc/services_manifest.json";
58 // Name of local state option that tracks if services customization has been
60 const char kServicesCustomizationAppliedPref
[] = "ServicesCustomizationApplied";
62 // Maximum number of retries to fetch file if network is not available.
63 const int kMaxFetchRetries
= 3;
65 // Delay between file fetch retries if network is not available.
66 const int kRetriesDelayInSec
= 2;
68 } // anonymous namespace
72 // CustomizationDocument implementation. ---------------------------------------
74 CustomizationDocument::CustomizationDocument(
75 const std::string
& accepted_version
)
76 : accepted_version_(accepted_version
) {}
78 CustomizationDocument::~CustomizationDocument() {}
80 bool CustomizationDocument::LoadManifestFromFile(
81 const base::FilePath
& manifest_path
) {
83 if (!base::ReadFileToString(manifest_path
, &manifest
))
85 return LoadManifestFromString(manifest
);
88 bool CustomizationDocument::LoadManifestFromString(
89 const std::string
& manifest
) {
92 scoped_ptr
<base::Value
> root(base::JSONReader::ReadAndReturnError(manifest
,
93 base::JSON_ALLOW_TRAILING_COMMAS
, &error_code
, &error
));
94 if (error_code
!= base::JSONReader::JSON_NO_ERROR
)
96 DCHECK(root
.get() != NULL
);
97 if (root
.get() == NULL
)
99 DCHECK(root
->GetType() == base::Value::TYPE_DICTIONARY
);
100 if (root
->GetType() == base::Value::TYPE_DICTIONARY
) {
101 root_
.reset(static_cast<base::DictionaryValue
*>(root
.release()));
103 if (root_
->GetString(kVersionAttr
, &result
) &&
104 result
== accepted_version_
)
107 LOG(ERROR
) << "Wrong customization manifest version";
113 std::string
CustomizationDocument::GetLocaleSpecificString(
114 const std::string
& locale
,
115 const std::string
& dictionary_name
,
116 const std::string
& entry_name
) const {
117 base::DictionaryValue
* dictionary_content
= NULL
;
119 !root_
->GetDictionary(dictionary_name
, &dictionary_content
))
120 return std::string();
122 base::DictionaryValue
* locale_dictionary
= NULL
;
123 if (dictionary_content
->GetDictionary(locale
, &locale_dictionary
)) {
125 if (locale_dictionary
->GetString(entry_name
, &result
))
129 base::DictionaryValue
* default_dictionary
= NULL
;
130 if (dictionary_content
->GetDictionary(kDefaultAttr
, &default_dictionary
)) {
132 if (default_dictionary
->GetString(entry_name
, &result
))
136 return std::string();
139 // StartupCustomizationDocument implementation. --------------------------------
141 StartupCustomizationDocument::StartupCustomizationDocument()
142 : CustomizationDocument(kAcceptedManifestVersion
) {
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
));
149 Init(chromeos::system::StatisticsProvider::GetInstance());
152 StartupCustomizationDocument::StartupCustomizationDocument(
153 chromeos::system::StatisticsProvider
* statistics_provider
,
154 const std::string
& manifest
)
155 : CustomizationDocument(kAcceptedManifestVersion
) {
156 LoadManifestFromString(manifest
);
157 Init(statistics_provider
);
160 StartupCustomizationDocument::~StartupCustomizationDocument() {}
162 StartupCustomizationDocument
* StartupCustomizationDocument::GetInstance() {
163 return Singleton
<StartupCustomizationDocument
,
164 DefaultSingletonTraits
<StartupCustomizationDocument
> >::get();
167 void StartupCustomizationDocument::Init(
168 chromeos::system::StatisticsProvider
* statistics_provider
) {
170 root_
->GetString(kInitialLocaleAttr
, &initial_locale_
);
171 root_
->GetString(kInitialTimezoneAttr
, &initial_timezone_
);
172 root_
->GetString(kKeyboardLayoutAttr
, &keyboard_layout_
);
173 root_
->GetString(kRegistrationUrlAttr
, ®istration_url_
);
176 if (statistics_provider
->GetMachineStatistic(
177 chromeos::system::kHardwareClassKey
, &hwid
)) {
178 base::ListValue
* hwid_list
= NULL
;
179 if (root_
->GetList(kHwidMapAttr
, &hwid_list
)) {
180 for (size_t i
= 0; i
< hwid_list
->GetSize(); ++i
) {
181 base::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
189 if (hwid_dictionary
->GetString(kInitialLocaleAttr
, &result
))
190 initial_locale_
= result
;
192 if (hwid_dictionary
->GetString(kInitialTimezoneAttr
, &result
))
193 initial_timezone_
= result
;
195 if (hwid_dictionary
->GetString(kKeyboardLayoutAttr
, &result
))
196 keyboard_layout_
= result
;
198 // Don't break here to allow other entires to be applied if match.
200 LOG(ERROR
) << "Syntax error in customization manifest";
205 LOG(ERROR
) << "HWID is missing in machine statistics";
209 // If manifest doesn't exist still apply values from VPD.
210 statistics_provider
->GetMachineStatistic(kInitialLocaleAttr
,
212 statistics_provider
->GetMachineStatistic(kInitialTimezoneAttr
,
214 statistics_provider
->GetMachineStatistic(kKeyboardLayoutAttr
,
216 configured_locales_
.resize(0);
217 base::SplitString(initial_locale_
, ',', &configured_locales_
);
218 // Let's always have configured_locales_.front() a valid entry.
219 if (configured_locales_
.size() == 0)
220 configured_locales_
.push_back(std::string());
223 const std::vector
<std::string
>&
224 StartupCustomizationDocument::configured_locales() const {
225 return configured_locales_
;
228 const std::string
& StartupCustomizationDocument::initial_locale_default()
230 DCHECK(configured_locales_
.size() > 0);
231 return configured_locales_
.front();
234 std::string
StartupCustomizationDocument::GetHelpPage(
235 const std::string
& locale
) const {
236 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kHelpPageAttr
);
239 std::string
StartupCustomizationDocument::GetEULAPage(
240 const std::string
& locale
) const {
241 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kEulaPageAttr
);
244 // ServicesCustomizationDocument implementation. -------------------------------
246 ServicesCustomizationDocument::ServicesCustomizationDocument()
247 : CustomizationDocument(kAcceptedManifestVersion
),
248 url_(kServicesCustomizationManifestUrl
) {
251 ServicesCustomizationDocument::ServicesCustomizationDocument(
252 const std::string
& manifest
)
253 : CustomizationDocument(kAcceptedManifestVersion
) {
254 LoadManifestFromString(manifest
);
257 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
260 ServicesCustomizationDocument
* ServicesCustomizationDocument::GetInstance() {
261 return Singleton
<ServicesCustomizationDocument
,
262 DefaultSingletonTraits
<ServicesCustomizationDocument
> >::get();
266 void ServicesCustomizationDocument::RegisterPrefs(
267 PrefRegistrySimple
* registry
) {
268 registry
->RegisterBooleanPref(kServicesCustomizationAppliedPref
, false);
272 bool ServicesCustomizationDocument::WasApplied() {
273 PrefService
* prefs
= g_browser_process
->local_state();
274 return prefs
->GetBoolean(kServicesCustomizationAppliedPref
);
278 void ServicesCustomizationDocument::SetApplied(bool val
) {
279 PrefService
* prefs
= g_browser_process
->local_state();
280 prefs
->SetBoolean(kServicesCustomizationAppliedPref
, val
);
283 void ServicesCustomizationDocument::StartFetching() {
284 if (url_
.SchemeIsFile()) {
285 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
286 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground
,
287 base::Unretained(this), // this class is a singleton.
288 base::FilePath(url_
.path())));
294 void ServicesCustomizationDocument::ReadFileInBackground(
295 const base::FilePath
& file
) {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
298 std::string manifest
;
299 if (base::ReadFileToString(file
, &manifest
)) {
300 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
303 &ServicesCustomizationDocument::LoadManifestFromString
),
304 base::Unretained(this), // this class is a singleton.
307 VLOG(1) << "Failed to load services customization manifest from: "
312 void ServicesCustomizationDocument::StartFileFetch() {
313 DCHECK(url_
.is_valid());
314 url_fetcher_
.reset(net::URLFetcher::Create(
315 url_
, net::URLFetcher::GET
, this));
316 url_fetcher_
->SetRequestContext(g_browser_process
->system_request_context());
317 url_fetcher_
->Start();
320 void ServicesCustomizationDocument::OnURLFetchComplete(
321 const net::URLFetcher
* source
) {
322 if (source
->GetResponseCode() == 200) {
324 source
->GetResponseAsString(&data
);
325 LoadManifestFromString(data
);
327 const NetworkState
* default_network
=
328 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
329 if (default_network
&& default_network
->IsConnectedState() &&
330 num_retries_
< kMaxFetchRetries
) {
332 retry_timer_
.Start(FROM_HERE
,
333 base::TimeDelta::FromSeconds(kRetriesDelayInSec
),
334 this, &ServicesCustomizationDocument::StartFileFetch
);
337 LOG(ERROR
) << "URL fetch for services customization failed:"
338 << " response code = " << source
->GetResponseCode()
339 << " URL = " << source
->GetURL().spec();
343 bool ServicesCustomizationDocument::ApplyCustomization() {
344 // TODO(dpolukhin): apply customized apps, exts and support page.
349 std::string
ServicesCustomizationDocument::GetInitialStartPage(
350 const std::string
& locale
) const {
351 return GetLocaleSpecificString(
352 locale
, kAppContentAttr
, kInitialStartPageAttr
);
355 std::string
ServicesCustomizationDocument::GetSupportPage(
356 const std::string
& locale
) const {
357 return GetLocaleSpecificString(
358 locale
, kAppContentAttr
, kSupportPageAttr
);
361 } // namespace chromeos