15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/apps/ephemeral_app_service.h" 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "apps/app_lifetime_monitor_factory.h" 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/command_line.h" 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/message_loop/message_loop.h" 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/apps/ephemeral_app_service_factory.h" 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h" 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/extensions/extension_util.h" 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/notification_service.h" 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/notification_source.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/notification_types.h" 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h" 2023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "extensions/browser/extension_registry.h" 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h" 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "extensions/browser/extension_util.h" 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/notification_types.h" 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/uninstall_reason.h" 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension.h" 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension_set.h" 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::Extension; 295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::ExtensionPrefs; 30116680a4aac90f2aa7413d9095a592090648e557Ben Murdochusing extensions::ExtensionRegistry; 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::ExtensionSet; 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using extensions::ExtensionSystem; 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// The number of seconds after startup before performing garbage collection 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// of ephemeral apps. 38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst int kGarbageCollectAppsStartupDelay = 60; 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// The number of seconds after an ephemeral app has been installed before 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// performing garbage collection. 42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst int kGarbageCollectAppsInstallDelay = 15; 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// When the number of ephemeral apps reaches this count, trigger garbage 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// collection to trim off the least-recently used apps in excess of 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// kMaxEphemeralAppsCount. 47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst int kGarbageCollectAppsTriggerCount = 35; 48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The number of seconds after an app has stopped running before it will be 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// disabled. 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kDefaultDisableAppDelay = 1; 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The number of seconds after startup before disabling inactive ephemeral apps. 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int kDisableAppsOnStartupDelay = 5; 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int EphemeralAppService::kAppInactiveThreshold = 10; 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int EphemeralAppService::kAppKeepThreshold = 1; 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const int EphemeralAppService::kMaxEphemeralAppsCount = 30; 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)EphemeralAppService* EphemeralAppService::Get(Profile* profile) { 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return EphemeralAppServiceFactory::GetForProfile(profile); 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)EphemeralAppService::EphemeralAppService(Profile* profile) 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : profile_(profile), 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) extension_registry_observer_(this), 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) app_lifetime_monitor_observer_(this), 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ephemeral_app_count_(-1), 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) disable_idle_app_delay_(kDefaultDisableAppDelay), 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) weak_ptr_factory_(this) { 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) registrar_.Add(this, 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) content::Source<Profile>(profile_)); 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)EphemeralAppService::~EphemeralAppService() { 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 82116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid EphemeralAppService::ClearCachedApps() { 83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Cancel any pending garbage collects. 84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch garbage_collect_apps_timer_.Stop(); 85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(registry); 88116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 89116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(prefs); 90116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionService* service = 91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionSystem::Get(profile_)->extension_service(); 92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(service); 93116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 94116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch scoped_ptr<ExtensionSet> extensions = 95116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch registry->GenerateInstalledExtensionsSet(); 96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for (ExtensionSet::const_iterator it = extensions->begin(); 98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch it != extensions->end(); 99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ++it) { 100116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch std::string extension_id = (*it)->id(); 101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!prefs->IsEphemeralApp(extension_id)) 102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue; 103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Do not remove apps that are running. 105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!extensions::util::IsExtensionIdle(extension_id, profile_)) 106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue; 107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(registry->GetExtensionById(extension_id, 109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionRegistry::EVERYTHING)); 110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch service->UninstallExtension( 111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch extension_id, 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&base::DoNothing), 114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch NULL); 115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::Observe( 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int type, 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const content::NotificationSource& source, 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const content::NotificationDetails& details) { 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) switch (type) { 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED: { 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Init(); 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) default: 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NOTREACHED(); 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void EphemeralAppService::OnExtensionWillBeInstalled( 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) content::BrowserContext* browser_context, 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const extensions::Extension* extension, 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool is_update, 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool from_ephemeral, 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const std::string& old_name) { 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (from_ephemeral) { 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // An ephemeral app was just promoted to a regular installed app. 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) --ephemeral_app_count_; 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_GE(ephemeral_app_count_, 0); 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) HandleEphemeralAppPromoted(extension); 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (!is_update && 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) extensions::util::IsEphemeralApp(extension->id(), profile_)) { 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // A new ephemeral app was launched. 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++ephemeral_app_count_; 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (ephemeral_app_count_ >= kGarbageCollectAppsTriggerCount) { 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) TriggerGarbageCollect( 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::TimeDelta::FromSeconds(kGarbageCollectAppsInstallDelay)); 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void EphemeralAppService::OnExtensionUninstalled( 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) content::BrowserContext* browser_context, 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const extensions::Extension* extension, 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::UninstallReason reason) { 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (extensions::util::IsEphemeralApp(extension->id(), profile_)) { 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) --ephemeral_app_count_; 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_GE(ephemeral_app_count_, 0); 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void EphemeralAppService::OnAppStop(Profile* profile, 1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const std::string& app_id) { 1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!extensions::util::IsEphemeralApp(app_id, profile_)) 1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return; 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::MessageLoop::current()->PostDelayedTask( 1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) FROM_HERE, 1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&EphemeralAppService::DisableEphemeralApp, 1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) weak_ptr_factory_.GetWeakPtr(), 1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) app_id), 1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::TimeDelta::FromSeconds(disable_idle_app_delay_)); 1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void EphemeralAppService::OnChromeTerminating() { 1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) garbage_collect_apps_timer_.Stop(); 1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extension_registry_observer_.RemoveAll(); 1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) app_lifetime_monitor_observer_.RemoveAll(); 1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::Init() { 1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InitEphemeralAppCount(); 1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Start observing. 1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) app_lifetime_monitor_observer_.Add( 1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) apps::AppLifetimeMonitorFactory::GetForProfile(profile_)); 1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Execute startup clean up tasks (except during tests). 1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) 1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return; 1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) TriggerGarbageCollect( 197effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch base::TimeDelta::FromSeconds(kGarbageCollectAppsStartupDelay)); 1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::MessageLoop::current()->PostDelayedTask( 2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) FROM_HERE, 2015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&EphemeralAppService::DisableEphemeralAppsOnStartup, 2025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) weak_ptr_factory_.GetWeakPtr()), 2035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::TimeDelta::FromSeconds(kDisableAppsOnStartupDelay)); 2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::InitEphemeralAppCount() { 2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) scoped_ptr<ExtensionSet> extensions = 208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet(); 209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(prefs); 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ephemeral_app_count_ = 0; 2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (ExtensionSet::const_iterator it = extensions->begin(); 2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) it != extensions->end(); ++it) { 2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Extension* extension = it->get(); 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (prefs->IsEphemeralApp(extension->id())) 2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ++ephemeral_app_count_; 2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void EphemeralAppService::DisableEphemeralApp(const std::string& app_id) { 2225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!extensions::util::IsEphemeralApp(app_id, profile_) || 2235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) !extensions::util::IsExtensionIdle(app_id, profile_)) { 2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return; 2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // After an ephemeral app has stopped running, unload it from extension 2285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // system and disable it to prevent all background activity. 2295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionService* service = 2305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionSystem::Get(profile_)->extension_service(); 2315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK(service); 2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) service->DisableExtension(app_id, Extension::DISABLE_INACTIVE_EPHEMERAL_APP); 2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void EphemeralAppService::DisableEphemeralAppsOnStartup() { 2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK(prefs); 2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionService* service = 2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionSystem::Get(profile_)->extension_service(); 2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK(service); 2415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Ensure that all inactive ephemeral apps are disabled to prevent all 2435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // background activity. This is done on startup to catch any apps that escaped 2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // being disabled on shutdown. 2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scoped_ptr<ExtensionSet> extensions = 2465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionRegistry::Get(profile_)->GenerateInstalledExtensionsSet(); 2475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for (ExtensionSet::const_iterator it = extensions->begin(); 2485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) it != extensions->end(); 2495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ++it) { 2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Extension* extension = it->get(); 2515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!prefs->IsEphemeralApp(extension->id())) 2525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) continue; 2535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Only V2 apps are installed ephemerally. Remove other ephemeral app types 2555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // that were cached before this policy was introduced. 2565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!extension->is_platform_app()) { 2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) service->UninstallExtension( 2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extension->id(), 2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, 2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&base::DoNothing), 2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) NULL); 2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) continue; 2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!prefs->HasDisableReason(extension->id(), 2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Extension::DISABLE_INACTIVE_EPHEMERAL_APP) && 2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) !prefs->IsExtensionRunning(extension->id()) && 2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::util::IsExtensionIdle(extension->id(), profile_)) { 2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) service->DisableExtension(extension->id(), 2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Extension::DISABLE_INACTIVE_EPHEMERAL_APP); 2715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 2745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void EphemeralAppService::HandleEphemeralAppPromoted(const Extension* app) { 2765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // When ephemeral apps are promoted to regular install apps, remove the 2775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // DISABLE_INACTIVE_EPHEMERAL_APP reason and enable the app if there are no 2785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // other reasons. 2795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK(prefs); 2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) int disable_reasons = prefs->GetDisableReasons(app->id()); 2835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (disable_reasons & Extension::DISABLE_INACTIVE_EPHEMERAL_APP) { 2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) prefs->RemoveDisableReason(app->id(), 2855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Extension::DISABLE_INACTIVE_EPHEMERAL_APP); 2865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (disable_reasons == Extension::DISABLE_INACTIVE_EPHEMERAL_APP) 2875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) prefs->SetExtensionState(app->id(), Extension::ENABLED); 2885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::TriggerGarbageCollect(const base::TimeDelta& delay) { 292effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!garbage_collect_apps_timer_.IsRunning()) { 293effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch garbage_collect_apps_timer_.Start( 294effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch FROM_HERE, 295effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch delay, 296effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch this, 297effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch &EphemeralAppService::GarbageCollectApps); 298effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::GarbageCollectApps() { 302116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); 303116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK(registry); 30423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_); 305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(prefs); 3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 307116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch scoped_ptr<ExtensionSet> extensions = 308116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch registry->GenerateInstalledExtensionsSet(); 309116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int app_count = 0; 3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) LaunchTimeAppMap app_launch_times; 3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::set<std::string> remove_app_ids; 3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Populate a list of ephemeral apps, ordered by their last launch time. 3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (ExtensionSet::const_iterator it = extensions->begin(); 3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) it != extensions->end(); ++it) { 3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Extension* extension = it->get(); 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!prefs->IsEphemeralApp(extension->id())) 3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) continue; 3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ++app_count; 3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Do not remove ephemeral apps that are running. 3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!extensions::util::IsExtensionIdle(extension->id(), profile_)) 3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) continue; 3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Time last_launch_time = prefs->GetLastLaunchTime(extension->id()); 3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If the last launch time is invalid, this may be because it was just 3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // installed. So use the install time. If this is also null for some reason, 3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // the app will be removed. 3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (last_launch_time.is_null()) 3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) last_launch_time = prefs->GetInstallTime(extension->id()); 3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) app_launch_times.insert(std::make_pair(last_launch_time, extension->id())); 3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 33823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) ExtensionService* service = 33923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) ExtensionSystem::Get(profile_)->extension_service(); 34023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) DCHECK(service); 341116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Execute the eviction policies and remove apps marked for deletion. 3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!app_launch_times.empty()) { 3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) GetAppsToRemove(app_count, app_launch_times, &remove_app_ids); 344116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (std::set<std::string>::const_iterator id = remove_app_ids.begin(); 3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) id != remove_app_ids.end(); ++id) { 347116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Protect against cascading uninstalls. 348116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!registry->GetExtensionById(*id, ExtensionRegistry::EVERYTHING)) 349116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue; 350116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 351116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch service->UninstallExtension( 352116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch *id, 3535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions::UNINSTALL_REASON_ORPHANED_EPHEMERAL_EXTENSION, 3545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) base::Bind(&base::DoNothing), 355116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch NULL); 3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static 3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void EphemeralAppService::GetAppsToRemove( 3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int app_count, 3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const LaunchTimeAppMap& app_launch_times, 3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::set<std::string>* remove_app_ids) { 3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::Time time_now = base::Time::Now(); 3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::Time inactive_threshold = 3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) time_now - base::TimeDelta::FromDays(kAppInactiveThreshold); 3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::Time keep_threshold = 3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) time_now - base::TimeDelta::FromDays(kAppKeepThreshold); 3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Visit the apps in order of least recently to most recently launched. 3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (LaunchTimeAppMap::const_iterator it = app_launch_times.begin(); 3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) it != app_launch_times.end(); ++it) { 3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Cannot remove apps that have been launched recently. So break when we 3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // reach the new apps. 3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (it->first > keep_threshold) 3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Remove ephemeral apps that have been inactive for a while or if the cache 3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // is larger than the desired size. 3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (it->first < inactive_threshold || app_count > kMaxEphemeralAppsCount) { 3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) remove_app_ids->insert(it->second); 3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) --app_count; 3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 389