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

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 + Nits 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"
15 #include "content/public/browser/browser_context.h" 16 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/child_process_security_policy.h" 17 #include "content/public/browser/child_process_security_policy.h"
17 #include "content/public/browser/notification_service.h" 18 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_frame_host.h" 19 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h" 20 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/api/runtime/runtime_api_delegate.h" 21 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
21 #include "extensions/browser/event_router.h" 22 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/extension_host.h" 23 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_prefs.h" 24 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h" 25 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h" 26 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/extension_util.h" 27 #include "extensions/browser/extension_util.h"
27 #include "extensions/browser/extensions_browser_client.h" 28 #include "extensions/browser/extensions_browser_client.h"
28 #include "extensions/browser/lazy_background_task_queue.h" 29 #include "extensions/browser/lazy_background_task_queue.h"
29 #include "extensions/browser/notification_types.h" 30 #include "extensions/browser/notification_types.h"
30 #include "extensions/browser/process_manager_factory.h" 31 #include "extensions/browser/process_manager_factory.h"
32 #include "extensions/browser/state_store.h"
31 #include "extensions/common/api/runtime.h" 33 #include "extensions/common/api/runtime.h"
32 #include "extensions/common/error_utils.h" 34 #include "extensions/common/error_utils.h"
33 #include "extensions/common/extension.h" 35 #include "extensions/common/extension.h"
34 #include "extensions/common/manifest_handlers/background_info.h" 36 #include "extensions/common/manifest_handlers/background_info.h"
35 #include "extensions/common/manifest_handlers/shared_module_info.h" 37 #include "extensions/common/manifest_handlers/shared_module_info.h"
36 #include "storage/browser/fileapi/isolated_context.h" 38 #include "storage/browser/fileapi/isolated_context.h"
37 #include "url/gurl.h" 39 #include "url/gurl.h"
38 40
39 using content::BrowserContext; 41 using content::BrowserContext;
40 42
41 namespace extensions { 43 namespace extensions {
42 44
43 namespace runtime = api::runtime; 45 namespace runtime = api::runtime;
44 46
45 namespace { 47 namespace {
46 48
47 const char kNoBackgroundPageError[] = "You do not have a background page."; 49 constexpr char kNoBackgroundPageError[] = "You do not have a background page.";
Devlin 2016/05/20 17:26:32 Is constexpr preferred by Chromium style now?
afakhry 2016/05/21 01:13:00 Yes. Please, see: http://chromium-cpp.appspot.com/
Devlin 2016/05/23 16:49:39 From there "Don't go out of the way to convert exi
afakhry 2016/05/25 01:55:46 Done.
48 const char kPageLoadError[] = "Background page failed to load."; 50 constexpr char kPageLoadError[] = "Background page failed to load.";
49 const char kFailedToCreateOptionsPage[] = "Could not create an options page."; 51 constexpr char kFailedToCreateOptionsPage[] =
50 const char kInstallId[] = "id"; 52 "Could not create an options page.";
51 const char kInstallReason[] = "reason"; 53 constexpr char kInstallId[] = "id";
52 const char kInstallReasonChromeUpdate[] = "chrome_update"; 54 constexpr char kInstallReason[] = "reason";
53 const char kInstallReasonUpdate[] = "update"; 55 constexpr char kInstallReasonChromeUpdate[] = "chrome_update";
54 const char kInstallReasonInstall[] = "install"; 56 constexpr char kInstallReasonUpdate[] = "update";
55 const char kInstallReasonSharedModuleUpdate[] = "shared_module_update"; 57 constexpr char kInstallReasonInstall[] = "install";
56 const char kInstallPreviousVersion[] = "previousVersion"; 58 constexpr char kInstallReasonSharedModuleUpdate[] = "shared_module_update";
57 const char kInvalidUrlError[] = "Invalid URL: \"*\"."; 59 constexpr char kInstallPreviousVersion[] = "previousVersion";
58 const char kPlatformInfoUnavailable[] = "Platform information unavailable."; 60 constexpr char kInvalidUrlError[] = "Invalid URL: \"*\".";
61 constexpr char kPlatformInfoUnavailable[] = "Platform information unavailable.";
59 62
60 const char kUpdatesDisabledError[] = "Autoupdate is not enabled."; 63 constexpr char kUpdatesDisabledError[] = "Autoupdate is not enabled.";
61 64
62 // A preference key storing the url loaded when an extension is uninstalled. 65 // A preference key storing the url loaded when an extension is uninstalled.
63 const char kUninstallUrl[] = "uninstall_url"; 66 constexpr char kUninstallUrl[] = "uninstall_url";
64 67
65 // A preference key storing the information about an extension that was 68 // A preference key storing the information about an extension that was
66 // installed but not loaded. We keep the pending info here so that we can send 69 // installed but not loaded. We keep the pending info here so that we can send
67 // chrome.runtime.onInstalled event during the extension load. 70 // chrome.runtime.onInstalled event during the extension load.
68 const char kPrefPendingOnInstalledEventDispatchInfo[] = 71 constexpr char kPrefPendingOnInstalledEventDispatchInfo[] =
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 constexpr 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 constexpr char kPackageDirectoryPath[] = "crxfs";
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 constexpr char kMessageRestartRequestCanceled[] =
93 "Restart request was cancelled.";
94 constexpr char kMessageNoMoreRestartsScheduled[] =
95 "No more restarts are scheduled.";
96
97 constexpr int kMinDurationBetweenSuccessiveRestartsHours = 3;
98
99 // This is used for browsertests, so that we can test the restartOnWatchdog
100 // API without a kiost app.
101 bool allow_non_kiost_apps_restart_api_for_test = false;
78 102
79 void DispatchOnStartupEventImpl(BrowserContext* browser_context, 103 void DispatchOnStartupEventImpl(BrowserContext* browser_context,
80 const std::string& extension_id, 104 const std::string& extension_id,
81 bool first_call, 105 bool first_call,
82 ExtensionHost* host) { 106 ExtensionHost* host) {
83 // A NULL host from the LazyBackgroundTaskQueue means the page failed to 107 // A NULL host from the LazyBackgroundTaskQueue means the page failed to
84 // load. Give up. 108 // load. Give up.
85 if (!host && !first_call) 109 if (!host && !first_call)
86 return; 110 return;
87 111
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 173
150 template <> 174 template <>
151 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() { 175 void BrowserContextKeyedAPIFactory<RuntimeAPI>::DeclareFactoryDependencies() {
152 DependsOn(ProcessManagerFactory::GetInstance()); 176 DependsOn(ProcessManagerFactory::GetInstance());
153 } 177 }
154 178
155 RuntimeAPI::RuntimeAPI(content::BrowserContext* context) 179 RuntimeAPI::RuntimeAPI(content::BrowserContext* context)
156 : browser_context_(context), 180 : browser_context_(context),
157 dispatch_chrome_updated_event_(false), 181 dispatch_chrome_updated_event_(false),
158 extension_registry_observer_(this), 182 extension_registry_observer_(this),
159 process_manager_observer_(this) { 183 process_manager_observer_(this),
184 minimum_duration_between_restarts_(base::TimeDelta::FromHours(
185 kMinDurationBetweenSuccessiveRestartsHours)),
186 weak_ptr_factory_(this) {
160 // RuntimeAPI is redirected in incognito, so |browser_context_| is never 187 // RuntimeAPI is redirected in incognito, so |browser_context_| is never
161 // incognito. 188 // incognito.
162 DCHECK(!browser_context_->IsOffTheRecord()); 189 DCHECK(!browser_context_->IsOffTheRecord());
163 190
164 registrar_.Add(this, 191 registrar_.Add(this,
165 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 192 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
166 content::Source<BrowserContext>(context)); 193 content::Source<BrowserContext>(context));
167 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 194 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
168 process_manager_observer_.Add(ProcessManager::Get(browser_context_)); 195 process_manager_observer_.Add(ProcessManager::Get(browser_context_));
169 196
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 } 341 }
315 342
316 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) { 343 bool RuntimeAPI::GetPlatformInfo(runtime::PlatformInfo* info) {
317 return delegate_->GetPlatformInfo(info); 344 return delegate_->GetPlatformInfo(info);
318 } 345 }
319 346
320 bool RuntimeAPI::RestartDevice(std::string* error_message) { 347 bool RuntimeAPI::RestartDevice(std::string* error_message) {
321 return delegate_->RestartDevice(error_message); 348 return delegate_->RestartDevice(error_message);
322 } 349 }
323 350
351 RuntimeAPI::RestartOnWatchdogStatus RuntimeAPI::RestartDeviceOnWatchdogTimeout(
352 const std::string& extension_id,
353 int seconds_from_now,
354 const OnWatchdogTimeoutCallback& callback) {
355 // To achieve as much accuracy as possible, record the time of the call as
Devlin 2016/05/20 17:26:32 If we have a min time of 3 hours, does "as much ac
afakhry 2016/05/21 01:13:00 The minimum time is for any two successive restart
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();
369 return RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELLED;
370 }
371
372 // Try to read any previously recorded restart request time.
373 StateStore* storage = ExtensionSystem::Get(browser_context_)->state_store();
374 if (storage) {
375 storage->GetExtensionValue(
Devlin 2016/05/20 17:26:32 The fact that we need to store this value implies
afakhry 2016/05/21 01:13:00 Yes, it is supposed to persist accross device powe
Devlin 2016/05/23 16:49:39 I think I understand the point of it, my question
xiyuan 2016/05/24 16:47:05 The minimum time 3 hours between API reboots means
Devlin 2016/05/24 16:51:46 Right, but I think we should store that when/if we
xiyuan 2016/05/24 16:56:04 The code here is to read the persist value to calc
Devlin 2016/05/24 16:57:31 Ah, whoops! Misread this. You're right. Though
afakhry 2016/05/25 01:55:46 We now get it from the pref service.
376 extension_id, kPrefRestartOnWatchdogTime,
377 base::Bind(&RuntimeAPI::ScheduleDelayedRestart,
378 weak_ptr_factory_.GetWeakPtr(), extension_id, now,
379 seconds_from_now, callback));
380 } else {
381 std::unique_ptr<base::Value> stored_last_restart(
382 new base::FundamentalValue(0.0));
383 ScheduleDelayedRestart(extension_id, now, seconds_from_now, callback,
384 std::move(stored_last_restart));
385 }
386
387 return RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED;
388 }
389
324 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) { 390 bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
325 return delegate_->OpenOptionsPage(extension); 391 return delegate_->OpenOptionsPage(extension);
326 } 392 }
327 393
394 void RuntimeAPI::MaybeCancelRunningWatchdogTimer() {
395 if (!watchdog_timer_.IsRunning())
396 return;
397
398 if (!current_watchdog_request_callback_.is_null()) {
399 current_watchdog_request_callback_.Run(false,
400 kMessageRestartRequestCanceled);
401 current_watchdog_request_callback_.Reset();
402 }
403 }
404
405 void RuntimeAPI::ScheduleDelayedRestart(
406 const std::string& extension_id,
407 const base::Time& now,
408 int seconds_from_now,
409 const OnWatchdogTimeoutCallback& callback,
410 std::unique_ptr<base::Value> stored_last_restart) {
411 base::TimeDelta delay_till_restart =
412 base::TimeDelta::FromSeconds(seconds_from_now);
413
414 // Read the last restart time, and throttle restart requests that are
415 // received too soon successively.
416 double last_restart_time_double = 0.0;
417 if (stored_last_restart &&
418 stored_last_restart->GetAsDouble(&last_restart_time_double)) {
419 base::Time last_restart_time =
420 base::Time::FromDoubleT(last_restart_time_double);
421
422 base::Time future_restart_time = now + delay_till_restart;
423 base::TimeDelta future_time_since_last_restart =
424 future_restart_time > last_restart_time
425 ? future_restart_time - last_restart_time
426 : base::TimeDelta();
427 if (future_time_since_last_restart < minimum_duration_between_restarts_) {
428 // Schedule the restart after |minimum_duration_between_restarts_| has
429 // passed.
430 delay_till_restart =
431 minimum_duration_between_restarts_ - (now - last_restart_time);
432 }
433 }
434
435 MaybeCancelRunningWatchdogTimer();
436 current_watchdog_request_callback_ = callback;
437 watchdog_timer_.Start(
438 FROM_HERE, delay_till_restart,
439 base::Bind(&RuntimeAPI::OnRestartWatchdogTimeout,
440 weak_ptr_factory_.GetWeakPtr(), extension_id));
441 }
442
443 void RuntimeAPI::OnRestartWatchdogTimeout(const std::string& extension_id) {
444 // We can persist "now" as the last successful restart time, assuming that the
445 // following restart request will succeed, since it can only fail if requested
446 // by non kiosk apps, and we prevent that from the beginning (unless in
447 // browsertests).
448 // This assumption is important, since once restart is requested, we might not
449 // have enough time to persist the data to disk.
450 StateStore* storage = ExtensionSystem::Get(browser_context_)->state_store();
451 if (storage) {
452 base::Time now = base::Time::NowFromSystemTime();
453 std::unique_ptr<base::Value> restart_time(
454 new base::FundamentalValue(now.ToDoubleT()));
455 storage->SetExtensionValue(extension_id, kPrefRestartOnWatchdogTime,
456 std::move(restart_time));
457 }
458
459 std::string error_message;
460 const bool success = delegate_->RestartDevice(&error_message);
461
462 current_watchdog_request_callback_.Run(success, error_message);
463 current_watchdog_request_callback_.Reset();
464
465 if (!success && !allow_non_kiost_apps_restart_api_for_test) {
466 // This is breaking our above assumption and should never be reached.
467 NOTREACHED();
468 }
469 }
470
471 void RuntimeAPI::AllowNonKiostAppsInRestartOnWatchdogForTesting() {
472 allow_non_kiost_apps_restart_api_for_test = true;
473 }
474
328 /////////////////////////////////////////////////////////////////////////////// 475 ///////////////////////////////////////////////////////////////////////////////
329 476
330 // static 477 // static
331 void RuntimeEventRouter::DispatchOnStartupEvent( 478 void RuntimeEventRouter::DispatchOnStartupEvent(
332 content::BrowserContext* context, 479 content::BrowserContext* context,
333 const std::string& extension_id) { 480 const std::string& extension_id) {
334 DispatchOnStartupEventImpl(context, extension_id, true, NULL); 481 DispatchOnStartupEventImpl(context, extension_id, true, NULL);
335 } 482 }
336 483
337 // static 484 // static
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
544 std::string message; 691 std::string message;
545 bool result = 692 bool result =
546 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice( 693 RuntimeAPI::GetFactoryInstance()->Get(browser_context())->RestartDevice(
547 &message); 694 &message);
548 if (!result) { 695 if (!result) {
549 return RespondNow(Error(message)); 696 return RespondNow(Error(message));
550 } 697 }
551 return RespondNow(NoArguments()); 698 return RespondNow(NoArguments());
552 } 699 }
553 700
701 ExtensionFunction::ResponseAction RuntimeRestartOnWatchdogFunction::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::RestartOnWatchdog::Params> params(
708 api::runtime::RestartOnWatchdog::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(
719 extension()->id(), seconds,
720 base::Bind(&RuntimeRestartOnWatchdogFunction::OnWatchdogTimeout,
721 this));
722
723 switch (request_status) {
724 case RuntimeAPI::RestartOnWatchdogStatus::FAILED_NOT_FIRST_EXTENSION:
725 return RespondNow(Error(kErrorOnlyFirstExtensionAllowed));
726
727 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_CANCELLED:
728 return RespondNow(
729 ArgumentList(api::runtime::RestartOnWatchdog::Results::Create(
730 true, kMessageNoMoreRestartsScheduled)));
731
732 case RuntimeAPI::RestartOnWatchdogStatus::SUCCESS_RESTART_SCHEDULED:
733 return RespondLater();
734 }
735
736 NOTREACHED();
737 return RespondNow(Error(kErrorInvalidStatus));
738 }
739
740 void RuntimeRestartOnWatchdogFunction::OnWatchdogTimeout(
Devlin 2016/05/20 17:26:32 This strikes me as a little scary, since we'll alm
afakhry 2016/05/21 01:13:00 Could you please clarify how we leak it? I tried t
Devlin 2016/05/23 16:49:39 For leaking, I was expecting the restart function
afakhry 2016/05/25 01:55:46 Done. No more RespondLater(), and the response cal
741 bool success,
742 const std::string& message) {
743 if (!success)
744 WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR, message);
745
746 Respond(ArgumentList(
747 api::runtime::RestartOnWatchdog::Results::Create(success, message)));
748 }
749
554 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() { 750 ExtensionFunction::ResponseAction RuntimeGetPlatformInfoFunction::Run() {
555 runtime::PlatformInfo info; 751 runtime::PlatformInfo info;
556 if (!RuntimeAPI::GetFactoryInstance() 752 if (!RuntimeAPI::GetFactoryInstance()
557 ->Get(browser_context()) 753 ->Get(browser_context())
558 ->GetPlatformInfo(&info)) { 754 ->GetPlatformInfo(&info)) {
559 return RespondNow(Error(kPlatformInfoUnavailable)); 755 return RespondNow(Error(kPlatformInfoUnavailable));
560 } 756 }
561 return RespondNow( 757 return RespondNow(
562 ArgumentList(runtime::GetPlatformInfo::Results::Create(info))); 758 ArgumentList(runtime::GetPlatformInfo::Results::Create(info)));
563 } 759 }
(...skipping 13 matching lines...) Expand all
577 content::ChildProcessSecurityPolicy* policy = 773 content::ChildProcessSecurityPolicy* policy =
578 content::ChildProcessSecurityPolicy::GetInstance(); 774 content::ChildProcessSecurityPolicy::GetInstance();
579 policy->GrantReadFileSystem(renderer_id, filesystem_id); 775 policy->GrantReadFileSystem(renderer_id, filesystem_id);
580 base::DictionaryValue* dict = new base::DictionaryValue(); 776 base::DictionaryValue* dict = new base::DictionaryValue();
581 dict->SetString("fileSystemId", filesystem_id); 777 dict->SetString("fileSystemId", filesystem_id);
582 dict->SetString("baseName", relative_path); 778 dict->SetString("baseName", relative_path);
583 return RespondNow(OneArgument(dict)); 779 return RespondNow(OneArgument(dict));
584 } 780 }
585 781
586 } // namespace extensions 782 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698