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

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: Missing include 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 on the watchdog timer
87 // timing out.
88 constexpr char kPrefLastRestartAfterDelayTime[] =
89 "last_restart_on_watchdog_time";
90
91 // Error and status messages strings for the restartAfterDelay() API.
92 constexpr char kErrorInvalidArgument[] = "Invalid argument: *.";
93 constexpr char kErrorOnlyKioskModeAllowed[] =
94 "API available only for ChromeOS kiosk mode.";
95 constexpr char kErrorOnlyFirstExtensionAllowed[] =
96 "Not the first extension to call this API.";
97 constexpr char kErrorInvalidStatus[] = "Invalid restart request status.";
98 constexpr char kErrorRequestedTooSoon[] =
99 "Restart was requested too soon. It was throttled instead.";
100
101 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3;
102
103 // This is used for browsertests, so that we can test the restartAfterDelay
104 // API without a kiost app.
105 bool allow_non_kiost_apps_restart_api_for_test = false;
106
83 void DispatchOnStartupEventImpl(BrowserContext* browser_context, 107 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
84 const std::string& extension_id, 108 const std::string& extension_id,
85 bool first_call, 109 bool first_call,
86 ExtensionHost* host) { 110 ExtensionHost* host) {
87 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 111 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
88 // load. Give up. 112 // load. Give up.
89 if (!host && !first_call) 113 if (!host && !first_call)
90 return; 114 return;
91 115
92 // Don't send onStartup events to incognito browser contexts. 116 // Don't send onStartup events to incognito browser contexts.
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 /////////////////////////////////////////////////////////////////////////////// 168 ///////////////////////////////////////////////////////////////////////////////
145 169
146 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> > 170 static base::LazyInstance<BrowserContextKeyedAPIFactory<RuntimeAPI> >
147 g_factory = LAZY_INSTANCE_INITIALIZER; 171 g_factory = LAZY_INSTANCE_INITIALIZER;
148 172
149 // static 173 // static
150 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() { 174 BrowserContextKeyedAPIFactory<RuntimeAPI>* RuntimeAPI::GetFactoryInstance() {
151 return g_factory.Pointer(); 175 return g_factory.Pointer();
152 } 176 }
153 177
178 // static
179 void RuntimeAPI::RegisterPrefs(PrefRegistrySimple* registry) {
180 registry->RegisterDoublePref(kPrefLastRestartAfterDelayTime, 0.0);
181 }
182
154 template <> 183 template <>
155 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { 184 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
156 DependsOn(ProcessManagerFactory::GetInstance()); 185 DependsOn(ProcessManagerFactory::GetInstance());
157 } 186 }
158 187
159 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 188 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
160 : browser_context_(context), 189 : browser_context_(context),
161 dispatch_chrome_updated_event_(false),
162 extension_registry_observer_(this), 190 extension_registry_observer_(this),
163 process_manager_observer_(this) { 191 process_manager_observer_(this),
192 minimum_duration_between_restarts_(base::TimeDelta::FromHours(
193 kMinDurationBetweenSuccessiveRestartsHours)),
194 weak_ptr_factory_(this) {
164 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 195 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
165 // incognito. 196 // incognito.
166 DCHECK(!browser_context_->IsOffTheRecord()); 197 DCHECK(!browser_context_->IsOffTheRecord());
167 198
168 registrar_.Add(this, 199 registrar_.Add(this,
169 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 200 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
170 content::Source<BrowserContext>(context)); 201 content::Source<BrowserContext>(context));
171 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 202 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
172 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); 203 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
173 204
174 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate( 205 delegate_ = ExtensionsBrowserClient::Get()->CreateRuntimeAPIDelegate(
175 browser_context_); 206 browser_context_);
176 207
208 delegate_->SetOnDeviceShutdownCallback(base::Bind(
209 &RuntimeAPI::OnDeviceShutdown, weak_ptr_factory_.GetWeakPtr()));
210
177 // Check if registered events are up-to-date. We can only do this once 211 // Check if registered events are up-to-date. We can only do this once
178 // per browser context, since it updates internal state when called. 212 // per browser context, since it updates internal state when called.
179 dispatch_chrome_updated_event_ = 213 dispatch_chrome_updated_event_ =
180 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_); 214 ExtensionsBrowserClient::Get()->DidVersionUpdate(browser_context_);
181 } 215 }
182 216
183 RuntimeAPI::~RuntimeAPI() { 217 RuntimeAPI::~RuntimeAPI() {
184 } 218 }
185 219
186 void RuntimeAPI::Observe(int type, 220 void RuntimeAPI::Observe(int type,
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
312 346
313 void RuntimeAPI::OpenURL(const GURL& update_url) { 347 void RuntimeAPI::OpenURL(const GURL& update_url) {
314 delegate_->OpenURL(update_url); 348 delegate_->OpenURL(update_url);
315 } 349 }
316 350
317 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 351 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
318 return delegate_->GetPlatformInfo(info); 352 return delegate_->GetPlatformInfo(info);
319 } 353 }
320 354
321 bool RuntimeAPI::RestartDevice(std::string* error_message) { 355 bool RuntimeAPI::RestartDevice(std::string* error_message) {
322 return delegate_->RestartDevice(error_message); 356 expecting_non_watchdog_restart_ = delegate_->RestartDevice(error_message);
357 return expecting_non_watchdog_restart_;
358 }
359
360 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::RestartDeviceOnWatchdogTimeout(
361 const std::string& extension_id,
362 int seconds_from_now) {
363 // To achieve as much accuracy as possible, record the time of the call as
364 // |now| here.
365 const base::Time now = base::Time::NowFromSystemTime();
366
367 if (schedule_restart_first_extension_id_.empty()) {
368 schedule_restart_first_extension_id_ = extension_id;
369 } else if (extension_id != schedule_restart_first_extension_id_) {
370 // We only allow the first extension to call this API to call it repeatedly.
371 // Any other extension will fail.
372 return RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION;
373 }
374
375 MaybeCancelRunningWatchdogTimer();
376
377 if (seconds_from_now == -1) {
378 // We already stopped the running timer (if any).
379 return RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED;
380 }
381
382 // Try to read any previous successful restart attempt time resulting from
383 // this API.
384 PrefService* pref_service =
385 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
386 browser_context_);
387 DCHECK(pref_service);
388 double last_restart_time =
389 pref_service->GetDouble(kPrefLastRestartAfterDelayTime);
390
391 return ScheduleDelayedRestart(now, seconds_from_now, last_restart_time);
323 } 392 }
324 393
325 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { 394 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
326 return delegate_->OpenOptionsPage(extension); 395 return delegate_->OpenOptionsPage(extension);
327 } 396 }
328 397
398 void RuntimeAPI::MaybeCancelRunningWatchdogTimer() {
399 if (watchdog_timer_.IsRunning())
400 watchdog_timer_.Stop();
401 }
402
403 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::ScheduleDelayedRestart(
404 const base::Time& now,
405 int seconds_from_now,
406 double stored_last_restart) {
407 base::TimeDelta delay_till_restart =
408 base::TimeDelta::FromSeconds(seconds_from_now);
409
410 // Throttle restart requests that are received too soon successively.
411 bool was_throttled = false;
412 base::Time last_restart_time = base::Time::FromDoubleT(stored_last_restart);
413 base::Time future_restart_time = now + delay_till_restart;
414 base::TimeDelta future_time_since_last_restart =
415 future_restart_time > last_restart_time
416 ? future_restart_time - last_restart_time
417 : base::TimeDelta();
418 if (future_time_since_last_restart < minimum_duration_between_restarts_) {
419 // Schedule the restart after |minimum_duration_between_restarts_| has
420 // passed.
421 delay_till_restart =
422 minimum_duration_between_restarts_ - (now - last_restart_time);
423 was_throttled = true;
424 }
425
426 watchdog_timer_.Start(FROM_HERE, delay_till_restart,
427 base::Bind(&RuntimeAPI::OnRestartWatchdogTimeout,
428 weak_ptr_factory_.GetWeakPtr()));
429
430 return was_throttled ? RestartOnWatchdogStatus::FAILED_THROTTLED
431 : RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED;
432 }
433
434 void RuntimeAPI::OnRestartWatchdogTimeout() {
435 // We can persist "now" as the last successful restart time, assuming that the
436 // following restart request will succeed, since it can only fail if requested
437 // by non kiosk apps, and we prevent that from the beginning (unless in
438 // browsertests).
439 // This assumption is important, since once restart is requested, we might not
440 // have enough time to persist the data to disk.
441 expecting_watchdog_restart_ = true;
442 std::string error_message;
443 const bool success = delegate_->RestartDevice(&error_message);
444
445 if (!success && !allow_non_kiost_apps_restart_api_for_test) {
446 // This is breaking our above assumption and should never be reached.
447 NOTREACHED();
448 }
449 }
450
451 void RuntimeAPI::OnDeviceShutdown() {
Devlin 2016/06/10 00:34:45 This worries me for a couple of reasons: - We typi
xiyuan 2016/06/10 16:33:48 This would not work because the restartAfterDelay
Devlin 2016/06/10 18:06:17 Sorry, I was unclear. I meant we should have a me
afakhry 2016/06/13 14:46:00 I didn't get this comment until I read your explan
452 if (expecting_non_watchdog_restart_) {
453 // Non-watchdog restarts should never clear the watchdog throttle.
454 return;
455 }
456
457 // Persist "now" as the current last watchdog restart time if we are expecting
458 // one, clear it otherwise.
459 double restart_time = 0.0;
460 if (expecting_watchdog_restart_)
461 restart_time = base::Time::NowFromSystemTime().ToDoubleT();
462
463 PrefService* pref_service =
464 ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
465 browser_context_);
466 DCHECK(pref_service);
467 pref_service->SetDouble(kPrefLastRestartAfterDelayTime, restart_time);
468 }
469
470 void RuntimeAPI::AllowNonKiostAppsInRestartOnWatchdogForTesting() {
471 allow_non_kiost_apps_restart_api_for_test = true;
472 }
473
329 /////////////////////////////////////////////////////////////////////////////// 474 ///////////////////////////////////////////////////////////////////////////////
330 475
331 // static 476 // static
332 void RuntimeEventRouter::DispatchOnStartupEvent( 477 void RuntimeEventRouter::DispatchOnStartupEvent(
333 content::BrowserContext* context, 478 content::BrowserContext* context,
334 const std::string& extension_id) { 479 const std::string& extension_id) {
335 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 480 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
336 } 481 }
337 482
338 // static 483 // static
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 std::string message; 691 std::string message;
547 bool result = 692 bool result =
548 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 693 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
549 &message); 694 &message);
550 if (!result) { 695 if (!result) {
551 return RespondNow(Error(message)); 696 return RespondNow(Error(message));
552 } 697 }
553 return RespondNow(NoArguments()); 698 return RespondNow(NoArguments());
554 } 699 }
555 700
701 ExtensionFunction::ResponseAction RuntimeRestartAfterDelayFunction::Run() {
702 if (!allow_non_kiost_apps_restart_api_for_test &&
703 !ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode()) {
704 return RespondNow(Error(kErrorOnlyKioskModeAllowed));
705 }
706
707 std::unique_ptr<api::runtime::RestartAfterDelay::Params> params(
708 api::runtime::RestartAfterDelay::Params::Create(*args_));
709 EXTENSION_FUNCTION_VALIDATE(params.get());
710 int seconds = params->seconds;
711
712 if (seconds < -1)
713 return RespondNow(Error(kErrorInvalidArgument, base::IntToString(seconds)));
714
715 RuntimeAPI::RestartOnWatchdogStatus request_status =
716 RuntimeAPI::GetFactoryInstance()
717 ->Get(browser_context())
718 ->RestartDeviceOnWatchdogTimeout(extension()->id(), seconds);
719
720 switch (request_status) {
721 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION:
722 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed));
723
724 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_THROTTLED:
725 return RespondNow(Error(kErrorRequestedTooSoon));
726
727 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELED:
728 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED:
729 return RespondNow(NoArguments());
730 }
731
732 NOTREACHED();
733 return RespondNow(Error(kErrorInvalidStatus));
734 }
735
556 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 736 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
557 runtime::PlatformInfo info; 737 runtime::PlatformInfo info;
558 if (!RuntimeAPI::GetFactoryInstance() 738 if (!RuntimeAPI::GetFactoryInstance()
559 ->Get(browser_context()) 739 ->Get(browser_context())
560 ->GetPlatformInfo(&info)) { 740 ->GetPlatformInfo(&info)) {
561 return RespondNow(Error(kPlatformInfoUnavailable)); 741 return RespondNow(Error(kPlatformInfoUnavailable));
562 } 742 }
563 return RespondNow( 743 return RespondNow(
564 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 744 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
565 } 745 }
(...skipping 13 matching lines...) Expand all
579 content::ChildProcessSecurityPolicy* policy = 759 content::ChildProcessSecurityPolicy* policy =
580 content::ChildProcessSecurityPolicy::GetInstance(); 760 content::ChildProcessSecurityPolicy::GetInstance();
581 policy->GrantReadFileSystem(renderer_id, filesystem_id); 761 policy->GrantReadFileSystem(renderer_id, filesystem_id);
582 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); 762 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
583 dict->SetString("fileSystemId", filesystem_id); 763 dict->SetString("fileSystemId", filesystem_id);
584 dict->SetString("baseName", relative_path); 764 dict->SetString("baseName", relative_path);
585 return RespondNow(OneArgument(std::move(dict))); 765 return RespondNow(OneArgument(std::move(dict)));
586 } 766 }
587 767
588 } // namespace extensions 768 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698