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_path.h"
10 #include "base/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/logging.h"
13 #include "base/string_tokenizer.h"
14 #include "base/string_util.h"
15 #include "base/time.h"
16 #include "base/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chromeos/cros/cros_library.h"
19 #include "chrome/browser/chromeos/cros/network_library.h"
20 #include "chrome/browser/chromeos/login/wizard_controller.h"
21 #include "chrome/browser/chromeos/system/statistics_provider.h"
22 #include "chrome/browser/prefs/pref_service.h"
23 #include "chrome/browser/profiles/profile_manager.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 const char kHardwareClass
[] = "hardware_class";
52 // Path to OEM partner startup customization manifest.
53 const char kStartupCustomizationManifestPath
[] =
54 "/opt/oem/etc/startup_manifest.json";
56 // URL where to fetch OEM services customization manifest from.
57 const char kServicesCustomizationManifestUrl
[] =
58 "file:///opt/oem/etc/services_manifest.json";
60 // Name of local state option that tracks if services customization has been
62 const char kServicesCustomizationAppliedPref
[] = "ServicesCustomizationApplied";
64 // Maximum number of retries to fetch file if network is not available.
65 const int kMaxFetchRetries
= 3;
67 // Delay between file fetch retries if network is not available.
68 const int kRetriesDelayInSec
= 2;
70 } // anonymous namespace
74 // CustomizationDocument implementation. ---------------------------------------
76 CustomizationDocument::CustomizationDocument(
77 const std::string
& accepted_version
)
78 : accepted_version_(accepted_version
) {}
80 CustomizationDocument::~CustomizationDocument() {}
82 bool CustomizationDocument::LoadManifestFromFile(
83 const FilePath
& manifest_path
) {
85 if (!file_util::ReadFileToString(manifest_path
, &manifest
))
87 return LoadManifestFromString(manifest
);
90 bool CustomizationDocument::LoadManifestFromString(
91 const std::string
& manifest
) {
94 scoped_ptr
<Value
> root(base::JSONReader::ReadAndReturnError(manifest
,
95 base::JSON_ALLOW_TRAILING_COMMAS
, &error_code
, &error
));
96 if (error_code
!= base::JSONReader::JSON_NO_ERROR
)
98 DCHECK(root
.get() != NULL
);
99 if (root
.get() == NULL
)
101 DCHECK(root
->GetType() == Value::TYPE_DICTIONARY
);
102 if (root
->GetType() == Value::TYPE_DICTIONARY
) {
103 root_
.reset(static_cast<DictionaryValue
*>(root
.release()));
105 if (root_
->GetString(kVersionAttr
, &result
) &&
106 result
== accepted_version_
)
109 LOG(ERROR
) << "Wrong customization manifest version";
115 std::string
CustomizationDocument::GetLocaleSpecificString(
116 const std::string
& locale
,
117 const std::string
& dictionary_name
,
118 const std::string
& entry_name
) const {
119 DictionaryValue
* dictionary_content
= NULL
;
121 !root_
->GetDictionary(dictionary_name
, &dictionary_content
))
122 return std::string();
124 DictionaryValue
* locale_dictionary
= NULL
;
125 if (dictionary_content
->GetDictionary(locale
, &locale_dictionary
)) {
127 if (locale_dictionary
->GetString(entry_name
, &result
))
131 DictionaryValue
* default_dictionary
= NULL
;
132 if (dictionary_content
->GetDictionary(kDefaultAttr
, &default_dictionary
)) {
134 if (default_dictionary
->GetString(entry_name
, &result
))
138 return std::string();
141 // StartupCustomizationDocument implementation. --------------------------------
143 StartupCustomizationDocument::StartupCustomizationDocument()
144 : CustomizationDocument(kAcceptedManifestVersion
) {
146 // Loading manifest causes us to do blocking IO on UI thread.
147 // Temporarily allow it until we fix http://crosbug.com/11103
148 base::ThreadRestrictions::ScopedAllowIO allow_io
;
149 LoadManifestFromFile(FilePath(kStartupCustomizationManifestPath
));
151 Init(chromeos::system::StatisticsProvider::GetInstance());
154 StartupCustomizationDocument::StartupCustomizationDocument(
155 chromeos::system::StatisticsProvider
* statistics_provider
,
156 const std::string
& manifest
)
157 : CustomizationDocument(kAcceptedManifestVersion
) {
158 LoadManifestFromString(manifest
);
159 Init(statistics_provider
);
162 StartupCustomizationDocument::~StartupCustomizationDocument() {}
164 StartupCustomizationDocument
* StartupCustomizationDocument::GetInstance() {
165 return Singleton
<StartupCustomizationDocument
,
166 DefaultSingletonTraits
<StartupCustomizationDocument
> >::get();
169 void StartupCustomizationDocument::Init(
170 chromeos::system::StatisticsProvider
* statistics_provider
) {
172 root_
->GetString(kInitialLocaleAttr
, &initial_locale_
);
173 root_
->GetString(kInitialTimezoneAttr
, &initial_timezone_
);
174 root_
->GetString(kKeyboardLayoutAttr
, &keyboard_layout_
);
175 root_
->GetString(kRegistrationUrlAttr
, ®istration_url_
);
178 if (statistics_provider
->GetMachineStatistic(kHardwareClass
, &hwid
)) {
179 ListValue
* hwid_list
= NULL
;
180 if (root_
->GetList(kHwidMapAttr
, &hwid_list
)) {
181 for (size_t i
= 0; i
< hwid_list
->GetSize(); ++i
) {
182 DictionaryValue
* hwid_dictionary
= NULL
;
183 std::string hwid_mask
;
184 if (hwid_list
->GetDictionary(i
, &hwid_dictionary
) &&
185 hwid_dictionary
->GetString(kHwidMaskAttr
, &hwid_mask
)) {
186 if (MatchPattern(hwid
, hwid_mask
)) {
187 // If HWID for this machine matches some mask, use HWID specific
190 if (hwid_dictionary
->GetString(kInitialLocaleAttr
, &result
))
191 initial_locale_
= result
;
193 if (hwid_dictionary
->GetString(kInitialTimezoneAttr
, &result
))
194 initial_timezone_
= result
;
196 if (hwid_dictionary
->GetString(kKeyboardLayoutAttr
, &result
))
197 keyboard_layout_
= result
;
199 // Don't break here to allow other entires to be applied if match.
201 LOG(ERROR
) << "Syntax error in customization manifest";
206 LOG(ERROR
) << "HWID is missing in machine statistics";
210 // If manifest doesn't exist still apply values from VPD.
211 statistics_provider
->GetMachineStatistic(kInitialLocaleAttr
,
213 statistics_provider
->GetMachineStatistic(kInitialTimezoneAttr
,
215 statistics_provider
->GetMachineStatistic(kKeyboardLayoutAttr
,
219 std::string
StartupCustomizationDocument::GetHelpPage(
220 const std::string
& locale
) const {
221 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kHelpPageAttr
);
224 std::string
StartupCustomizationDocument::GetEULAPage(
225 const std::string
& locale
) const {
226 return GetLocaleSpecificString(locale
, kSetupContentAttr
, kEulaPageAttr
);
229 // ServicesCustomizationDocument implementation. -------------------------------
231 ServicesCustomizationDocument::ServicesCustomizationDocument()
232 : CustomizationDocument(kAcceptedManifestVersion
),
233 url_(kServicesCustomizationManifestUrl
) {
236 ServicesCustomizationDocument::ServicesCustomizationDocument(
237 const std::string
& manifest
)
238 : CustomizationDocument(kAcceptedManifestVersion
) {
239 LoadManifestFromString(manifest
);
242 ServicesCustomizationDocument::~ServicesCustomizationDocument() {}
245 ServicesCustomizationDocument
* ServicesCustomizationDocument::GetInstance() {
246 return Singleton
<ServicesCustomizationDocument
,
247 DefaultSingletonTraits
<ServicesCustomizationDocument
> >::get();
251 void ServicesCustomizationDocument::RegisterPrefs(PrefService
* local_state
) {
252 local_state
->RegisterBooleanPref(kServicesCustomizationAppliedPref
, false,
253 PrefService::UNSYNCABLE_PREF
);
257 bool ServicesCustomizationDocument::WasApplied() {
258 PrefService
* prefs
= g_browser_process
->local_state();
259 return prefs
->GetBoolean(kServicesCustomizationAppliedPref
);
263 void ServicesCustomizationDocument::SetApplied(bool val
) {
264 PrefService
* prefs
= g_browser_process
->local_state();
265 prefs
->SetBoolean(kServicesCustomizationAppliedPref
, val
);
268 void ServicesCustomizationDocument::StartFetching() {
269 if (url_
.SchemeIsFile()) {
270 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
271 base::Bind(&ServicesCustomizationDocument::ReadFileInBackground
,
272 base::Unretained(this), // this class is a singleton.
273 FilePath(url_
.path())));
279 void ServicesCustomizationDocument::ReadFileInBackground(const FilePath
& file
) {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
282 std::string manifest
;
283 if (file_util::ReadFileToString(file
, &manifest
)) {
284 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
287 &ServicesCustomizationDocument::LoadManifestFromString
),
288 base::Unretained(this), // this class is a singleton.
291 VLOG(1) << "Failed to load services customization manifest from: "
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();
305 void ServicesCustomizationDocument::OnURLFetchComplete(
306 const net::URLFetcher
* source
) {
307 if (source
->GetResponseCode() == 200) {
309 source
->GetResponseAsString(&data
);
310 LoadManifestFromString(data
);
312 NetworkLibrary
* network
= CrosLibrary::Get()->GetNetworkLibrary();
313 if (!network
->Connected() && 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