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

Side by Side Diff: chrome/browser/apps/ephemeral_app_service.cc

Issue 344543006: Disable ephemeral apps after they stop running (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Prevent demotion of installed app due to race condition Created 6 years, 5 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "chrome/browser/apps/ephemeral_app_service.h" 5 #include "chrome/browser/apps/ephemeral_app_service.h"
6 6
7 #include "apps/app_lifetime_monitor_factory.h"
7 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
8 #include "chrome/browser/apps/ephemeral_app_service_factory.h" 10 #include "chrome/browser/apps/ephemeral_app_service_factory.h"
9 #include "chrome/browser/chrome_notification_types.h" 11 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_util.h" 13 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/chrome_switches.h" 15 #include "chrome/common/chrome_switches.h"
14 #include "content/public/browser/notification_service.h" 16 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_source.h" 17 #include "content/public/browser/notification_source.h"
16 #include "content/public/browser/notification_types.h" 18 #include "content/public/browser/notification_types.h"
17 #include "extensions/browser/extension_prefs.h" 19 #include "extensions/browser/extension_prefs.h"
18 #include "extensions/browser/extension_registry.h" 20 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h" 21 #include "extensions/browser/extension_system.h"
20 #include "extensions/browser/extension_util.h" 22 #include "extensions/browser/extension_util.h"
21 #include "extensions/common/extension.h" 23 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_set.h" 24 #include "extensions/common/extension_set.h"
23 25
24 using extensions::Extension; 26 using extensions::Extension;
25 using extensions::ExtensionPrefs; 27 using extensions::ExtensionPrefs;
28 using extensions::ExtensionRegistry;
26 using extensions::ExtensionSet; 29 using extensions::ExtensionSet;
27 using extensions::ExtensionSystem; 30 using extensions::ExtensionSystem;
28 31
29 namespace { 32 namespace {
30 33
31 // The number of seconds after startup before performing garbage collection 34 // The number of seconds after startup before performing garbage collection
32 // of ephemeral apps. 35 // of ephemeral apps.
33 const int kGarbageCollectAppsStartupDelay = 60; 36 const int kGarbageCollectAppsStartupDelay = 60;
34 37
35 // The number of seconds after an ephemeral app has been installed before 38 // The number of seconds after an ephemeral app has been installed before
36 // performing garbage collection. 39 // performing garbage collection.
37 const int kGarbageCollectAppsInstallDelay = 15; 40 const int kGarbageCollectAppsInstallDelay = 15;
38 41
39 // When the number of ephemeral apps reaches this count, trigger garbage 42 // When the number of ephemeral apps reaches this count, trigger garbage
40 // collection to trim off the least-recently used apps in excess of 43 // collection to trim off the least-recently used apps in excess of
41 // kMaxEphemeralAppsCount. 44 // kMaxEphemeralAppsCount.
42 const int kGarbageCollectAppsTriggerCount = 35; 45 const int kGarbageCollectAppsTriggerCount = 35;
43 46
47 // The number of seconds after an app has stopped running before it will be
48 // unloaded and disabled.
49 const int kDefaultUnloadAppDelay = 1;
50
51 // The number of seconds after startup before unloading inactive ephemeral apps.
52 const int kUnloadAppsOnStartupDelay = 5;
53
44 } // namespace 54 } // namespace
45 55
46 const int EphemeralAppService::kAppInactiveThreshold = 10; 56 const int EphemeralAppService::kAppInactiveThreshold = 10;
47 const int EphemeralAppService::kAppKeepThreshold = 1; 57 const int EphemeralAppService::kAppKeepThreshold = 1;
48 const int EphemeralAppService::kMaxEphemeralAppsCount = 30; 58 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
49 59
50 // static 60 // static
51 EphemeralAppService* EphemeralAppService::Get(Profile* profile) { 61 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
52 return EphemeralAppServiceFactory::GetForProfile(profile); 62 return EphemeralAppServiceFactory::GetForProfile(profile);
53 } 63 }
54 64
55 EphemeralAppService::EphemeralAppService(Profile* profile) 65 EphemeralAppService::EphemeralAppService(Profile* profile)
56 : profile_(profile), 66 : profile_(profile),
57 extension_registry_observer_(this), 67 extension_registry_observer_(this),
58 ephemeral_app_count_(-1) { 68 ephemeral_app_count_(-1),
69 unload_idle_app_delay_(kDefaultUnloadAppDelay),
70 weak_ptr_factory_(this) {
59 if (!CommandLine::ForCurrentProcess()->HasSwitch( 71 if (!CommandLine::ForCurrentProcess()->HasSwitch(
60 switches::kEnableEphemeralApps)) 72 switches::kEnableEphemeralApps))
61 return; 73 return;
62 74
63 extension_registry_observer_.Add( 75 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
64 extensions::ExtensionRegistry::Get(profile_));
65 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY, 76 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
66 content::Source<Profile>(profile_)); 77 content::Source<Profile>(profile_));
67 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
68 content::Source<Profile>(profile_));
69 } 78 }
70 79
71 EphemeralAppService::~EphemeralAppService() { 80 EphemeralAppService::~EphemeralAppService() {
72 } 81 }
73 82
74 void EphemeralAppService::Observe( 83 void EphemeralAppService::Observe(
75 int type, 84 int type,
76 const content::NotificationSource& source, 85 const content::NotificationSource& source,
77 const content::NotificationDetails& details) { 86 const content::NotificationDetails& details) {
78 switch (type) { 87 switch (type) {
79 case chrome::NOTIFICATION_EXTENSIONS_READY: { 88 case chrome::NOTIFICATION_EXTENSIONS_READY: {
80 Init(); 89 Init();
81 break; 90 break;
82 } 91 }
83 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
84 // Ideally we need to know when the extension system is shutting down.
85 garbage_collect_apps_timer_.Stop();
86 break;
87 }
88 default: 92 default:
89 NOTREACHED(); 93 NOTREACHED();
90 } 94 }
91 } 95 }
92 96
93 void EphemeralAppService::OnExtensionWillBeInstalled( 97 void EphemeralAppService::OnExtensionWillBeInstalled(
94 content::BrowserContext* browser_context, 98 content::BrowserContext* browser_context,
95 const extensions::Extension* extension, 99 const extensions::Extension* extension,
96 bool is_update, 100 bool is_update,
97 bool from_ephemeral, 101 bool from_ephemeral,
98 const std::string& old_name) { 102 const std::string& old_name) {
99 if (from_ephemeral) { 103 if (from_ephemeral) {
100 // An ephemeral app was just promoted to a regular installed app. 104 // An ephemeral app was just promoted to a regular installed app.
101 --ephemeral_app_count_; 105 --ephemeral_app_count_;
102 DCHECK_GE(ephemeral_app_count_, 0); 106 DCHECK_GE(ephemeral_app_count_, 0);
107 HandleEphemeralAppPromoted(extension);
103 } else if (!is_update && 108 } else if (!is_update &&
104 extensions::util::IsEphemeralApp(extension->id(), profile_)) { 109 extensions::util::IsEphemeralApp(extension->id(), profile_)) {
110 // A new ephemeral app was launched.
105 ++ephemeral_app_count_; 111 ++ephemeral_app_count_;
106 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) { 112 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) {
107 TriggerGarbageCollect( 113 TriggerGarbageCollect(
108 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay)); 114 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay));
109 } 115 }
110 } 116 }
111 } 117 }
112 118
113 void EphemeralAppService::OnExtensionUninstalled( 119 void EphemeralAppService::OnExtensionUninstalled(
114 content::BrowserContext* browser_context, 120 content::BrowserContext* browser_context,
115 const extensions::Extension* extension) { 121 const extensions::Extension* extension) {
116 if (extensions::util::IsEphemeralApp(extension->id(), profile_)) { 122 if (extensions::util::IsEphemeralApp(extension->id(), profile_)) {
117 --ephemeral_app_count_; 123 --ephemeral_app_count_;
118 DCHECK_GE(ephemeral_app_count_, 0); 124 DCHECK_GE(ephemeral_app_count_, 0);
119 } 125 }
120 } 126 }
121 127
128 void EphemeralAppService::OnAppStop(Profile* profile,
129 const std::string& app_id) {
130 if (!extensions::util::IsEphemeralApp(app_id, profile_))
131 return;
132
133 base::MessageLoop::current()->PostDelayedTask(
134 FROM_HERE,
135 base::Bind(&EphemeralAppService::UnloadEphemeralApp,
136 weak_ptr_factory_.GetWeakPtr(),
137 app_id),
138 base::TimeDelta::FromSeconds(unload_idle_app_delay_));
139 }
140
141 void EphemeralAppService::OnChromeTerminating() {
142 garbage_collect_apps_timer_.Stop();
143
144 extension_registry_observer_.RemoveAll();
145
146 apps::AppLifetimeMonitor* lifetime_monitor =
147 apps::AppLifetimeMonitorFactory::GetForProfile(profile_);
148 DCHECK(lifetime_monitor);
149 lifetime_monitor->RemoveObserver(this);
150 }
151
122 void EphemeralAppService::Init() { 152 void EphemeralAppService::Init() {
123 InitEphemeralAppCount(); 153 InitEphemeralAppCount();
154
155 apps::AppLifetimeMonitor* lifetime_monitor =
156 apps::AppLifetimeMonitorFactory::GetForProfile(profile_);
157 DCHECK(lifetime_monitor);
158 lifetime_monitor->AddObserver(this);
159
124 TriggerGarbageCollect( 160 TriggerGarbageCollect(
125 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay)); 161 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay));
162
163 base::MessageLoop::current()->PostDelayedTask(
164 FROM_HERE,
165 base::Bind(&EphemeralAppService::UnloadEphemeralAppsOnStartup,
166 weak_ptr_factory_.GetWeakPtr()),
167 base::TimeDelta::FromSeconds(kUnloadAppsOnStartupDelay));
126 } 168 }
127 169
128 void EphemeralAppService::InitEphemeralAppCount() { 170 void EphemeralAppService::InitEphemeralAppCount() {
129 scoped_ptr<ExtensionSet> extensions = 171 scoped_ptr<ExtensionSet> extensions =
130 extensions::ExtensionRegistry::Get(profile_) 172 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
131 ->GenerateInstalledExtensionsSet();
132 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 173 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
133 DCHECK(prefs); 174 DCHECK(prefs);
134 175
135 ephemeral_app_count_ = 0; 176 ephemeral_app_count_ = 0;
136 for (ExtensionSet::const_iterator it = extensions->begin(); 177 for (ExtensionSet::const_iterator it = extensions->begin();
137 it != extensions->end(); ++it) { 178 it != extensions->end(); ++it) {
138 const Extension* extension = *it; 179 const Extension* extension = *it;
139 if (prefs->IsEphemeralApp(extension->id())) 180 if (prefs->IsEphemeralApp(extension->id()))
140 ++ephemeral_app_count_; 181 ++ephemeral_app_count_;
141 } 182 }
142 } 183 }
143 184
185 void EphemeralAppService::UnloadEphemeralApp(const std::string& app_id) {
186 if (!extensions::util::IsEphemeralApp(app_id, profile_) ||
187 !extensions::util::IsExtensionIdle(app_id, profile_)) {
188 return;
189 }
190
191 // After an ephemeral app has stopped running, unload it from extension
192 // system to prevent all background activity.
193 ExtensionService* service =
194 ExtensionSystem::Get(profile_)->extension_service();
195 DCHECK(service);
196 service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
197 }
198
199 void EphemeralAppService::UnloadEphemeralAppsOnStartup() {
200 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
201 DCHECK(prefs);
202 ExtensionService* service =
203 ExtensionSystem::Get(profile_)->extension_service();
204 DCHECK(service);
205
206 // Ensure that all inactive ephemeral apps are unloaded from extension system
207 // to prevent all background activity. This is done on startup to catch any
208 // apps that escaped unloading on shutdown.
209 scoped_ptr<ExtensionSet> extensions =
210 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
211 for (ExtensionSet::const_iterator it = extensions->begin();
212 it != extensions->end();
213 ++it) {
214 const Extension* extension = *it;
215 if (prefs->IsEphemeralApp(extension->id()) &&
216 !prefs->IsExtensionDisabled(extension->id()) &&
217 !prefs->IsExtensionRunning(extension->id()) &&
218 extensions::util::IsExtensionIdle(extension->id(), profile_)) {
219 service->DisableExtension(extension->id(),
220 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
221 }
222 }
223 }
224
225 void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) {
226 // When ephemeral apps are promoted to regular install apps, remove the
227 // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no
228 // other reasons.
229 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
230 DCHECK(prefs);
231
232 int disable_reasons = prefs->GetDisableReasons(app->id());
233 if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) {
234 prefs->RemoveDisableReason(app->id(),
235 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
236 if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP)
237 prefs->SetExtensionState(app->id(), Extension::ENABLED);
238 }
239 }
240
144 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) { 241 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
145 if (!garbage_collect_apps_timer_.IsRunning()) { 242 if (!garbage_collect_apps_timer_.IsRunning()) {
146 garbage_collect_apps_timer_.Start( 243 garbage_collect_apps_timer_.Start(
147 FROM_HERE, 244 FROM_HERE,
148 delay, 245 delay,
149 this, 246 this,
150 &EphemeralAppService::GarbageCollectApps); 247 &EphemeralAppService::GarbageCollectApps);
151 } 248 }
152 } 249 }
153 250
154 void EphemeralAppService::GarbageCollectApps() { 251 void EphemeralAppService::GarbageCollectApps() {
155 scoped_ptr<ExtensionSet> extensions = 252 scoped_ptr<ExtensionSet> extensions =
156 extensions::ExtensionRegistry::Get(profile_) 253 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
157 ->GenerateInstalledExtensionsSet();
158 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 254 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
159 DCHECK(prefs); 255 DCHECK(prefs);
160 256
161 int app_count = 0; 257 int app_count = 0;
162 LaunchTimeAppMap app_launch_times; 258 LaunchTimeAppMap app_launch_times;
163 std::set<std::string> remove_app_ids; 259 std::set<std::string> remove_app_ids;
164 260
165 // Populate a list of ephemeral apps, ordered by their last launch time. 261 // Populate a list of ephemeral apps, ordered by their last launch time.
166 for (ExtensionSet::const_iterator it = extensions->begin(); 262 for (ExtensionSet::const_iterator it = extensions->begin();
167 it != extensions->end(); ++it) { 263 it != extensions->end(); ++it) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 // Remove ephemeral apps that have been inactive for a while or if the cache 320 // Remove ephemeral apps that have been inactive for a while or if the cache
225 // is larger than the desired size. 321 // is larger than the desired size.
226 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) { 322 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
227 remove_app_ids->insert(it->second); 323 remove_app_ids->insert(it->second);
228 --app_count; 324 --app_count;
229 } else { 325 } else {
230 break; 326 break;
231 } 327 }
232 } 328 }
233 } 329 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698