Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: extensions/browser/api/runtime/runtime_api.cc

Issue 1970613003: Add a new app API to enable watchdog behavior restarts in kiosk apps (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a new app API to enable watchdog behavior restarts in kiosk apps Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/browser/api/runtime/runtime_api.h" 5 #include "extensions/browser/api/runtime/runtime_api.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
15 #include "base/single_thread_task_runner.h" 15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/thread_task_runner_handle.h" 17 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/values.h" 18 #include "base/values.h"
18 #include "base/version.h" 19 #include "base/version.h"
20 #include "components/prefs/pref_registry_simple.h"
21 #include "components/prefs/pref_service.h"
19 #include "content/public/browser/browser_context.h" 22 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/child_process_security_policy.h" 23 #include "content/public/browser/child_process_security_policy.h"
21 #include "content/public/browser/notification_service.h" 24 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/render_frame_host.h" 25 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h" 26 #include "content/public/browser/render_process_host.h"
24 #include "extensions/browser/api/runtime/runtime_api_delegate.h" 27 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
25 #include "extensions/browser/event_router.h" 28 #include "extensions/browser/event_router.h"
26 #include "extensions/browser/extension_host.h" 29 #include "extensions/browser/extension_host.h"
27 #include "extensions/browser/extension_prefs.h" 30 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_registry.h" 31 #include "extensions/browser/extension_registry.h"
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 "pending_on_installed_event_dispatch_info"; 76 "pending_on_installed_event_dispatch_info";
74 77
75 // Previously installed version number. 78 // Previously installed version number.
76 const char kPrefPreviousVersion[] = "previous_version"; 79 const char kPrefPreviousVersion[] = "previous_version";
77 80
78 // The name of the directory to be returned by getPackageDirectoryEntry. This 81 // The name of the directory to be returned by getPackageDirectoryEntry. This
79 // particular value does not matter to user code, but is chosen for consistency 82 // particular value does not matter to user code, but is chosen for consistency
80 // with the equivalent Pepper API. 83 // with the equivalent Pepper API.
81 const char kPackageDirectoryPath[] = "crxfs"; 84 const char kPackageDirectoryPath[] = "crxfs";
82 85
86 // Preference key for storing the last successful restart due to a call to
87 // chrome.runtime.restartAfterDelay().
88 constexpr char kPrefLastRestartAfterDelayTime[] =
89 "last_restart_after_delay_time";
90 // Preference key for storing whether the most recent restart was due to a
91 // successful call to chrome.runtime.restartAfterDelay().
92 constexpr char kPrefLastRestartWasDuetoDelayedRestartApi[] =
93 "last_restart_was_due_to_delayed_restart_api";
94
95 // Error and status messages strings for the restartAfterDelay() API.
96 constexpr char kErrorInvalidArgument[] = "Invalid argument: *.";
97 constexpr char kErrorOnlyKioskModeAllowed[] =
98 "API available only for ChromeOS kiosk mode.";
99 constexpr char kErrorOnlyFirstExtensionAllowed[] =
100 "Not the first extension to call this API.";
101 constexpr char kErrorInvalidStatus[] = "Invalid restart request status.";
102 constexpr char kErrorRequestedTooSoon[] =
103 "Restart was requested too soon. It was throttled instead.";
104
105 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3;
106
107 // This is used for unit tests, so that we can test the restartAfterDelay
108 // API without a kiosk app.
109 bool allow_non_kiosk_apps_restart_api_for_test = false;
110
83 void DispatchOnStartupEventImpl(BrowserContext* browser_context, 111 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
84 const std::string& extension_id, 112 const std::string& extension_id,
85 bool first_call, 113 bool first_call,
86 ExtensionHost* host) { 114 ExtensionHost* host) {
87 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 115 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
88 // load. Give up. 116 // load. Give up.
89 if (!host && !first_call) 117 if (!host && !first_call)
90 return; 118 return;
91 119
92 // Don't send onStartup events to incognito browser contexts. 120 // Don't send onStartup events to incognito browser contexts.
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 /////////////////////////////////////////////////////////////////////////////// 172 ///////////////////////////////////////////////////////////////////////////////
145 173
146 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 174 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
147 g_factory = LAZY_INSTANCE_INITIALIZER; 175 g_factory = LAZY_INSTANCE_INITIALIZER;
148 176
149 // static 177 // static
150 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 178 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
151 return g_factory.Pointer(); 179 return g_factory.Pointer();
152 } 180 }
153 181
182 // static
183 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) {
184 registry->RegisterBooleanPref(kPrefLastRestartWasDuetoDelayedRestartApi,
Devlin 2016/06/14 16:27:21 nit: DueTo, not Dueto
afakhry 2016/06/14 18:00:04 Done.
185 false);
186 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0);
187 }
188
154 template <> 189 template <>
155 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { 190 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
156 DependsOn(ProcessManagerFactory::GetInstance()); 191 DependsOn(ProcessManagerFactory::GetInstance());
157 } 192 }
158 193
159 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 194 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
160 : browser_context_(context), 195 : browser_context_(context),
196 extension_registry_observer_(this),
197 process_manager_observer_(this),
198 minimum_duration_between_restarts_(base::TimeDelta::FromHours(
199 kMinDurationBetweenSuccessiveRestartsHours)),
161 dispatch_chrome_updated_event_(false), 200 dispatch_chrome_updated_event_(false),
162 extension_registry_observer_(this), 201 did_read_delayed_restart_preferences_(false),
163 process_manager_observer_(this) { 202 was_last_restart_due_to_delayed_restart_api_(false),
203 weak_ptr_factory_(this) {
164 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 204 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
165 // incognito. 205 // incognito.
166 DCHECK(!browser_context_->IsOffTheRecord()); 206 DCHECK(!browser_context_->IsOffTheRecord());
167 207
168 registrar_.Add(this, 208 registrar_.Add(this,
169 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 209 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
170 content::Source<BrowserContext>(context)); 210 content::Source<BrowserContext>(context));
171 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 211 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
172 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); 212 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
173 213
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
312 352
313 void RuntimeAPI::OpenURL(const GURL& update_url) { 353 void RuntimeAPI::OpenURL(const GURL& update_url) {
314 delegate_->OpenURL(update_url); 354 delegate_->OpenURL(update_url);
315 } 355 }
316 356
317 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 357 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
318 return delegate_->GetPlatformInfo(info); 358 return delegate_->GetPlatformInfo(info);
319 } 359 }
320 360
321 bool RuntimeAPI::RestartDevice(std::string* error_message) { 361 bool RuntimeAPI::RestartDevice(std::string* error_message) {
362 if (was_last_restart_due_to_delayed_restart_api_ &&
363 (ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode() ||
364 allow_non_kiosk_apps_restart_api_for_test)) {
365 // We don't allow an app by calling chrome.runtime.restart() to clear the
366 // throttle enforced on it when calling chrome.runtime.restartAfterDelay(),
367 // i.e. the app can't unthrottle itself.
368 // When running in forced kiosk app mode, we assume the following restart
369 // request will succeed.
370 PrefService* pref_service =
371 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
372 browser_context_);
373 DCHECK(pref_service);
374 pref_service->SetBoolean(kPrefLastRestartWasDuetoDelayedRestartApi, true);
375 }
322 return delegate_->RestartDevice(error_message); 376 return delegate_->RestartDevice(error_message);
377 ;
Devlin 2016/06/14 16:27:21 delete
afakhry 2016/06/14 18:00:04 Not sure where that came from. Done!
378 }
379
380 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::RestartDeviceAfterDelay(
381 const std::string& extension_id,
382 int seconds_from_now) {
383 // To achieve as much accuracy as possible, record the time of the call as
384 // |now| here.
385 const base::Time now = base::Time::NowFromSystemTime();
386
387 if (schedule_restart_first_extension_id_.empty()) {
388 schedule_restart_first_extension_id_ = extension_id;
389 } else if (extension_id != schedule_restart_first_extension_id_) {
390 // We only allow the first extension to call this API to call it repeatedly.
391 // Any other extension will fail.
392 return RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION;
393 }
394
395 MaybeCancelRunningDelayedRestartTimer();
396
397 if (seconds_from_now == -1) {
398 // We already stopped the running timer (if any).
399 return RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED;
400 }
401
402 if (!did_read_delayed_restart_preferences_) {
403 // Try to read any previous successful restart attempt time resulting from
404 // this API.
405 PrefService* pref_service =
406 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
407 browser_context_);
408 DCHECK(pref_service);
409
410 was_last_restart_due_to_delayed_restart_api_ =
411 pref_service->GetBoolean(kPrefLastRestartWasDuetoDelayedRestartApi);
412 if (was_last_restart_due_to_delayed_restart_api_) {
413 // We clear this bit if the previous restart was due to this API, so that
414 // we don't throttle restart requests coming after other restarts or
415 // shutdowns not caused by the runtime API.
416 pref_service->SetBoolean(kPrefLastRestartWasDuetoDelayedRestartApi,
417 false);
418 }
419
420 last_delayed_restart_time_ = base::Time::FromDoubleT(
421 pref_service->GetDouble(kPrefLastRestartAfterDelayTime));
422
423 if (!allow_non_kiosk_apps_restart_api_for_test) {
424 // Don't read every time unless in tests.
425 did_read_delayed_restart_preferences_ = true;
426 }
427 }
428
429 return ScheduleDelayedRestart(now, seconds_from_now);
323 } 430 }
324 431
325 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { 432 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
326 return delegate_->OpenOptionsPage(extension); 433 return delegate_->OpenOptionsPage(extension);
327 } 434 }
328 435
436 void RuntimeAPI::MaybeCancelRunningDelayedRestartTimer() {
437 if (restart_after_delay_timer_.IsRunning())
438 restart_after_delay_timer_.Stop();
439 }
440
441 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::ScheduleDelayedRestart(
442 const base::Time& now,
443 int seconds_from_now) {
444 base::TimeDelta delay_till_restart =
445 base::TimeDelta::FromSeconds(seconds_from_now);
446
447 // Throttle restart requests that are received too soon successively, only if
448 // the previous restart was due to this API.
449 bool was_throttled = false;
450 if (was_last_restart_due_to_delayed_restart_api_) {
451 base::Time future_restart_time = now + delay_till_restart;
452 base::TimeDelta future_time_since_last_restart =
Devlin 2016/06/14 16:27:21 nit: "future_time_since_last_restart" sounds a lit
afakhry 2016/06/14 18:00:05 Done.
453 future_restart_time > last_delayed_restart_time_
Devlin 2016/06/14 16:27:21 When would this be false (exempting cases of e.g.
afakhry 2016/06/14 18:00:04 Could be corruption, or the device wall clock has
454 ? future_restart_time - last_delayed_restart_time_
455 : base::TimeDelta();
456 if (future_time_since_last_restart < minimum_duration_between_restarts_) {
457 // Schedule the restart after |minimum_duration_between_restarts_| has
458 // passed.
459 delay_till_restart = minimum_duration_between_restarts_ -
460 (now - last_delayed_restart_time_);
461 was_throttled = true;
462 }
463 }
464
465 restart_after_delay_timer_.Start(
466 FROM_HERE, delay_till_restart,
467 base::Bind(&RuntimeAPI::OnDelayedRestartTimerTimeout,
468 weak_ptr_factory_.GetWeakPtr()));
469
470 return was_throttled ? RestartOnWatchdogStatus::FAILED_THROTTLED
471 : RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED;
472 }
473
474 void RuntimeAPI::OnDelayedRestartTimerTimeout() {
475 // We can persist "now" as the last successful restart time, assuming that the
476 // following restart request will succeed, since it can only fail if requested
477 // by non kiosk apps, and we prevent that from the beginning (unless in
478 // unit tests).
479 // This assumption is important, since once restart is requested, we might not
480 // have enough time to persist the data to disk.
481 double now = base::Time::NowFromSystemTime().ToDoubleT();
482 PrefService* pref_service =
483 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
484 browser_context_);
485 DCHECK(pref_service);
486 pref_service->SetDouble(kPrefLastRestartAfterDelayTime, now);
487 pref_service->SetBoolean(kPrefLastRestartWasDuetoDelayedRestartApi, true);
488
489 std::string error_message;
490 const bool success = delegate_->RestartDevice(&error_message);
491
492 if (!success && !allow_non_kiosk_apps_restart_api_for_test) {
Devlin 2016/06/14 16:27:21 DCHECK?
afakhry 2016/06/14 18:00:04 Done.
493 // This is breaking our above assumption and should never be reached.
494 NOTREACHED();
495 }
496 }
497
498 void RuntimeAPI::AllowNonKioskAppsInRestartAfterDelayForTesting() {
499 allow_non_kiosk_apps_restart_api_for_test = true;
500 }
501
329 /////////////////////////////////////////////////////////////////////////////// 502 ///////////////////////////////////////////////////////////////////////////////
330 503
331 // static 504 // static
332 void RuntimeEventRouter::DispatchOnStartupEvent( 505 void RuntimeEventRouter::DispatchOnStartupEvent(
333 content::BrowserContext* context, 506 content::BrowserContext* context,
334 const std::string& extension_id) { 507 const std::string& extension_id) {
335 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 508 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
336 } 509 }
337 510
338 // static 511 // static
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 std::string message; 719 std::string message;
547 bool result = 720 bool result =
548 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 721 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
549 &message); 722 &message);
550 if (!result) { 723 if (!result) {
551 return RespondNow(Error(message)); 724 return RespondNow(Error(message));
552 } 725 }
553 return RespondNow(NoArguments()); 726 return RespondNow(NoArguments());
554 } 727 }
555 728
729 ExtensionFunction::ResponseAction RuntimeRestartAfterDelayFunction::Run() {
730 if (!allow_non_kiosk_apps_restart_api_for_test &&
731 !ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode()) {
732 return RespondNow(Error(kErrorOnlyKioskModeAllowed));
733 }
734
735 std::unique_ptr<api::runtime::RestartAfterDelay::Params> params(
736 api::runtime::RestartAfterDelay::Params::Create(*args_));
737 EXTENSION_FUNCTION_VALIDATE(params.get());
738 int seconds = params->seconds;
739
740 if (seconds < -1)
Devlin 2016/06/14 16:27:21 maybe also catch 0?
afakhry 2016/06/14 18:00:04 Done.
741 return RespondNow(Error(kErrorInvalidArgument, base::IntToString(seconds)));
742
743 RuntimeAPI::RestartOnWatchdogStatus request_status =
744 RuntimeAPI::GetFactoryInstance()
745 ->Get(browser_context())
746 ->RestartDeviceAfterDelay(extension()->id(), seconds);
747
748 switch (request_status) {
749 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION:
750 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed));
751
752 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_THROTTLED:
753 return RespondNow(Error(kErrorRequestedTooSoon));
754
755 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED:
756 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED:
757 return RespondNow(NoArguments());
758 }
759
760 NOTREACHED();
761 return RespondNow(Error(kErrorInvalidStatus));
762 }
763
556 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 764 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
557 runtime::PlatformInfo info; 765 runtime::PlatformInfo info;
558 if (!RuntimeAPI::GetFactoryInstance() 766 if (!RuntimeAPI::GetFactoryInstance()
559 ->Get(browser_context()) 767 ->Get(browser_context())
560 ->GetPlatformInfo(&info)) { 768 ->GetPlatformInfo(&info)) {
561 return RespondNow(Error(kPlatformInfoUnavailable)); 769 return RespondNow(Error(kPlatformInfoUnavailable));
562 } 770 }
563 return RespondNow( 771 return RespondNow(
564 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 772 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
565 } 773 }
(...skipping 13 matching lines...) Expand all
579 content::ChildProcessSecurityPolicy* policy = 787 content::ChildProcessSecurityPolicy* policy =
580 content::ChildProcessSecurityPolicy::GetInstance(); 788 content::ChildProcessSecurityPolicy::GetInstance();
581 policy->GrantReadFileSystem(renderer_id, filesystem_id); 789 policy->GrantReadFileSystem(renderer_id, filesystem_id);
582 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); 790 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
583 dict->SetString("fileSystemId", filesystem_id); 791 dict->SetString("fileSystemId", filesystem_id);
584 dict->SetString("baseName", relative_path); 792 dict->SetString("baseName", relative_path);
585 return RespondNow(OneArgument(std::move(dict))); 793 return RespondNow(OneArgument(std::move(dict)));
586 } 794 }
587 795
588 } // namespace extensions 796 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698