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

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: Addressed review comments and refactoring Created 6 years, 4 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"
(...skipping 19 matching lines...) Expand all
37 39
38 // The number of seconds after an ephemeral app has been installed before 40 // The number of seconds after an ephemeral app has been installed before
39 // performing garbage collection. 41 // performing garbage collection.
40 const int kGarbageCollectAppsInstallDelay = 15; 42 const int kGarbageCollectAppsInstallDelay = 15;
41 43
42 // When the number of ephemeral apps reaches this count, trigger garbage 44 // When the number of ephemeral apps reaches this count, trigger garbage
43 // collection to trim off the least-recently used apps in excess of 45 // collection to trim off the least-recently used apps in excess of
44 // kMaxEphemeralAppsCount. 46 // kMaxEphemeralAppsCount.
45 const int kGarbageCollectAppsTriggerCount = 35; 47 const int kGarbageCollectAppsTriggerCount = 35;
46 48
49 // The number of seconds after an app has stopped running before it will be
50 // disabled.
51 const int kDefaultDisableAppDelay = 1;
52
53 // The number of seconds after startup before disabling inactive ephemeral apps.
54 const int kDisableAppsOnStartupDelay = 5;
55
47 } // namespace 56 } // namespace
48 57
49 const int EphemeralAppService::kAppInactiveThreshold = 10; 58 const int EphemeralAppService::kAppInactiveThreshold = 10;
50 const int EphemeralAppService::kAppKeepThreshold = 1; 59 const int EphemeralAppService::kAppKeepThreshold = 1;
51 const int EphemeralAppService::kMaxEphemeralAppsCount = 30; 60 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
52 61
53 // static 62 // static
54 EphemeralAppService* EphemeralAppService::Get(Profile* profile) { 63 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
55 return EphemeralAppServiceFactory::GetForProfile(profile); 64 return EphemeralAppServiceFactory::GetForProfile(profile);
56 } 65 }
57 66
58 EphemeralAppService::EphemeralAppService(Profile* profile) 67 EphemeralAppService::EphemeralAppService(Profile* profile)
59 : profile_(profile), 68 : profile_(profile),
60 extension_registry_observer_(this), 69 extension_registry_observer_(this),
61 ephemeral_app_count_(-1) { 70 app_lifetime_monitor_observer_(this),
62 if (!CommandLine::ForCurrentProcess()->HasSwitch( 71 ephemeral_app_count_(-1),
63 switches::kEnableEphemeralApps)) 72 disable_idle_app_delay_(kDefaultDisableAppDelay),
64 return; 73 weak_ptr_factory_(this) {
65
66 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
67 registrar_.Add(this, 74 registrar_.Add(this,
68 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 75 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
69 content::Source<Profile>(profile_)); 76 content::Source<Profile>(profile_));
70 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
71 content::Source<Profile>(profile_));
72 } 77 }
73 78
74 EphemeralAppService::~EphemeralAppService() { 79 EphemeralAppService::~EphemeralAppService() {
75 } 80 }
76 81
77 void EphemeralAppService::ClearCachedApps() { 82 void EphemeralAppService::ClearCachedApps() {
78 // Cancel any pending garbage collects. 83 // Cancel any pending garbage collects.
79 garbage_collect_apps_timer_.Stop(); 84 garbage_collect_apps_timer_.Stop();
80 85
81 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 86 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
(...skipping 30 matching lines...) Expand all
112 117
113 void EphemeralAppService::Observe( 118 void EphemeralAppService::Observe(
114 int type, 119 int type,
115 const content::NotificationSource& source, 120 const content::NotificationSource& source,
116 const content::NotificationDetails& details) { 121 const content::NotificationDetails& details) {
117 switch (type) { 122 switch (type) {
118 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: { 123 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: {
119 Init(); 124 Init();
120 break; 125 break;
121 } 126 }
122 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
123 // Ideally we need to know when the extension system is shutting down.
124 garbage_collect_apps_timer_.Stop();
125 break;
126 }
127 default: 127 default:
128 NOTREACHED(); 128 NOTREACHED();
129 } 129 }
130 } 130 }
131 131
132 void EphemeralAppService::OnExtensionWillBeInstalled( 132 void EphemeralAppService::OnExtensionWillBeInstalled(
133 content::BrowserContext* browser_context, 133 content::BrowserContext* browser_context,
134 const extensions::Extension* extension, 134 const extensions::Extension* extension,
135 bool is_update, 135 bool is_update,
136 bool from_ephemeral, 136 bool from_ephemeral,
137 const std::string& old_name) { 137 const std::string& old_name) {
138 if (from_ephemeral) { 138 if (from_ephemeral) {
139 // An ephemeral app was just promoted to a regular installed app. 139 // An ephemeral app was just promoted to a regular installed app.
140 --ephemeral_app_count_; 140 --ephemeral_app_count_;
141 DCHECK_GE(ephemeral_app_count_, 0); 141 DCHECK_GE(ephemeral_app_count_, 0);
142 HandleEphemeralAppPromoted(extension);
142 } else if (!is_update && 143 } else if (!is_update &&
143 extensions::util::IsEphemeralApp(extension->id(), profile_)) { 144 extensions::util::IsEphemeralApp(extension->id(), profile_)) {
145 // A new ephemeral app was launched.
144 ++ephemeral_app_count_; 146 ++ephemeral_app_count_;
145 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) { 147 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) {
146 TriggerGarbageCollect( 148 TriggerGarbageCollect(
147 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay)); 149 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay));
148 } 150 }
149 } 151 }
150 } 152 }
151 153
152 void EphemeralAppService::OnExtensionUninstalled( 154 void EphemeralAppService::OnExtensionUninstalled(
153 content::BrowserContext* browser_context, 155 content::BrowserContext* browser_context,
154 const extensions::Extension* extension, 156 const extensions::Extension* extension,
155 extensions::UninstallReason reason) { 157 extensions::UninstallReason reason) {
156 if (extensions::util::IsEphemeralApp(extension->id(), profile_)) { 158 if (extensions::util::IsEphemeralApp(extension->id(), profile_)) {
157 --ephemeral_app_count_; 159 --ephemeral_app_count_;
158 DCHECK_GE(ephemeral_app_count_, 0); 160 DCHECK_GE(ephemeral_app_count_, 0);
159 } 161 }
160 } 162 }
161 163
164 void EphemeralAppService::OnAppStop(Profile* profile,
165 const std::string& app_id) {
166 if (!extensions::util::IsEphemeralApp(app_id, profile_))
167 return;
168
169 base::MessageLoop::current()->PostDelayedTask(
170 FROM_HERE,
171 base::Bind(&EphemeralAppService::DisableEphemeralApp,
172 weak_ptr_factory_.GetWeakPtr(),
173 app_id),
174 base::TimeDelta::FromSeconds(disable_idle_app_delay_));
175 }
176
177 void EphemeralAppService::OnChromeTerminating() {
178 garbage_collect_apps_timer_.Stop();
179
180 extension_registry_observer_.RemoveAll();
181 app_lifetime_monitor_observer_.RemoveAll();
182 }
183
162 void EphemeralAppService::Init() { 184 void EphemeralAppService::Init() {
163 InitEphemeralAppCount(); 185 InitEphemeralAppCount();
186
187 // Start observing.
188 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
189 app_lifetime_monitor_observer_.Add(
190 apps::AppLifetimeMonitorFactory::GetForProfile(profile_));
191
192 // Execute startup clean up tasks (except during tests).
193 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
194 return;
195
164 TriggerGarbageCollect( 196 TriggerGarbageCollect(
165 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay)); 197 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay));
198
199 base::MessageLoop::current()->PostDelayedTask(
200 FROM_HERE,
201 base::Bind(&EphemeralAppService::DisableEphemeralAppsOnStartup,
202 weak_ptr_factory_.GetWeakPtr()),
203 base::TimeDelta::FromSeconds(kDisableAppsOnStartupDelay));
166 } 204 }
167 205
168 void EphemeralAppService::InitEphemeralAppCount() { 206 void EphemeralAppService::InitEphemeralAppCount() {
169 scoped_ptr<ExtensionSet> extensions = 207 scoped_ptr<ExtensionSet> extensions =
170 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet(); 208 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
171 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 209 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
172 DCHECK(prefs); 210 DCHECK(prefs);
173 211
174 ephemeral_app_count_ = 0; 212 ephemeral_app_count_ = 0;
175 for (ExtensionSet::const_iterator it = extensions->begin(); 213 for (ExtensionSet::const_iterator it = extensions->begin();
176 it != extensions->end(); ++it) { 214 it != extensions->end(); ++it) {
177 const Extension* extension = *it; 215 const Extension* extension = *it;
178 if (prefs->IsEphemeralApp(extension->id())) 216 if (prefs->IsEphemeralApp(extension->id()))
179 ++ephemeral_app_count_; 217 ++ephemeral_app_count_;
180 } 218 }
181 } 219 }
182 220
221 void EphemeralAppService::DisableEphemeralApp(const std::string& app_id) {
222 if (!extensions::util::IsEphemeralApp(app_id, profile_) ||
223 !extensions::util::IsExtensionIdle(app_id, profile_)) {
224 return;
225 }
226
227 // After an ephemeral app has stopped running, unload it from extension
228 // system and disable it to prevent all background activity.
229 ExtensionService* service =
230 ExtensionSystem::Get(profile_)->extension_service();
231 DCHECK(service);
232 service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
233 }
234
235 void EphemeralAppService::DisableEphemeralAppsOnStartup() {
236 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
237 DCHECK(prefs);
238 ExtensionService* service =
239 ExtensionSystem::Get(profile_)->extension_service();
240 DCHECK(service);
241
242 // Ensure that all inactive ephemeral apps are disabled to prevent all
243 // background activity. This is done on startup to catch any apps that escaped
244 // being disabled on shutdown.
245 scoped_ptr<ExtensionSet> extensions =
246 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
247 for (ExtensionSet::const_iterator it = extensions->begin();
248 it != extensions->end();
249 ++it) {
250 const Extension* extension = *it;
251 if (!prefs->IsEphemeralApp(extension->id()))
252 continue;
253
254 // Only V2 apps are installed ephemerally. Remove other ephemeral app types
255 // that were cached before this policy was introduced.
256 if (!extension->is_platform_app()) {
257 service->UninstallExtension(
258 extension->id(),
259 extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION,
260 base::Bind(&base::DoNothing),
261 NULL);
262 continue;
263 }
264
265 if (!prefs->HasDisableReason(extension->id(),
266 Extension::DISABLE_INACTIVE_EPHEMERAL_APP) &&
267 !prefs->IsExtensionRunning(extension->id()) &&
268 extensions::util::IsExtensionIdle(extension->id(), profile_)) {
269 service->DisableExtension(extension->id(),
270 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
271 }
272 }
273 }
274
275 void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) {
276 // When ephemeral apps are promoted to regular install apps, remove the
277 // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no
278 // other reasons.
279 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
280 DCHECK(prefs);
281
282 int disable_reasons = prefs->GetDisableReasons(app->id());
283 if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) {
284 prefs->RemoveDisableReason(app->id(),
285 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
286 if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP)
287 prefs->SetExtensionState(app->id(), Extension::ENABLED);
288 }
289 }
290
183 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) { 291 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
184 if (!garbage_collect_apps_timer_.IsRunning()) { 292 if (!garbage_collect_apps_timer_.IsRunning()) {
185 garbage_collect_apps_timer_.Start( 293 garbage_collect_apps_timer_.Start(
186 FROM_HERE, 294 FROM_HERE,
187 delay, 295 delay,
188 this, 296 this,
189 &EphemeralAppService::GarbageCollectApps); 297 &EphemeralAppService::GarbageCollectApps);
190 } 298 }
191 } 299 }
192 300
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 // Remove ephemeral apps that have been inactive for a while or if the cache 379 // Remove ephemeral apps that have been inactive for a while or if the cache
272 // is larger than the desired size. 380 // is larger than the desired size.
273 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) { 381 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
274 remove_app_ids->insert(it->second); 382 remove_app_ids->insert(it->second);
275 --app_count; 383 --app_count;
276 } else { 384 } else {
277 break; 385 break;
278 } 386 }
279 } 387 }
280 } 388 }
OLDNEW
« no previous file with comments | « chrome/browser/apps/ephemeral_app_service.h ('k') | chrome/browser/apps/ephemeral_app_service_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698