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

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: Rebase + Convert browser tests to unit tests. 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/logging.h" 11 #include "base/logging.h"
12 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h" 14 #include "base/values.h"
14 #include "base/version.h" 15 #include "base/version.h"
16 #include "components/prefs/pref_registry_simple.h"
17 #include "components/prefs/pref_service.h"
15 #include "content/public/browser/browser_context.h" 18 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/child_process_security_policy.h" 19 #include "content/public/browser/child_process_security_policy.h"
17 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_frame_host.h" 21 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h" 22 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/api/runtime/runtime_api_delegate.h" 23 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
21 #include "extensions/browser/event_router.h" 24 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/extension_host.h" 25 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_prefs.h" 26 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h" 27 #include "extensions/browser/extension_registry.h"
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 "pending_on_installed_event_dispatch_info"; 72 "pending_on_installed_event_dispatch_info";
70 73
71 // Previously installed version number. 74 // Previously installed version number.
72 const char kPrefPreviousVersion[] = "previous_version"; 75 const char kPrefPreviousVersion[] = "previous_version";
73 76
74 // The name of the directory to be returned by getPackageDirectoryEntry. This 77 // The name of the directory to be returned by getPackageDirectoryEntry. This
75 // particular value does not matter to user code, but is chosen for consistency 78 // particular value does not matter to user code, but is chosen for consistency
76 // with the equivalent Pepper API. 79 // with the equivalent Pepper API.
77 const char kPackageDirectoryPath[] = "crxfs"; 80 const char kPackageDirectoryPath[] = "crxfs";
78 81
82 // Preference key for storing the last successful restart on the watchdog timer
83 // timing out.
84 constexpr char kPrefLastRestartAfterDelayTime[] =
85 "last_restart_on_watchdog_time";
86
87 // Error and status messages strings for the restartAfterDelay() API.
88 constexpr char kErrorInvalidArgument[] = "Invalid argument: *.";
89 constexpr char kErrorOnlyKioskModeAllowed[] =
90 "API available only for ChromeOS kiosk mode.";
91 constexpr char kErrorOnlyFirstExtensionAllowed[] =
92 "Not the first extension to call this API.";
93 constexpr char kErrorInvalidStatus[] = "Invalid restart request status.";
94
95 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3;
96
97 // This is used for browsertests, so that we can test the restartAfterDelay
98 // API without a kiost app.
99 bool allow_non_kiost_apps_restart_api_for_test = false;
100
79 void DispatchOnStartupEventImpl(BrowserContext* browser_context, 101 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
80 const std::string& extension_id, 102 const std::string& extension_id,
81 bool first_call, 103 bool first_call,
82 ExtensionHost* host) { 104 ExtensionHost* host) {
83 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 105 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
84 // load. Give up. 106 // load. Give up.
85 if (!host && !first_call) 107 if (!host && !first_call)
86 return; 108 return;
87 109
88 // Don't send onStartup events to incognito browser contexts. 110 // Don't send onStartup events to incognito browser contexts.
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 /////////////////////////////////////////////////////////////////////////////// 162 ///////////////////////////////////////////////////////////////////////////////
141 163
142 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 164 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
143 g_factory = LAZY_INSTANCE_INITIALIZER; 165 g_factory = LAZY_INSTANCE_INITIALIZER;
144 166
145 // static 167 // static
146 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 168 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
147 return g_factory.Pointer(); 169 return g_factory.Pointer();
148 } 170 }
149 171
172 // static
173 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) {
174 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0);
175 }
176
150 template <> 177 template <>
151 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { 178 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
152 DependsOn(ProcessManagerFactory::GetInstance()); 179 DependsOn(ProcessManagerFactory::GetInstance());
153 } 180 }
154 181
155 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 182 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
156 : browser_context_(context), 183 : browser_context_(context),
157 dispatch_chrome_updated_event_(false), 184 dispatch_chrome_updated_event_(false),
158 extension_registry_observer_(this), 185 extension_registry_observer_(this),
159 process_manager_observer_(this) { 186 process_manager_observer_(this),
187 minimum_duration_between_restarts_(base::TimeDelta::FromHours(
188 kMinDurationBetweenSuccessiveRestartsHours)),
189 weak_ptr_factory_(this) {
160 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 190 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
161 // incognito. 191 // incognito.
162 DCHECK(!browser_context_->IsOffTheRecord()); 192 DCHECK(!browser_context_->IsOffTheRecord());
163 193
164 registrar_.Add(this, 194 registrar_.Add(this,
165 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 195 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
166 content::Source<BrowserContext>(context)); 196 content::Source<BrowserContext>(context));
167 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 197 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
168 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); 198 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
169 199
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 } 344 }
315 345
316 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 346 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
317 return delegate_->GetPlatformInfo(info); 347 return delegate_->GetPlatformInfo(info);
318 } 348 }
319 349
320 bool RuntimeAPI::RestartDevice(std::string* error_message) { 350 bool RuntimeAPI::RestartDevice(std::string* error_message) {
321 return delegate_->RestartDevice(error_message); 351 return delegate_->RestartDevice(error_message);
322 } 352 }
323 353
354 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::RestartDeviceOnWatchdogTimeout(
355 const std::string& extension_id,
356 int seconds_from_now) {
357 // To achieve as much accuracy as possible, record the time of the call as
358 // |now| here.
359 const base::Time now = base::Time::NowFromSystemTime();
360
361 if (schedule_restart_first_extension_id_.empty()) {
362 schedule_restart_first_extension_id_ = extension_id;
363 } else if (extension_id != schedule_restart_first_extension_id_) {
364 // We only allow the first extension to call this API to call it repeatedly.
365 // Any other extension will fail.
366 return RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION;
367 }
368
369 MaybeCancelRunningWatchdogTimer();
370
371 if (seconds_from_now == -1) {
372 // We already stopped the running timer (if any).
373 return RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELLED;
374 }
375
376 // Try to read any previous successful restart attempt time resulting from
377 // this API.
378 PrefService* pref_service =
379 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
380 browser_context_);
381 DCHECK(pref_service);
382 double last_restart_time =
383 pref_service->GetDouble(kPrefLastRestartAfterDelayTime);
384
385 ScheduleDelayedRestart(now, seconds_from_now, last_restart_time);
386 return RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED;
387 }
388
324 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { 389 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
325 return delegate_->OpenOptionsPage(extension); 390 return delegate_->OpenOptionsPage(extension);
326 } 391 }
327 392
393 void RuntimeAPI::MaybeCancelRunningWatchdogTimer() {
394 if (watchdog_timer_.IsRunning())
395 watchdog_timer_.Stop();
396 }
397
398 void RuntimeAPI::ScheduleDelayedRestart(const base::Time& now,
399 int seconds_from_now,
400 double stored_last_restart) {
401 base::TimeDelta delay_till_restart =
402 base::TimeDelta::FromSeconds(seconds_from_now);
403
404 // Throttle restart requests that are received too soon successively.
405 base::Time last_restart_time = base::Time::FromDoubleT(stored_last_restart);
406 base::Time future_restart_time = now + delay_till_restart;
407 base::TimeDelta future_time_since_last_restart =
408 future_restart_time > last_restart_time
409 ? future_restart_time - last_restart_time
410 : base::TimeDelta();
411 if (future_time_since_last_restart < minimum_duration_between_restarts_) {
412 // Schedule the restart after |minimum_duration_between_restarts_| has
413 // passed.
414 delay_till_restart =
415 minimum_duration_between_restarts_ - (now - last_restart_time);
416 }
417
418 watchdog_timer_.Start(FROM_HERE, delay_till_restart,
419 base::Bind(&RuntimeAPI::OnRestartWatchdogTimeout,
420 weak_ptr_factory_.GetWeakPtr()));
421 }
422
423 void RuntimeAPI::OnRestartWatchdogTimeout() {
424 // We can persist "now" as the last successful restart time, assuming that the
425 // following restart request will succeed, since it can only fail if requested
426 // by non kiosk apps, and we prevent that from the beginning (unless in
427 // browsertests).
428 // This assumption is important, since once restart is requested, we might not
429 // have enough time to persist the data to disk.
430 PrefService* pref_service =
431 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
432 browser_context_);
433 DCHECK(pref_service);
434 base::Time now = base::Time::NowFromSystemTime();
435 pref_service->SetDouble(kPrefLastRestartAfterDelayTime, now.ToDoubleT());
436
437 std::string error_message;
438 const bool success = delegate_->RestartDevice(&error_message);
439
440 if (!success && !allow_non_kiost_apps_restart_api_for_test) {
441 // This is breaking our above assumption and should never be reached.
442 NOTREACHED();
443 }
444 }
445
446 void RuntimeAPI::AllowNonKiostAppsInRestartOnWatchdogForTesting() {
447 allow_non_kiost_apps_restart_api_for_test = true;
448 }
449
328 /////////////////////////////////////////////////////////////////////////////// 450 ///////////////////////////////////////////////////////////////////////////////
329 451
330 // static 452 // static
331 void RuntimeEventRouter::DispatchOnStartupEvent( 453 void RuntimeEventRouter::DispatchOnStartupEvent(
332 content::BrowserContext* context, 454 content::BrowserContext* context,
333 const std::string& extension_id) { 455 const std::string& extension_id) {
334 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 456 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
335 } 457 }
336 458
337 // static 459 // static
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 std::string message; 666 std::string message;
545 bool result = 667 bool result =
546 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 668 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
547 &message); 669 &message);
548 if (!result) { 670 if (!result) {
549 return RespondNow(Error(message)); 671 return RespondNow(Error(message));
550 } 672 }
551 return RespondNow(NoArguments()); 673 return RespondNow(NoArguments());
552 } 674 }
553 675
676 ExtensionFunction::ResponseAction RuntimeRestartAfterDelayFunction::Run() {
677 if (!allow_non_kiost_apps_restart_api_for_test &&
678 !ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode()) {
679 return RespondNow(Error(kErrorOnlyKioskModeAllowed));
680 }
681
682 std::unique_ptr<api::runtime::RestartAfterDelay::Params> params(
683 api::runtime::RestartAfterDelay::Params::Create(*args_));
684 EXTENSION_FUNCTION_VALIDATE(params.get());
685 int seconds = params->seconds;
686
687 if (seconds < -1)
688 return RespondNow(Error(kErrorInvalidArgument, base::IntToString(seconds)));
689
690 RuntimeAPI::RestartOnWatchdogStatus request_status =
691 RuntimeAPI::GetFactoryInstance()
692 ->Get(browser_context())
693 ->RestartDeviceOnWatchdogTimeout(extension()->id(), seconds);
694
695 switch (request_status) {
696 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION:
697 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed));
698
699 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELLED:
700 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED:
701 return RespondNow(NoArguments());
702 }
703
704 NOTREACHED();
705 return RespondNow(Error(kErrorInvalidStatus));
706 }
707
554 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 708 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
555 runtime::PlatformInfo info; 709 runtime::PlatformInfo info;
556 if (!RuntimeAPI::GetFactoryInstance() 710 if (!RuntimeAPI::GetFactoryInstance()
557 ->Get(browser_context()) 711 ->Get(browser_context())
558 ->GetPlatformInfo(&info)) { 712 ->GetPlatformInfo(&info)) {
559 return RespondNow(Error(kPlatformInfoUnavailable)); 713 return RespondNow(Error(kPlatformInfoUnavailable));
560 } 714 }
561 return RespondNow( 715 return RespondNow(
562 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 716 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
563 } 717 }
(...skipping 13 matching lines...) Expand all
577 content::ChildProcessSecurityPolicy* policy = 731 content::ChildProcessSecurityPolicy* policy =
578 content::ChildProcessSecurityPolicy::GetInstance(); 732 content::ChildProcessSecurityPolicy::GetInstance();
579 policy->GrantReadFileSystem(renderer_id, filesystem_id); 733 policy->GrantReadFileSystem(renderer_id, filesystem_id);
580 base::DictionaryValue* dict = new base::DictionaryValue(); 734 base::DictionaryValue* dict = new base::DictionaryValue();
581 dict->SetString("fileSystemId", filesystem_id); 735 dict->SetString("fileSystemId", filesystem_id);
582 dict->SetString("baseName", relative_path); 736 dict->SetString("baseName", relative_path);
583 return RespondNow(OneArgument(dict)); 737 return RespondNow(OneArgument(dict));
584 } 738 }
585 739
586 } // namespace extensions 740 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698