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

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

Powered by Google App Engine
This is Rietveld 408576698