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

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