• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/file_path.h"
8 #include "base/file_util.h"
9 #include "base/json/json_reader.h"
10 #include "base/logging.h"
11 #include "base/string_tokenizer.h"
12 #include "base/string_util.h"
13 #include "base/time.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chromeos/cros/cros_library.h"
17 #include "chrome/browser/chromeos/cros/network_library.h"
18 #include "chrome/browser/chromeos/login/wizard_controller.h"
19 #include "chrome/browser/chromeos/system_access.h"
20 #include "chrome/browser/prefs/pref_service.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "content/browser/browser_thread.h"
23 
24 // Manifest attributes names.
25 
26 namespace {
27 
28 const char kVersionAttr[] = "version";
29 const char kDefaultAttr[] = "default";
30 const char kInitialLocaleAttr[] = "initial_locale";
31 const char kInitialTimezoneAttr[] = "initial_timezone";
32 const char kKeyboardLayoutAttr[] = "keyboard_layout";
33 const char kRegistrationUrlAttr[] = "registration_url";
34 const char kHwidMapAttr[] = "hwid_map";
35 const char kHwidMaskAttr[] = "hwid_mask";
36 const char kSetupContentAttr[] = "setup_content";
37 const char kHelpPageAttr[] = "help_page";
38 const char kEulaPageAttr[] = "eula_page";
39 const char kAppContentAttr[] = "app_content";
40 const char kInitialStartPageAttr[] = "initial_start_page";
41 const char kSupportPageAttr[] = "support_page";
42 
43 const char kAcceptedManifestVersion[] = "1.0";
44 
45 const char kHwid[] = "hwid";
46 
47 // Carrier deals attributes.
48 const char kCarrierDealsAttr[] = "carrier_deals";
49 const char kDealLocaleAttr[] = "deal_locale";
50 const char kTopUpURLAttr[] = "top_up_url";
51 const char kNotificationCountAttr[] = "notification_count";
52 const char kDealExpireDateAttr[] = "expire_date";
53 const char kLocalizedContentAttr[] = "localized_content";
54 const char kNotificationTextAttr[] = "notification_text";
55 
56 // Path to OEM partner startup customization manifest.
57 const char kStartupCustomizationManifestPath[] =
58     "/opt/oem/etc/startup_manifest.json";
59 
60 // URL where to fetch OEM services customization manifest from.
61 const char kServicesCustomizationManifestUrl[] =
62     "file:///opt/oem/etc/services_manifest.json";
63 
64 // Name of local state option that tracks if services customization has been
65 // applied.
66 const char kServicesCustomizationAppliedPref[] = "ServicesCustomizationApplied";
67 
68 // Maximum number of retries to fetch file if network is not available.
69 const int kMaxFetchRetries = 3;
70 
71 // Delay between file fetch retries if network is not available.
72 const int kRetriesDelayInSec = 2;
73 
74 }  // anonymous namespace
75 
76 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::ServicesCustomizationDocument);
77 
78 namespace chromeos {
79 
80 // CustomizationDocument implementation. ---------------------------------------
81 
LoadManifestFromFile(const FilePath & manifest_path)82 bool CustomizationDocument::LoadManifestFromFile(
83     const FilePath& manifest_path) {
84   std::string manifest;
85   if (!file_util::ReadFileToString(manifest_path, &manifest))
86     return false;
87   return LoadManifestFromString(manifest);
88 }
89 
LoadManifestFromString(const std::string & manifest)90 bool CustomizationDocument::LoadManifestFromString(
91     const std::string& manifest) {
92   scoped_ptr<Value> root(base::JSONReader::Read(manifest, true));
93   DCHECK(root.get() != NULL);
94   if (root.get() == NULL)
95     return false;
96   DCHECK(root->GetType() == Value::TYPE_DICTIONARY);
97   if (root->GetType() == Value::TYPE_DICTIONARY) {
98     root_.reset(static_cast<DictionaryValue*>(root.release()));
99     std::string result;
100     if (root_->GetString(kVersionAttr, &result) &&
101         result == kAcceptedManifestVersion)
102       return true;
103 
104     LOG(ERROR) << "Wrong customization manifest version";
105     root_.reset(NULL);
106   }
107   return false;
108 }
109 
GetLocaleSpecificString(const std::string & locale,const std::string & dictionary_name,const std::string & entry_name) const110 std::string CustomizationDocument::GetLocaleSpecificString(
111     const std::string& locale,
112     const std::string& dictionary_name,
113     const std::string& entry_name) const {
114   DictionaryValue* dictionary_content = NULL;
115   if (!root_.get() ||
116       !root_->GetDictionary(dictionary_name, &dictionary_content))
117     return std::string();
118 
119   DictionaryValue* locale_dictionary = NULL;
120   if (dictionary_content->GetDictionary(locale, &locale_dictionary)) {
121     std::string result;
122     if (locale_dictionary->GetString(entry_name, &result))
123       return result;
124   }
125 
126   DictionaryValue* default_dictionary = NULL;
127   if (dictionary_content->GetDictionary(kDefaultAttr, &default_dictionary)) {
128     std::string result;
129     if (default_dictionary->GetString(entry_name, &result))
130       return result;
131   }
132 
133   return std::string();
134 }
135 
136 // StartupCustomizationDocument implementation. --------------------------------
137 
StartupCustomizationDocument()138 StartupCustomizationDocument::StartupCustomizationDocument() {
139   {
140     // Loading manifest causes us to do blocking IO on UI thread.
141     // Temporarily allow it until we fix http://crosbug.com/11103
142     base::ThreadRestrictions::ScopedAllowIO allow_io;
143     LoadManifestFromFile(FilePath(kStartupCustomizationManifestPath));
144   }
145   Init(SystemAccess::GetInstance());
146 }
147 
StartupCustomizationDocument(SystemAccess * system_access,const std::string & manifest)148 StartupCustomizationDocument::StartupCustomizationDocument(
149     SystemAccess* system_access, const std::string& manifest) {
150   LoadManifestFromString(manifest);
151   Init(system_access);
152 }
153 
GetInstance()154 StartupCustomizationDocument* StartupCustomizationDocument::GetInstance() {
155   return Singleton<StartupCustomizationDocument,
156       DefaultSingletonTraits<StartupCustomizationDocument> >::get();
157 }
158 
Init(SystemAccess * system_access)159 void StartupCustomizationDocument::Init(SystemAccess* system_access) {
160   if (!IsReady())
161     return;
162 
163   root_->GetString(kInitialLocaleAttr, &initial_locale_);
164   root_->GetString(kInitialTimezoneAttr, &initial_timezone_);
165   root_->GetString(kKeyboardLayoutAttr, &keyboard_layout_);
166   root_->GetString(kRegistrationUrlAttr, &registration_url_);
167 
168   std::string hwid;
169   if (system_access->GetMachineStatistic(kHwid, &hwid)) {
170     ListValue* hwid_list = NULL;
171     if (root_->GetList(kHwidMapAttr, &hwid_list)) {
172       for (size_t i = 0; i < hwid_list->GetSize(); ++i) {
173         DictionaryValue* hwid_dictionary = NULL;
174         std::string hwid_mask;
175         if (hwid_list->GetDictionary(i, &hwid_dictionary) &&
176             hwid_dictionary->GetString(kHwidMaskAttr, &hwid_mask)) {
177           if (MatchPattern(hwid, hwid_mask)) {
178             // If HWID for this machine matches some mask, use HWID specific
179             // settings.
180             std::string result;
181             if (hwid_dictionary->GetString(kInitialLocaleAttr, &result))
182               initial_locale_ = result;
183 
184             if (hwid_dictionary->GetString(kInitialTimezoneAttr, &result))
185               initial_timezone_ = result;
186 
187             if (hwid_dictionary->GetString(kKeyboardLayoutAttr, &result))
188               keyboard_layout_ = result;
189           }
190           // Don't break here to allow other entires to be applied if match.
191         } else {
192           LOG(ERROR) << "Syntax error in customization manifest";
193         }
194       }
195     }
196   } else {
197     LOG(ERROR) << "HWID is missing in machine statistics";
198   }
199 
200   system_access->GetMachineStatistic(kInitialLocaleAttr, &initial_locale_);
201   system_access->GetMachineStatistic(kInitialTimezoneAttr, &initial_timezone_);
202   system_access->GetMachineStatistic(kKeyboardLayoutAttr, &keyboard_layout_);
203 }
204 
GetHelpPage(const std::string & locale) const205 std::string StartupCustomizationDocument::GetHelpPage(
206     const std::string& locale) const {
207   return GetLocaleSpecificString(locale, kSetupContentAttr, kHelpPageAttr);
208 }
209 
GetEULAPage(const std::string & locale) const210 std::string StartupCustomizationDocument::GetEULAPage(
211     const std::string& locale) const {
212   return GetLocaleSpecificString(locale, kSetupContentAttr, kEulaPageAttr);
213 }
214 
215 // ServicesCustomizationDocument implementation. -------------------------------
216 
CarrierDeal(DictionaryValue * deal_dict)217 ServicesCustomizationDocument::CarrierDeal::CarrierDeal(
218     DictionaryValue* deal_dict)
219     : notification_count(0),
220       localized_strings(NULL) {
221   deal_dict->GetString(kDealLocaleAttr, &deal_locale);
222   deal_dict->GetString(kTopUpURLAttr, &top_up_url);
223   deal_dict->GetInteger(kNotificationCountAttr, &notification_count);
224   std::string date_string;
225   if (deal_dict->GetString(kDealExpireDateAttr, &date_string)) {
226     if (!base::Time::FromString(ASCIIToWide(date_string).c_str(), &expire_date))
227       LOG(ERROR) << "Error parsing deal_expire_date: " << date_string;
228   }
229   deal_dict->GetDictionary(kLocalizedContentAttr, &localized_strings);
230 }
231 
GetLocalizedString(const std::string & locale,const std::string & id) const232 std::string ServicesCustomizationDocument::CarrierDeal::GetLocalizedString(
233     const std::string& locale, const std::string& id) const {
234   std::string result;
235   if (localized_strings) {
236     DictionaryValue* locale_dict = NULL;
237     if (localized_strings->GetDictionary(locale, &locale_dict) &&
238         locale_dict->GetString(id, &result)) {
239       return result;
240     } else if (localized_strings->GetDictionary(kDefaultAttr, &locale_dict) &&
241                locale_dict->GetString(id, &result)) {
242       return result;
243     }
244   }
245   return result;
246 }
247 
ServicesCustomizationDocument()248 ServicesCustomizationDocument::ServicesCustomizationDocument()
249   : url_(kServicesCustomizationManifestUrl),
250     initial_locale_(WizardController::GetInitialLocale()) {
251 }
252 
ServicesCustomizationDocument(const std::string & manifest,const std::string & initial_locale)253 ServicesCustomizationDocument::ServicesCustomizationDocument(
254     const std::string& manifest, const std::string& initial_locale)
255     : initial_locale_(initial_locale) {
256   LoadManifestFromString(manifest);
257 }
258 
259 // static
GetInstance()260 ServicesCustomizationDocument* ServicesCustomizationDocument::GetInstance() {
261   return Singleton<ServicesCustomizationDocument,
262       DefaultSingletonTraits<ServicesCustomizationDocument> >::get();
263 }
264 
265 // static
RegisterPrefs(PrefService * local_state)266 void ServicesCustomizationDocument::RegisterPrefs(PrefService* local_state) {
267   local_state->RegisterBooleanPref(kServicesCustomizationAppliedPref, false);
268 }
269 
270 // static
WasApplied()271 bool ServicesCustomizationDocument::WasApplied() {
272   PrefService* prefs = g_browser_process->local_state();
273   return prefs->GetBoolean(kServicesCustomizationAppliedPref);
274 }
275 
276 // static
SetApplied(bool val)277 void ServicesCustomizationDocument::SetApplied(bool val) {
278   PrefService* prefs = g_browser_process->local_state();
279   prefs->SetBoolean(kServicesCustomizationAppliedPref, val);
280 }
281 
StartFetching()282 void ServicesCustomizationDocument::StartFetching() {
283   if (url_.SchemeIsFile()) {
284     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
285         NewRunnableMethod(this,
286             &ServicesCustomizationDocument::ReadFileInBackground,
287             FilePath(url_.path())));
288   } else {
289     StartFileFetch();
290   }
291 }
292 
ReadFileInBackground(const FilePath & file)293 void ServicesCustomizationDocument::ReadFileInBackground(const FilePath& file) {
294   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
295 
296   std::string manifest;
297   if (file_util::ReadFileToString(file, &manifest)) {
298     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
299         NewRunnableMethod(
300             this,
301             &ServicesCustomizationDocument::LoadManifestFromString,
302             manifest));
303   } else {
304     VLOG(1) << "Failed to load services customization manifest from: "
305             << file.value();
306   }
307 }
308 
StartFileFetch()309 void ServicesCustomizationDocument::StartFileFetch() {
310   DCHECK(url_.is_valid());
311   url_fetcher_.reset(new URLFetcher(url_, URLFetcher::GET, this));
312   url_fetcher_->set_request_context(
313       ProfileManager::GetDefaultProfile()->GetRequestContext());
314   url_fetcher_->Start();
315 }
316 
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)317 void ServicesCustomizationDocument::OnURLFetchComplete(
318     const URLFetcher* source,
319     const GURL& url,
320     const net::URLRequestStatus& status,
321     int response_code,
322     const ResponseCookies& cookies,
323     const std::string& data) {
324   if (response_code == 200) {
325     LoadManifestFromString(data);
326   } else {
327     NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary();
328     if (!network->Connected() && num_retries_ < kMaxFetchRetries) {
329       num_retries_++;
330       retry_timer_.Start(base::TimeDelta::FromSeconds(kRetriesDelayInSec),
331                          this, &ServicesCustomizationDocument::StartFileFetch);
332       return;
333     }
334     LOG(ERROR) << "URL fetch for services customization failed:"
335                << " response code = " << response_code
336                << " URL = " << url.spec();
337   }
338 }
339 
ApplyCustomization()340 bool ServicesCustomizationDocument::ApplyCustomization() {
341   // TODO(dpolukhin): apply customized apps, exts and support page.
342   SetApplied(true);
343   return true;
344 }
345 
GetInitialStartPage(const std::string & locale) const346 std::string ServicesCustomizationDocument::GetInitialStartPage(
347     const std::string& locale) const {
348   return GetLocaleSpecificString(
349       locale, kAppContentAttr, kInitialStartPageAttr);
350 }
351 
GetSupportPage(const std::string & locale) const352 std::string ServicesCustomizationDocument::GetSupportPage(
353     const std::string& locale) const {
354   return GetLocaleSpecificString(
355       locale, kAppContentAttr, kSupportPageAttr);
356 }
357 
358 const ServicesCustomizationDocument::CarrierDeal*
GetCarrierDeal(const std::string & carrier_id,bool check_restrictions) const359 ServicesCustomizationDocument::GetCarrierDeal(const std::string& carrier_id,
360                                               bool check_restrictions) const {
361   CarrierDeals::const_iterator iter = carrier_deals_.find(carrier_id);
362   if (iter != carrier_deals_.end()) {
363     CarrierDeal* deal = iter->second;
364     if (check_restrictions) {
365       // Deal locale has to match initial_locale (= launch country).
366       if (initial_locale_ != deal->deal_locale)
367         return NULL;
368       // Make sure that deal is still active,
369       // i.e. if deal expire date is defined, check it.
370       if (!deal->expire_date.is_null() &&
371           deal->expire_date <= base::Time::Now()) {
372         return NULL;
373       }
374     }
375     return deal;
376   } else {
377     return NULL;
378   }
379 }
380 
LoadManifestFromString(const std::string & manifest)381 bool ServicesCustomizationDocument::LoadManifestFromString(
382     const std::string& manifest) {
383   if (!CustomizationDocument::LoadManifestFromString(manifest))
384     return false;
385 
386   DictionaryValue* carriers = NULL;
387   if (root_.get() && root_->GetDictionary(kCarrierDealsAttr, &carriers)) {
388     for (DictionaryValue::key_iterator iter = carriers->begin_keys();
389          iter != carriers->end_keys(); ++iter) {
390      DictionaryValue* carrier_deal = NULL;
391      if (carriers->GetDictionary(*iter, &carrier_deal)) {
392        carrier_deals_[*iter] = new CarrierDeal(carrier_deal);
393      }
394    }
395   }
396   return true;
397 }
398 
399 }  // namespace chromeos
400