Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / browser / apps / ephemeral_app_service.cc
blob0832e1408eac55aa97ff88b51f3b6518c17608e1
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
3 // found in the LICENSE file.
5 #include "chrome/browser/apps/ephemeral_app_service.h"
7 #include "base/command_line.h"
8 #include "chrome/browser/apps/ephemeral_app_service_factory.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/data_deleter.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_source.h"
17 #include "content/public/browser/notification_types.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_set.h"
24 using extensions::Extension;
25 using extensions::ExtensionInfo;
26 using extensions::ExtensionPrefs;
27 using extensions::ExtensionSet;
28 using extensions::ExtensionSystem;
29 using extensions::InstalledExtensionInfo;
31 namespace {
33 // The number of seconds after startup before performing garbage collection
34 // of ephemeral apps.
35 const int kGarbageCollectAppsStartupDelay = 60;
37 // The number of seconds after an ephemeral app has been installed before
38 // performing garbage collection.
39 const int kGarbageCollectAppsInstallDelay = 15;
41 // When the number of ephemeral apps reaches this count, trigger garbage
42 // collection to trim off the least-recently used apps in excess of
43 // kMaxEphemeralAppsCount.
44 const int kGarbageCollectAppsTriggerCount = 35;
46 // The number of seconds after startup before performing garbage collection
47 // of the data of evicted ephemeral apps.
48 const int kGarbageCollectDataStartupDelay = 120;
50 } // namespace
52 const int EphemeralAppService::kAppInactiveThreshold = 10;
53 const int EphemeralAppService::kAppKeepThreshold = 1;
54 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
55 const int EphemeralAppService::kDataInactiveThreshold = 90;
57 // static
58 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
59 return EphemeralAppServiceFactory::GetForProfile(profile);
62 EphemeralAppService::EphemeralAppService(Profile* profile)
63 : profile_(profile),
64 ephemeral_app_count_(-1) {
65 if (!CommandLine::ForCurrentProcess()->HasSwitch(
66 switches::kEnableEphemeralApps))
67 return;
69 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
70 content::Source<Profile>(profile_));
71 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
72 content::Source<Profile>(profile_));
73 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
74 content::Source<Profile>(profile_));
75 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
76 content::Source<Profile>(profile_));
79 EphemeralAppService::~EphemeralAppService() {
82 void EphemeralAppService::Observe(
83 int type,
84 const content::NotificationSource& source,
85 const content::NotificationDetails& details) {
86 switch (type) {
87 case chrome::NOTIFICATION_EXTENSIONS_READY: {
88 Init();
89 break;
91 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
92 const Extension* extension =
93 content::Details<const InstalledExtensionInfo>(details)->extension;
94 DCHECK(extension);
95 if (extension->is_ephemeral()) {
96 ++ephemeral_app_count_;
97 if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount)
98 TriggerGarbageCollect(
99 base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay));
101 break;
103 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
104 const Extension* extension =
105 content::Details<const Extension>(details).ptr();
106 DCHECK(extension);
107 if (extension->is_ephemeral())
108 --ephemeral_app_count_;
109 break;
111 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
112 // Ideally we need to know when the extension system is shutting down.
113 garbage_collect_apps_timer_.Stop();
114 break;
116 default:
117 NOTREACHED();
121 void EphemeralAppService::Init() {
122 InitEphemeralAppCount();
123 TriggerGarbageCollect(
124 base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay));
126 garbage_collect_data_timer_.Start(
127 FROM_HERE,
128 base::TimeDelta::FromSeconds(kGarbageCollectDataStartupDelay),
129 this,
130 &EphemeralAppService::GarbageCollectData);
133 void EphemeralAppService::InitEphemeralAppCount() {
134 scoped_ptr<ExtensionSet> extensions =
135 extensions::ExtensionRegistry::Get(profile_)
136 ->GenerateInstalledExtensionsSet();
138 ephemeral_app_count_ = 0;
139 for (ExtensionSet::const_iterator it = extensions->begin();
140 it != extensions->end(); ++it) {
141 const Extension* extension = *it;
142 if (extension->is_ephemeral())
143 ++ephemeral_app_count_;
147 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
148 if (!garbage_collect_apps_timer_.IsRunning()) {
149 garbage_collect_apps_timer_.Start(
150 FROM_HERE,
151 delay,
152 this,
153 &EphemeralAppService::GarbageCollectApps);
157 void EphemeralAppService::GarbageCollectApps() {
158 scoped_ptr<ExtensionSet> extensions =
159 extensions::ExtensionRegistry::Get(profile_)
160 ->GenerateInstalledExtensionsSet();
161 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
163 int app_count = 0;
164 LaunchTimeAppMap app_launch_times;
165 std::set<std::string> remove_app_ids;
167 // Populate a list of ephemeral apps, ordered by their last launch time.
168 for (ExtensionSet::const_iterator it = extensions->begin();
169 it != extensions->end(); ++it) {
170 const Extension* extension = *it;
171 if (!extension->is_ephemeral())
172 continue;
174 ++app_count;
176 // Do not remove ephemeral apps that are running.
177 if (!extensions::util::IsExtensionIdle(extension->id(), profile_))
178 continue;
180 base::Time last_launch_time = prefs->GetLastLaunchTime(extension->id());
182 // If the last launch time is invalid, this may be because it was just
183 // installed. So use the install time. If this is also null for some reason,
184 // the app will be removed.
185 if (last_launch_time.is_null())
186 last_launch_time = prefs->GetInstallTime(extension->id());
188 app_launch_times.insert(std::make_pair(last_launch_time, extension->id()));
191 ExtensionService* service =
192 ExtensionSystem::Get(profile_)->extension_service();
193 DCHECK(service);
194 // Execute the replacement policies and remove apps marked for deletion.
195 if (!app_launch_times.empty()) {
196 GetAppsToRemove(app_count, app_launch_times, &remove_app_ids);
197 for (std::set<std::string>::const_iterator id = remove_app_ids.begin();
198 id != remove_app_ids.end(); ++id) {
199 if (service->UninstallExtension(*id, false, NULL))
200 --app_count;
204 ephemeral_app_count_ = app_count;
207 // static
208 void EphemeralAppService::GetAppsToRemove(
209 int app_count,
210 const LaunchTimeAppMap& app_launch_times,
211 std::set<std::string>* remove_app_ids) {
212 base::Time time_now = base::Time::Now();
213 const base::Time inactive_threshold =
214 time_now - base::TimeDelta::FromDays(kAppInactiveThreshold);
215 const base::Time keep_threshold =
216 time_now - base::TimeDelta::FromDays(kAppKeepThreshold);
218 // Visit the apps in order of least recently to most recently launched.
219 for (LaunchTimeAppMap::const_iterator it = app_launch_times.begin();
220 it != app_launch_times.end(); ++it) {
221 // Cannot remove apps that have been launched recently. So break when we
222 // reach the new apps.
223 if (it->first > keep_threshold)
224 break;
226 // Remove ephemeral apps that have been inactive for a while or if the cache
227 // is larger than the desired size.
228 if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
229 remove_app_ids->insert(it->second);
230 --app_count;
231 } else {
232 break;
237 void EphemeralAppService::GarbageCollectData() {
238 ExtensionService* service =
239 ExtensionSystem::Get(profile_)->extension_service();
240 DCHECK(service);
241 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_);
242 DCHECK(prefs);
243 scoped_ptr<ExtensionPrefs::ExtensionsInfo> evicted_apps_info(
244 prefs->GetEvictedEphemeralAppsInfo());
246 base::Time time_now = base::Time::Now();
247 const base::Time inactive_threshold =
248 time_now - base::TimeDelta::FromDays(kDataInactiveThreshold);
250 for (size_t i = 0; i < evicted_apps_info->size(); ++i) {
251 ExtensionInfo* info = evicted_apps_info->at(i).get();
252 base::Time last_launch_time = prefs->GetLastLaunchTime(info->extension_id);
253 if (last_launch_time > inactive_threshold)
254 continue;
256 // Sanity check to ensure the app is not currently installed.
257 if (service->GetInstalledExtension(info->extension_id)) {
258 NOTREACHED();
259 continue;
262 // Ensure the app is not waiting to be installed.
263 scoped_ptr<ExtensionInfo> delayed_install(
264 prefs->GetDelayedInstallInfo(info->extension_id));
265 if (delayed_install.get())
266 continue;
268 if (info->extension_manifest.get()) {
269 std::string error;
270 scoped_refptr<const Extension> extension(Extension::Create(
271 info->extension_path,
272 info->extension_location,
273 *info->extension_manifest,
274 prefs->GetCreationFlags(info->extension_id),
275 info->extension_id,
276 &error));
278 if (extension.get())
279 extensions::DataDeleter::StartDeleting(profile_, extension.get());
282 prefs->RemoveEvictedEphemeralApp(info->extension_id);