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

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

Issue 1415863002: Delete all cached ephemeral apps at startup. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove dud file Created 5 years, 2 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 "apps/app_lifetime_monitor_factory.h"
8 #include "base/bind.h"
8 #include "base/command_line.h" 9 #include "base/command_line.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h" 10 #include "base/thread_task_runner_handle.h"
12 #include "chrome/browser/apps/ephemeral_app_service_factory.h" 11 #include "chrome/browser/apps/ephemeral_app_service_factory.h"
13 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/extension_util.h" 13 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/chrome_switches.h" 15 #include "content/public/browser/browser_thread.h"
17 #include "content/public/common/content_switches.h" 16 #include "content/public/common/content_switches.h"
18 #include "extensions/browser/extension_prefs.h" 17 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h" 18 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h" 19 #include "extensions/browser/extension_system.h"
21 #include "extensions/browser/extension_util.h" 20 #include "extensions/browser/extension_util.h"
22 #include "extensions/browser/uninstall_reason.h" 21 #include "extensions/browser/uninstall_reason.h"
23 #include "extensions/common/extension.h" 22 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_set.h" 23 #include "extensions/common/extension_set.h"
25 #include "extensions/common/one_shot_event.h" 24 #include "extensions/common/one_shot_event.h"
26 25
27 using extensions::Extension; 26 using extensions::Extension;
28 using extensions::ExtensionPrefs; 27 using extensions::ExtensionPrefs;
29 using extensions::ExtensionRegistry; 28 using extensions::ExtensionRegistry;
30 using extensions::ExtensionSet; 29 using extensions::ExtensionSet;
31 using extensions::ExtensionSystem; 30 using extensions::ExtensionSystem;
32 31
33 namespace { 32 namespace {
34 33
35 // The number of seconds after startup before performing garbage collection
36 // of ephemeral apps.
37 const int kGarbageCollectAppsStartupDelay = 60;
38
39 // The number of seconds after an ephemeral app has been installed before
40 // performing garbage collection.
41 const int kGarbageCollectAppsInstallDelay = 15;
42
43 // When the number of ephemeral apps reaches this count, trigger garbage
44 // collection to trim off the least-recently used apps in excess of
45 // kMaxEphemeralAppsCount.
46 const int kGarbageCollectAppsTriggerCount = 35;
47
48 // The number of seconds after an app has stopped running before it will be 34 // The number of seconds after an app has stopped running before it will be
49 // disabled. 35 // disabled.
50 const int kDefaultDisableAppDelay = 1; 36 const int kDefaultDisableAppDelay = 1;
51 37
52 // The number of seconds after startup before disabling inactive ephemeral apps.
53 const int kDisableAppsOnStartupDelay = 5;
54
55 } // namespace 38 } // namespace
56 39
57 const int EphemeralAppService::kAppInactiveThreshold = 10;
58 const int EphemeralAppService::kAppKeepThreshold = 1;
59 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
60
61 // static 40 // static
62 EphemeralAppService* EphemeralAppService::Get(Profile* profile) { 41 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
63 return EphemeralAppServiceFactory::GetForProfile(profile); 42 return EphemeralAppServiceFactory::GetForProfile(profile);
64 } 43 }
65 44
66 EphemeralAppService::EphemeralAppService(Profile* profile) 45 EphemeralAppService::EphemeralAppService(Profile* profile)
67 : profile_(profile), 46 : profile_(profile),
68 extension_registry_observer_(this), 47 extension_registry_observer_(this),
69 app_lifetime_monitor_observer_(this), 48 app_lifetime_monitor_observer_(this),
70 ephemeral_app_count_(-1),
71 disable_idle_app_delay_(kDefaultDisableAppDelay), 49 disable_idle_app_delay_(kDefaultDisableAppDelay),
72 weak_ptr_factory_(this) { 50 weak_ptr_factory_(this) {
73 ExtensionSystem::Get(profile_)->ready().Post( 51 ExtensionSystem::Get(profile_)->ready().Post(
74 FROM_HERE, 52 FROM_HERE,
75 base::Bind(&EphemeralAppService::Init, weak_ptr_factory_.GetWeakPtr())); 53 base::Bind(&EphemeralAppService::Init, weak_ptr_factory_.GetWeakPtr()));
76 } 54 }
77 55
78 EphemeralAppService::~EphemeralAppService() { 56 EphemeralAppService::~EphemeralAppService() {
79 } 57 }
80 58
81 void EphemeralAppService::ClearCachedApps() { 59 void EphemeralAppService::ClearCachedApps() {
82 // Cancel any pending garbage collects.
83 garbage_collect_apps_timer_.Stop();
84
85 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 60 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
86 DCHECK(registry); 61 DCHECK(registry);
87 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 62 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
88 DCHECK(prefs); 63 DCHECK(prefs);
89 ExtensionService* service = 64 ExtensionService* service =
90 ExtensionSystem::Get(profile_)->extension_service(); 65 ExtensionSystem::Get(profile_)->extension_service();
91 DCHECK(service); 66 DCHECK(service);
92 67
93 scoped_ptr<ExtensionSet> extensions = 68 scoped_ptr<ExtensionSet> extensions =
94 registry->GenerateInstalledExtensionsSet(); 69 registry->GenerateInstalledExtensionsSet();
95 70
96 for (ExtensionSet::const_iterator it = extensions->begin(); 71 for (ExtensionSet::const_iterator it = extensions->begin();
97 it != extensions->end(); 72 it != extensions->end();
98 ++it) { 73 ++it) {
99 std::string extension_id = (*it)->id(); 74 std::string extension_id = (*it)->id();
100 if (!prefs->IsEphemeralApp(extension_id)) 75 if (!prefs->IsEphemeralApp(extension_id))
101 continue; 76 continue;
102 77
103 // Do not remove apps that are running.
104 if (!extensions::util::IsExtensionIdle(extension_id, profile_))
105 continue;
106
107 DCHECK(registry->GetExtensionById(extension_id, 78 DCHECK(registry->GetExtensionById(extension_id,
108 ExtensionRegistry::EVERYTHING)); 79 ExtensionRegistry::EVERYTHING));
109 service->UninstallExtension( 80 service->UninstallExtension(
110 extension_id, 81 extension_id,
111 extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, 82 extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION,
112 base::Bind(&base::DoNothing), 83 base::Bind(&base::DoNothing),
113 NULL); 84 NULL);
114 } 85 }
115 } 86 }
116 87
117 void EphemeralAppService::OnExtensionWillBeInstalled( 88 void EphemeralAppService::OnExtensionWillBeInstalled(
118 content::BrowserContext* browser_context, 89 content::BrowserContext* browser_context,
119 const extensions::Extension* extension, 90 const extensions::Extension* extension,
120 bool is_update, 91 bool is_update,
121 bool from_ephemeral, 92 bool from_ephemeral,
122 const std::string& old_name) { 93 const std::string& old_name) {
123 if (from_ephemeral) { 94 if (from_ephemeral)
124 // An ephemeral app was just promoted to a regular installed app.
125 --ephemeral_app_count_;
126 DCHECK_GE(ephemeral_app_count_, 0);
127 HandleEphemeralAppPromoted(extension); 95 HandleEphemeralAppPromoted(extension);
128 } else if (!is_update &&
129 extensions::util::IsEphemeralApp(extension->id(), profile_)) {
130 // A new ephemeral app was launched.
131 ++ephemeral_app_count_;
132 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) {
133 TriggerGarbageCollect(
134 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay));
135 }
136 }
137 }
138
139 void EphemeralAppService::OnExtensionUninstalled(
140 content::BrowserContext* browser_context,
141 const extensions::Extension* extension,
142 extensions::UninstallReason reason) {
143 if (extensions::util::IsEphemeralApp(extension->id(), profile_)) {
144 --ephemeral_app_count_;
145 DCHECK_GE(ephemeral_app_count_, 0);
146 }
147 } 96 }
148 97
149 void EphemeralAppService::OnAppStop(Profile* profile, 98 void EphemeralAppService::OnAppStop(Profile* profile,
150 const std::string& app_id) { 99 const std::string& app_id) {
151 if (!extensions::util::IsEphemeralApp(app_id, profile_)) 100 if (!extensions::util::IsEphemeralApp(app_id, profile_))
152 return; 101 return;
153 102
154 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 103 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
155 FROM_HERE, base::Bind(&EphemeralAppService::DisableEphemeralApp, 104 FROM_HERE, base::Bind(&EphemeralAppService::DisableEphemeralApp,
156 weak_ptr_factory_.GetWeakPtr(), app_id), 105 weak_ptr_factory_.GetWeakPtr(), app_id),
157 base::TimeDelta::FromSeconds(disable_idle_app_delay_)); 106 base::TimeDelta::FromSeconds(disable_idle_app_delay_));
158 } 107 }
159 108
160 void EphemeralAppService::OnChromeTerminating() { 109 void EphemeralAppService::OnChromeTerminating() {
161 garbage_collect_apps_timer_.Stop();
162
163 extension_registry_observer_.RemoveAll(); 110 extension_registry_observer_.RemoveAll();
164 app_lifetime_monitor_observer_.RemoveAll(); 111 app_lifetime_monitor_observer_.RemoveAll();
165 } 112 }
166 113
167 void EphemeralAppService::Init() { 114 void EphemeralAppService::Init() {
168 InitEphemeralAppCount();
169
170 // Start observing. 115 // Start observing.
171 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 116 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
172 app_lifetime_monitor_observer_.Add( 117 app_lifetime_monitor_observer_.Add(
173 apps::AppLifetimeMonitorFactory::GetForProfile(profile_)); 118 apps::AppLifetimeMonitorFactory::GetForProfile(profile_));
174 119
175 // Execute startup clean up tasks (except during tests). 120 // Execute startup clean up tasks (except during tests).
176 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) 121 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
177 return; 122 return;
178 123
179 TriggerGarbageCollect( 124 content::BrowserThread::PostAfterStartupTask(
180 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay)); 125 FROM_HERE, content::BrowserThread::GetMessageLoopProxyForThread(
181 126 content::BrowserThread::UI),
182 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 127 base::Bind(&EphemeralAppService::ClearCachedApps,
183 FROM_HERE, base::Bind(&EphemeralAppService::DisableEphemeralAppsOnStartup, 128 weak_ptr_factory_.GetWeakPtr()));
184 weak_ptr_factory_.GetWeakPtr()),
185 base::TimeDelta::FromSeconds(kDisableAppsOnStartupDelay));
186 }
187
188 void EphemeralAppService::InitEphemeralAppCount() {
189 scoped_ptr<ExtensionSet> extensions =
190 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
191 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
192 DCHECK(prefs);
193
194 ephemeral_app_count_ = 0;
195 for (ExtensionSet::const_iterator it = extensions->begin();
196 it != extensions->end(); ++it) {
197 const Extension* extension = it->get();
198 if (prefs->IsEphemeralApp(extension->id()))
199 ++ephemeral_app_count_;
200 }
201 } 129 }
202 130
203 void EphemeralAppService::DisableEphemeralApp(const std::string& app_id) { 131 void EphemeralAppService::DisableEphemeralApp(const std::string& app_id) {
204 if (!extensions::util::IsEphemeralApp(app_id, profile_) || 132 if (!extensions::util::IsEphemeralApp(app_id, profile_) ||
205 !extensions::util::IsExtensionIdle(app_id, profile_)) { 133 !extensions::util::IsExtensionIdle(app_id, profile_)) {
206 return; 134 return;
207 } 135 }
208 136
209 // After an ephemeral app has stopped running, unload it from extension 137 // After an ephemeral app has stopped running, unload it from extension
210 // system and disable it to prevent all background activity. 138 // system and disable it to prevent all background activity.
211 ExtensionService* service = 139 ExtensionService* service =
212 ExtensionSystem::Get(profile_)->extension_service(); 140 ExtensionSystem::Get(profile_)->extension_service();
213 DCHECK(service); 141 DCHECK(service);
214 service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP); 142 service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
215 } 143 }
216 144
217 void EphemeralAppService::DisableEphemeralAppsOnStartup() {
218 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
219 DCHECK(prefs);
220 ExtensionService* service =
221 ExtensionSystem::Get(profile_)->extension_service();
222 DCHECK(service);
223
224 // Ensure that all inactive ephemeral apps are disabled to prevent all
225 // background activity. This is done on startup to catch any apps that escaped
226 // being disabled on shutdown.
227 scoped_ptr<ExtensionSet> extensions =
228 ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet();
229 for (ExtensionSet::const_iterator it = extensions->begin();
230 it != extensions->end();
231 ++it) {
232 const Extension* extension = it->get();
233 if (!prefs->IsEphemeralApp(extension->id()))
234 continue;
235
236 // Only V2 apps are installed ephemerally. Remove other ephemeral app types
237 // that were cached before this policy was introduced.
238 if (!extension->is_platform_app()) {
239 service->UninstallExtension(
240 extension->id(),
241 extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION,
242 base::Bind(&base::DoNothing),
243 NULL);
244 continue;
245 }
246
247 if (!prefs->HasDisableReason(extension->id(),
248 Extension::DISABLE_INACTIVE_EPHEMERAL_APP) &&
249 !prefs->IsExtensionRunning(extension->id()) &&
250 extensions::util::IsExtensionIdle(extension->id(), profile_)) {
251 service->DisableExtension(extension->id(),
252 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
253 }
254 }
255 }
256
257 void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) { 145 void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) {
258 // When ephemeral apps are promoted to regular install apps, remove the 146 // When ephemeral apps are promoted to regular install apps, remove the
259 // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no 147 // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no
260 // other reasons. 148 // other reasons.
261 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 149 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
262 DCHECK(prefs); 150 DCHECK(prefs);
263 151
264 int disable_reasons = prefs->GetDisableReasons(app->id()); 152 int disable_reasons = prefs->GetDisableReasons(app->id());
265 if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) { 153 if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) {
266 if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP) { 154 if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP) {
267 // This will also clear disable reasons. 155 // This will also clear disable reasons.
268 prefs->SetExtensionEnabled(app->id()); 156 prefs->SetExtensionEnabled(app->id());
269 } else { 157 } else {
270 prefs->RemoveDisableReason(app->id(), 158 prefs->RemoveDisableReason(app->id(),
271 Extension::DISABLE_INACTIVE_EPHEMERAL_APP); 159 Extension::DISABLE_INACTIVE_EPHEMERAL_APP);
272 } 160 }
273 } 161 }
274 } 162 }
275
276 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
277 if (!garbage_collect_apps_timer_.IsRunning()) {
278 garbage_collect_apps_timer_.Start(
279 FROM_HERE,
280 delay,
281 this,
282 &EphemeralAppService::GarbageCollectApps);
283 }
284 }
285
286 void EphemeralAppService::GarbageCollectApps() {
287 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
288 DCHECK(registry);
289 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
290 DCHECK(prefs);
291
292 scoped_ptr<ExtensionSet> extensions =
293 registry->GenerateInstalledExtensionsSet();
294
295 int app_count = 0;
296 LaunchTimeAppMap app_launch_times;
297 std::set<std::string> remove_app_ids;
298
299 // Populate a list of ephemeral apps, ordered by their last launch time.
300 for (ExtensionSet::const_iterator it = extensions->begin();
301 it != extensions->end(); ++it) {
302 const Extension* extension = it->get();
303 if (!prefs->IsEphemeralApp(extension->id()))
304 continue;
305
306 ++app_count;
307
308 // Do not remove ephemeral apps that are running.
309 if (!extensions::util::IsExtensionIdle(extension->id(), profile_))
310 continue;
311
312 base::Time last_launch_time = prefs->GetLastLaunchTime(extension->id());
313
314 // If the last launch time is invalid, this may be because it was just
315 // installed. So use the install time. If this is also null for some reason,
316 // the app will be removed.
317 if (last_launch_time.is_null())
318 last_launch_time = prefs->GetInstallTime(extension->id());
319
320 app_launch_times.insert(std::make_pair(last_launch_time, extension->id()));
321 }
322
323 ExtensionService* service =
324 ExtensionSystem::Get(profile_)->extension_service();
325 DCHECK(service);
326 // Execute the eviction policies and remove apps marked for deletion.
327 if (!app_launch_times.empty()) {
328 GetAppsToRemove(app_count, app_launch_times, &remove_app_ids);
329
330 for (std::set<std::string>::const_iterator id = remove_app_ids.begin();
331 id != remove_app_ids.end(); ++id) {
332 // Protect against cascading uninstalls.
333 if (!registry->GetExtensionById(*id, ExtensionRegistry::EVERYTHING))
334 continue;
335
336 service->UninstallExtension(
337 *id,
338 extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION,
339 base::Bind(&base::DoNothing),
340 NULL);
341 }
342 }
343 }
344
345 // static
346 void EphemeralAppService::GetAppsToRemove(
347 int app_count,
348 const LaunchTimeAppMap& app_launch_times,
349 std::set<std::string>* remove_app_ids) {
350 base::Time time_now = base::Time::Now();
351 const base::Time inactive_threshold =
352 time_now - base::TimeDelta::FromDays(kAppInactiveThreshold);
353 const base::Time keep_threshold =
354 time_now - base::TimeDelta::FromDays(kAppKeepThreshold);
355
356 // Visit the apps in order of least recently to most recently launched.
357 for (LaunchTimeAppMap::const_iterator it = app_launch_times.begin();
358 it != app_launch_times.end(); ++it) {
359 // Cannot remove apps that have been launched recently. So break when we
360 // reach the new apps.
361 if (it->first > keep_threshold)
362 break;
363
364 // Remove ephemeral apps that have been inactive for a while or if the cache
365 // is larger than the desired size.
366 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
367 remove_app_ids->insert(it->second);
368 --app_count;
369 } else {
370 break;
371 }
372 }
373 }
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