Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / ephemeral_app_service.cc
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.
4
5 #include "chrome/browser/apps/ephemeral_app_service.h"
6
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/extension_service.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_source.h"
16 #include "content/public/browser/notification_types.h"
17 #include "extensions/browser/extension_prefs.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_set.h"
21
22 using extensions::Extension;
23 using extensions::ExtensionPrefs;
24 using extensions::ExtensionSet;
25 using extensions::ExtensionSystem;
26 using extensions::InstalledExtensionInfo;
27
28 namespace {
29
30 // The number of seconds after startup before performing garbage collection
31 // of ephemeral apps.
32 const int kGarbageCollectStartupDelay = 60;
33
34 // The number of seconds after an ephemeral app has been installed before
35 // performing garbage collection.
36 const int kGarbageCollectInstallDelay = 15;
37
38 // When the number of ephemeral apps reaches this count, trigger garbage
39 // collection to trim off the least-recently used apps in excess of
40 // kMaxEphemeralAppsCount.
41 const int kGarbageCollectTriggerCount = 35;
42
43 }  // namespace
44
45 // The number of days of inactivity before an ephemeral app will be removed.
46 const int EphemeralAppService::kAppInactiveThreshold = 10;
47
48 // If the ephemeral app has been launched within this number of days, it will
49 // definitely not be garbage collected.
50 const int EphemeralAppService::kAppKeepThreshold = 1;
51
52 // The maximum number of ephemeral apps to keep cached. Excess may be removed.
53 const int EphemeralAppService::kMaxEphemeralAppsCount = 30;
54
55 // static
56 EphemeralAppService* EphemeralAppService::Get(Profile* profile) {
57   return EphemeralAppServiceFactory::GetForProfile(profile);
58 }
59
60 EphemeralAppService::EphemeralAppService(Profile* profile)
61     : profile_(profile),
62       ephemeral_app_count_(-1) {
63   if (!CommandLine::ForCurrentProcess()->HasSwitch(
64             switches::kEnableEphemeralApps))
65     return;
66
67   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
68                  content::Source<Profile>(profile_));
69   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
70                  content::Source<Profile>(profile_));
71   registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
72                  content::Source<Profile>(profile_));
73   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
74                  content::Source<Profile>(profile_));
75 }
76
77 EphemeralAppService::~EphemeralAppService() {
78 }
79
80 void EphemeralAppService::Observe(
81     int type,
82     const content::NotificationSource& source,
83     const content::NotificationDetails& details) {
84   switch (type) {
85     case chrome::NOTIFICATION_EXTENSIONS_READY: {
86       Init();
87       break;
88     }
89     case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
90       const Extension* extension =
91           content::Details<const InstalledExtensionInfo>(details)->extension;
92       DCHECK(extension);
93       if (extension->is_ephemeral()) {
94         ++ephemeral_app_count_;
95         if (ephemeral_app_count_ >= kGarbageCollectTriggerCount)
96           TriggerGarbageCollect(
97               base::TimeDelta::FromSeconds(kGarbageCollectInstallDelay));
98       }
99       break;
100     }
101     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
102       const Extension* extension =
103           content::Details<const Extension>(details).ptr();
104       DCHECK(extension);
105       if (extension->is_ephemeral())
106         --ephemeral_app_count_;
107       break;
108     }
109     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
110       // Ideally we need to know when the extension system is shutting down.
111       garbage_collect_timer_.Stop();
112       break;
113     }
114     default:
115       NOTREACHED();
116   };
117 }
118
119 void EphemeralAppService::Init() {
120   InitEphemeralAppCount();
121   TriggerGarbageCollect(
122       base::TimeDelta::FromSeconds(kGarbageCollectStartupDelay));
123 }
124
125 void EphemeralAppService::InitEphemeralAppCount() {
126   ExtensionService* service =
127       ExtensionSystem::Get(profile_)->extension_service();
128   DCHECK(service);
129   scoped_ptr<ExtensionSet> extensions =
130       service->GenerateInstalledExtensionsSet();
131
132   ephemeral_app_count_ = 0;
133   for (ExtensionSet::const_iterator it = extensions->begin();
134        it != extensions->end(); ++it) {
135     const Extension* extension = *it;
136     if (extension->is_ephemeral())
137       ++ephemeral_app_count_;
138   }
139 }
140
141 void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) {
142   if (!garbage_collect_timer_.IsRunning())
143     garbage_collect_timer_.Start(
144       FROM_HERE,
145       delay,
146       this,
147       &EphemeralAppService::GarbageCollectApps);
148 }
149
150 void EphemeralAppService::GarbageCollectApps() {
151   ExtensionSystem* system = ExtensionSystem::Get(profile_);
152   DCHECK(system);
153   ExtensionService* service = system->extension_service();
154   DCHECK(service);
155   ExtensionPrefs* prefs = service->extension_prefs();
156   scoped_ptr<ExtensionSet> extensions =
157       service->GenerateInstalledExtensionsSet();
158
159   int app_count = 0;
160   LaunchTimeAppMap app_launch_times;
161   std::set<std::string> remove_app_ids;
162
163   // Populate a list of ephemeral apps, ordered by their last launch time.
164   for (ExtensionSet::const_iterator it = extensions->begin();
165        it != extensions->end(); ++it) {
166     const Extension* extension = *it;
167     if (!extension->is_ephemeral())
168       continue;
169
170     ++app_count;
171
172     // Do not remove ephemeral apps that are running.
173     if (!extensions::util::IsExtensionIdle(extension->id(), profile_))
174       continue;
175
176     base::Time last_launch_time = prefs->GetLastLaunchTime(extension->id());
177
178     // If the last launch time is invalid, this may be because it was just
179     // installed. So use the install time. If this is also null for some reason,
180     // the app will be removed.
181     if (last_launch_time.is_null())
182       last_launch_time = prefs->GetInstallTime(extension->id());
183
184     app_launch_times.insert(std::make_pair(last_launch_time, extension->id()));
185   }
186
187   // Execute the replacement policies and remove apps marked for deletion.
188   if (!app_launch_times.empty()) {
189     GetAppsToRemove(app_count, app_launch_times, &remove_app_ids);
190     for (std::set<std::string>::const_iterator id = remove_app_ids.begin();
191          id != remove_app_ids.end(); ++id) {
192       if (service->UninstallExtension(*id, false, NULL))
193         --app_count;
194     }
195   }
196
197   ephemeral_app_count_ = app_count;
198 }
199
200 // static
201 void EphemeralAppService::GetAppsToRemove(
202     int app_count,
203     const LaunchTimeAppMap& app_launch_times,
204     std::set<std::string>* remove_app_ids) {
205   base::Time time_now = base::Time::Now();
206   const base::Time inactive_threshold =
207       time_now - base::TimeDelta::FromDays(kAppInactiveThreshold);
208   const base::Time keep_threshold =
209       time_now - base::TimeDelta::FromDays(kAppKeepThreshold);
210
211   // Visit the apps in order of least recently to most recently launched.
212   for (LaunchTimeAppMap::const_iterator it = app_launch_times.begin();
213        it != app_launch_times.end(); ++it) {
214     // Cannot remove apps that have been launched recently. So break when we
215     // reach the new apps.
216     if (it->first > keep_threshold)
217         break;
218
219     // Remove ephemeral apps that have been inactive for a while or if the cache
220     // is larger than the desired size.
221     if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) {
222       remove_app_ids->insert(it->second);
223       --app_count;
224     } else {
225       break;
226     }
227   }
228 }