Commit 59617d98 authored by benwells's avatar benwells Committed by Commit bot

Delete all cached ephemeral apps at startup.

Ephemeral apps were cached when downloaded. The ephemeral app service
managed this cache, uninstalling old apps when the cache got full etc.
It also disabled ephemeral apps when they weren't running.

This change removes most of the ephemeral app service, and replaces it
with a simple implementation that deletes all cached apps at startup.
Some code to disable after they are stopped is left to keep the existing
tests passing.

Once the code to delete all cached ephemeral apps has been in stable
for a milestone, the remaining bits of the ephemeral app system will be
removed.

BUG=517735

Review URL: https://codereview.chromium.org/1415863002

Cr-Commit-Position: refs/heads/master@{#357008}
parent 5c2c5188
......@@ -10,7 +10,6 @@
#include "apps/app_lifetime_monitor.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/extension_registry_observer.h"
......@@ -21,7 +20,8 @@ class Extension;
class ExtensionRegistry;
} // namespace extensions
// Performs the background garbage collection of ephemeral apps.
// Delete cached ephemeral apps at startup.
// TODO(benwells): Remove this system. https://crbug.com/517735.
class EphemeralAppService : public KeyedService,
public extensions::ExtensionRegistryObserver,
public apps::AppLifetimeMonitor::Observer {
......@@ -36,55 +36,28 @@ class EphemeralAppService : public KeyedService,
// Clears the ephemeral app cache. Removes all idle ephemeral apps.
void ClearCachedApps();
int ephemeral_app_count() const { return ephemeral_app_count_; }
void set_disable_delay_for_test(int delay) {
disable_idle_app_delay_ = delay;
}
// Constants exposed for testing purposes:
// The number of days of inactivity before an ephemeral app will be removed.
static const int kAppInactiveThreshold;
// If the ephemeral app has been launched within this number of days, it will
// definitely not be garbage collected.
static const int kAppKeepThreshold;
// The maximum number of ephemeral apps to keep cached. Excess may be removed.
static const int kMaxEphemeralAppsCount;
private:
// A map used to order the ephemeral apps by their last launch time.
typedef std::multimap<base::Time, std::string> LaunchTimeAppMap;
// extensions::ExtensionRegistryObserver.
void OnExtensionWillBeInstalled(content::BrowserContext* browser_context,
const extensions::Extension* extension,
bool is_update,
bool from_ephemeral,
const std::string& old_name) override;
void OnExtensionUninstalled(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UninstallReason reason) override;
// apps::AppLifetimeMonitor::Observer implementation.
void OnAppStop(Profile* profile, const std::string& app_id) override;
void OnChromeTerminating() override;
void Init();
void InitEphemeralAppCount();
void DisableEphemeralApp(const std::string& app_id);
void DisableEphemeralAppsOnStartup();
void HandleEphemeralAppPromoted(const extensions::Extension* app);
// Garbage collect ephemeral apps.
void TriggerGarbageCollect(const base::TimeDelta& delay);
void GarbageCollectApps();
static void GetAppsToRemove(int app_count,
const LaunchTimeAppMap& app_launch_times,
std::set<std::string>* remove_app_ids);
Profile* profile_;
ScopedObserver<extensions::ExtensionRegistry,
......@@ -93,18 +66,12 @@ class EphemeralAppService : public KeyedService,
ScopedObserver<apps::AppLifetimeMonitor, apps::AppLifetimeMonitor::Observer>
app_lifetime_monitor_observer_;
base::OneShotTimer garbage_collect_apps_timer_;
// The count of cached ephemeral apps.
int ephemeral_app_count_;
// Number of seconds before disabling idle ephemeral apps.
// Overridden in tests.
int disable_idle_app_delay_;
base::WeakPtrFactory<EphemeralAppService> weak_ptr_factory_;
friend class EphemeralAppServiceTest;
friend class EphemeralAppServiceBrowserTest;
DISALLOW_COPY_AND_ASSIGN(EphemeralAppService);
......
......@@ -6,137 +6,15 @@
#include "chrome/browser/apps/ephemeral_app_browsertest.h"
#include "chrome/browser/apps/ephemeral_app_service.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/test_extension_registry_observer.h"
using extensions::Extension;
using extensions::ExtensionPrefs;
using extensions::ExtensionRegistry;
namespace {
class EphemeralAppServiceBrowserTest : public EphemeralAppTestBase {};
const int kNumTestApps = 2;
const char* kTestApps[] = {
"app_window/generic",
"minimal"
};
} // namespace
class EphemeralAppServiceBrowserTest : public EphemeralAppTestBase {
protected:
void LoadApps() {
for (int i = 0; i < kNumTestApps; ++i) {
const Extension* extension = InstallEphemeralApp(kTestApps[i]);
ASSERT_TRUE(extension);
app_ids_.push_back(extension->id());
}
ASSERT_EQ(kNumTestApps, (int) app_ids_.size());
}
void GarbageCollectEphemeralApps() {
EphemeralAppService* ephemeral_service = EphemeralAppService::Get(
browser()->profile());
ASSERT_TRUE(ephemeral_service);
ephemeral_service->GarbageCollectApps();
}
void InitEphemeralAppCount(EphemeralAppService* ephemeral_service) {
ephemeral_service->InitEphemeralAppCount();
}
void DisableEphemeralAppsOnStartup() {
EphemeralAppService* ephemeral_service =
EphemeralAppService::Get(browser()->profile());
ASSERT_TRUE(ephemeral_service);
ephemeral_service->DisableEphemeralAppsOnStartup();
}
std::vector<std::string> app_ids_;
};
// Verifies that inactive ephemeral apps are uninstalled and active apps are
// not removed. Extensive testing of the ephemeral app cache's replacement
// policies is done in the unit tests for EphemeralAppService. This is more
// like an integration test.
IN_PROC_BROWSER_TEST_F(EphemeralAppServiceBrowserTest,
GarbageCollectInactiveApps) {
EphemeralAppService* ephemeral_service =
EphemeralAppService::Get(browser()->profile());
ASSERT_TRUE(ephemeral_service);
InitEphemeralAppCount(ephemeral_service);
LoadApps();
const base::Time time_now = base::Time::Now();
ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
ASSERT_TRUE(prefs);
// Set launch time for an inactive app.
std::string inactive_app_id = app_ids_[0];
base::Time inactive_launch = time_now -
base::TimeDelta::FromDays(EphemeralAppService::kAppInactiveThreshold + 1);
prefs->SetLastLaunchTime(inactive_app_id, inactive_launch);
// Set launch time for an active app.
std::string active_app_id = app_ids_[1];
base::Time active_launch = time_now -
base::TimeDelta::FromDays(EphemeralAppService::kAppKeepThreshold);
prefs->SetLastLaunchTime(active_app_id, active_launch);
// Perform garbage collection.
extensions::TestExtensionRegistryObserver observer(
ExtensionRegistry::Get(browser()->profile()));
GarbageCollectEphemeralApps();
observer.WaitForExtensionUninstalled();
ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
ASSERT_TRUE(registry);
EXPECT_FALSE(registry->GetExtensionById(inactive_app_id,
ExtensionRegistry::EVERYTHING));
EXPECT_TRUE(
registry->GetExtensionById(active_app_id, ExtensionRegistry::EVERYTHING));
EXPECT_EQ(1, ephemeral_service->ephemeral_app_count());
}
// Verify that the count of ephemeral apps is maintained correctly.
IN_PROC_BROWSER_TEST_F(EphemeralAppServiceBrowserTest, EphemeralAppCount) {
EphemeralAppService* ephemeral_service =
EphemeralAppService::Get(browser()->profile());
ASSERT_TRUE(ephemeral_service);
InitEphemeralAppCount(ephemeral_service);
// The count should not increase for regular installed apps.
EXPECT_TRUE(InstallPlatformApp("minimal"));
EXPECT_EQ(0, ephemeral_service->ephemeral_app_count());
// The count should increase when an ephemeral app is added.
const Extension* app = InstallEphemeralApp(kMessagingReceiverApp);
ASSERT_TRUE(app);
EXPECT_EQ(1, ephemeral_service->ephemeral_app_count());
// The count should remain constant if the ephemeral app is updated.
const std::string app_id = app->id();
app = UpdateEphemeralApp(
app_id, GetTestPath(kMessagingReceiverAppV2),
GetTestPath(kMessagingReceiverApp).ReplaceExtension(
FILE_PATH_LITERAL(".pem")));
ASSERT_TRUE(app);
EXPECT_EQ(1, ephemeral_service->ephemeral_app_count());
// The count should decrease when an ephemeral app is promoted to a regular
// installed app.
PromoteEphemeralApp(app);
EXPECT_EQ(0, ephemeral_service->ephemeral_app_count());
}
// Verify that the cache of ephemeral apps is correctly cleared. Running apps
// should not be removed.
// Verify that the cache of ephemeral apps is correctly cleared. All ephemeral
// apps should be removed.
IN_PROC_BROWSER_TEST_F(EphemeralAppServiceBrowserTest, ClearCachedApps) {
const Extension* running_app =
InstallAndLaunchEphemeralApp(kMessagingReceiverApp);
......@@ -149,59 +27,19 @@ IN_PROC_BROWSER_TEST_F(EphemeralAppServiceBrowserTest, ClearCachedApps) {
EphemeralAppService* ephemeral_service =
EphemeralAppService::Get(browser()->profile());
ASSERT_TRUE(ephemeral_service);
EXPECT_EQ(2, ephemeral_service->ephemeral_app_count());
ephemeral_service->ClearCachedApps();
ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
ASSERT_TRUE(registry);
EXPECT_FALSE(registry->GetExtensionById(inactive_app_id,
ExtensionRegistry::EVERYTHING));
EXPECT_TRUE(registry->GetExtensionById(running_app_id,
ExtensionRegistry::EVERYTHING));
EXPECT_EQ(1, ephemeral_service->ephemeral_app_count());
}
// Verify that the service will unload and disable ephemeral apps on startup.
IN_PROC_BROWSER_TEST_F(EphemeralAppServiceBrowserTest,
DisableEphemeralAppsOnStartup) {
const Extension* installed_app = InstallPlatformApp(kNotificationsTestApp);
const Extension* running_app =
InstallAndLaunchEphemeralApp(kMessagingReceiverApp);
const Extension* inactive_app = InstallEphemeralApp(kDispatchEventTestApp);
const Extension* disabled_app = InstallEphemeralApp(kFileSystemTestApp);
ASSERT_TRUE(installed_app);
ASSERT_TRUE(running_app);
ASSERT_TRUE(inactive_app);
ASSERT_TRUE(disabled_app);
DisableEphemeralApp(disabled_app, Extension::DISABLE_PERMISSIONS_INCREASE);
ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
ASSERT_TRUE(registry);
EXPECT_TRUE(registry->enabled_extensions().Contains(installed_app->id()));
EXPECT_TRUE(registry->enabled_extensions().Contains(running_app->id()));
EXPECT_TRUE(registry->enabled_extensions().Contains(inactive_app->id()));
EXPECT_TRUE(registry->disabled_extensions().Contains(disabled_app->id()));
int extension_count = registry->GenerateInstalledExtensionsSet()->size();
DisableEphemeralAppsOnStartup();
ephemeral_service->ClearCachedApps();
// Verify that the inactive app is disabled.
EXPECT_TRUE(registry->enabled_extensions().Contains(installed_app->id()));
EXPECT_TRUE(registry->enabled_extensions().Contains(running_app->id()));
EXPECT_TRUE(registry->disabled_extensions().Contains(inactive_app->id()));
EXPECT_TRUE(registry->disabled_extensions().Contains(disabled_app->id()));
EXPECT_FALSE(registry->GetExtensionById(inactive_app_id,
ExtensionRegistry::EVERYTHING));
EXPECT_FALSE(registry->GetExtensionById(running_app_id,
ExtensionRegistry::EVERYTHING));
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
ASSERT_TRUE(prefs);
EXPECT_FALSE(prefs->HasDisableReason(
installed_app->id(), Extension::DISABLE_INACTIVE_EPHEMERAL_APP));
EXPECT_FALSE(prefs->HasDisableReason(
running_app->id(), Extension::DISABLE_INACTIVE_EPHEMERAL_APP));
EXPECT_TRUE(prefs->HasDisableReason(
inactive_app->id(), Extension::DISABLE_INACTIVE_EPHEMERAL_APP));
EXPECT_TRUE(prefs->HasDisableReason(
disabled_app->id(), Extension::DISABLE_INACTIVE_EPHEMERAL_APP));
EXPECT_TRUE(prefs->HasDisableReason(
disabled_app->id(), Extension::DISABLE_PERMISSIONS_INCREASE));
int new_extension_count = registry->GenerateInstalledExtensionsSet()->size();
EXPECT_EQ(2, extension_count - new_extension_count);
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/apps/ephemeral_app_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Generate a time N number of days before the reference time.
// The generated time can be randomized.
base::Time GetPastTime(const base::Time& reference_time,
int days_before,
bool randomize_time = false) {
base::Time generated_time =
reference_time - base::TimeDelta::FromDays(days_before);
// Add an hour so that the time is well within the number of days before.
generated_time += base::TimeDelta::FromHours(1);
// Add a random number of seconds between 0 - 10 hours.
if (randomize_time)
generated_time += base::TimeDelta::FromSeconds(base::RandInt(0, 36000));
return generated_time;
}
} // namespace
class EphemeralAppServiceTest : public testing::Test {
protected:
typedef EphemeralAppService::LaunchTimeAppMap LaunchTimeAppMap;
EphemeralAppServiceTest() {}
~EphemeralAppServiceTest() override {}
void RunTest(int ephemeral_app_count,
const LaunchTimeAppMap& launch_times,
const std::set<std::string>& expected_removed_ids) {
std::set<std::string> remove_app_ids;
EphemeralAppService::GetAppsToRemove(ephemeral_app_count,
launch_times,
&remove_app_ids);
EXPECT_EQ(expected_removed_ids, remove_app_ids);
}
void RunTestCheckLRU(int ephemeral_app_count,
LaunchTimeAppMap& launch_times,
int expected_removed_count) {
std::set<std::string> remove_app_ids;
EphemeralAppService::GetAppsToRemove(ephemeral_app_count,
launch_times,
&remove_app_ids);
EXPECT_EQ(expected_removed_count, (int) remove_app_ids.size());
// Move the launch times of removed apps to another map.
LaunchTimeAppMap removed_apps;
for (LaunchTimeAppMap::iterator it = launch_times.begin();
it != launch_times.end(); ) {
if (remove_app_ids.find(it->second) != remove_app_ids.end()) {
removed_apps.insert(*it);
launch_times.erase(it++);
} else {
++it;
}
}
if (launch_times.empty())
return;
// Verify that the removed apps have launch times earlier than or equal to
// all retained apps. We can actually just compare with the first entry in
// |launch_times| but will make no implementation assumptions.
for (LaunchTimeAppMap::const_iterator removed = removed_apps.begin();
removed != removed_apps.end(); ++removed) {
for (LaunchTimeAppMap::iterator retained = launch_times.begin();
retained != launch_times.end(); ++retained) {
EXPECT_LE(removed->first, retained->first);
}
}
}
// Generate X app launch times, N days before the reference time.
// If |generated_ids| is not NULL, the generated app IDs will be added
// to the set.
void GenerateLaunchTimes(const base::Time& reference_time,
int days_before,
int count,
LaunchTimeAppMap* launch_times,
std::set<std::string>* generated_ids = NULL) {
for (int i = 0; i < count; ++i) {
std::string app_id = base::SizeTToString(launch_times->size());
launch_times->insert(std::make_pair(
GetPastTime(reference_time, days_before, true),
app_id));
if (generated_ids)
generated_ids->insert(app_id);
}
}
// Add inactive apps that should always be removed by garbage collection.
void AddInactiveApps(const base::Time& reference_time,
int count,
LaunchTimeAppMap* launch_times,
std::set<std::string>* generated_ids = NULL) {
GenerateLaunchTimes(reference_time,
EphemeralAppService::kAppInactiveThreshold + 1,
count,
launch_times,
generated_ids);
}
// Add recently launched apps that should NOT be removed by garbage
// collection regardless of the number of cached ephemeral apps.
void AddRecentlyLaunchedApps(const base::Time& reference_time,
int count,
LaunchTimeAppMap* launch_times,
std::set<std::string>* generated_ids = NULL) {
GenerateLaunchTimes(reference_time,
EphemeralAppService::kAppKeepThreshold,
count,
launch_times,
generated_ids);
}
// Add apps launched between the kAppInactiveThreshold and kAppKeepThreshold,
// which may or may not be removed by garbage collection depending on the
// number of ephemeral apps in the cache.
void AddIntermediateApps(const base::Time& reference_time,
int count,
LaunchTimeAppMap* launch_times,
std::set<std::string>* generated_ids = NULL) {
int days_before = base::RandInt(EphemeralAppService::kAppKeepThreshold + 1,
EphemeralAppService::kAppInactiveThreshold);
GenerateLaunchTimes(reference_time,
days_before,
count,
launch_times,
generated_ids);
}
};
// Verify that inactive apps are removed even if the cache has not reached
// capacity.
// Test case: | inactive |
// Expected output: All inactive apps removed.
TEST_F(EphemeralAppServiceTest, RemoveInactiveApps) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
std::set<std::string> expected_removed_ids;
AddInactiveApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount / 5,
&launch_times,
&expected_removed_ids);
RunTest(launch_times.size(), launch_times, expected_removed_ids);
}
// Verify that inactive apps are removed even if the cache has not reached
// capacity.
// Test case: | inactive | intermediate | recently launched |
// Expected output: All inactive apps removed, other apps retained.
TEST_F(EphemeralAppServiceTest, RemoveInactiveAppsKeepOthers) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
std::set<std::string> expected_removed_ids;
AddInactiveApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount / 5,
&launch_times,
&expected_removed_ids);
AddIntermediateApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount / 5,
&launch_times);
AddRecentlyLaunchedApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount / 5,
&launch_times);
RunTest(launch_times.size(), launch_times, expected_removed_ids);
}
// Verify that recently launched apps will not be removed, even when the cache
// overflows.
// Test case: | recently launched |
// Expected output: All recently launched apps retained.
TEST_F(EphemeralAppServiceTest, KeepRecentLaunch) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
AddRecentlyLaunchedApps(
time_now,
3,
&launch_times);
RunTest(launch_times.size(), launch_times, std::set<std::string>());
AddRecentlyLaunchedApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount,
&launch_times); // overflow
RunTest(launch_times.size(), launch_times, std::set<std::string>());
}
// Verify that recently launched apps will not be removed, even when the cache
// overflows.
// Test case: | intermediate (overflow) | recently launched (overflow) |
// Expected output: All recently launched apps retained, intermediate apps
// removed.
TEST_F(EphemeralAppServiceTest, KeepRecentLaunchRemoveOthers) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
std::set<std::string> expected_removed_ids;
AddRecentlyLaunchedApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount + 3,
&launch_times); // overflow
AddIntermediateApps(
time_now,
3,
&launch_times,
&expected_removed_ids); // overflow
RunTest(launch_times.size(), launch_times, expected_removed_ids);
}
// Verify that the LRU algorithm is implemented correctly.
// Test case: | intermediate (overflow) |
// Expected output: The least recently launched apps are removed.
TEST_F(EphemeralAppServiceTest, RemoveOverflow) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
const int kOverflow = 3;
AddIntermediateApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount + kOverflow,
&launch_times); // overflow
RunTestCheckLRU(launch_times.size(), launch_times, kOverflow);
}
// Verify that GetAppsToRemove() takes into account the number of running apps,
// since they are not included in the launch times.
// Test case: | intermediate (overflow) | running apps |
// Expected output: The least recently launched apps are removed.
TEST_F(EphemeralAppServiceTest, RemoveOverflowWithRunningApps) {
base::Time time_now = base::Time::Now();
LaunchTimeAppMap launch_times;
const int kRunningApps = 3;
AddIntermediateApps(
time_now,
EphemeralAppService::kMaxEphemeralAppsCount,
&launch_times); // overflow
RunTestCheckLRU(
launch_times.size() + kRunningApps,
launch_times,
kRunningApps);
}
......@@ -582,7 +582,6 @@
'browser/apps/app_shim/app_shim_host_mac_unittest.cc',
'browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc',
'browser/apps/drive/drive_app_mapping_unittest.cc',
'browser/apps/ephemeral_app_service_unittest.cc',
'browser/autocomplete/keyword_extensions_delegate_impl_unittest.cc',
'browser/extensions/active_script_controller_unittest.cc',
'browser/extensions/active_tab_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment