Merge "Introduce global package hibernation" am: 6857f6ed91

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1554675

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Idb2e666ad166fba7c59c75d1428082aa9f2a6da5
This commit is contained in:
Kevin Han
2021-01-30 02:11:17 +00:00
committed by Automerger Merge Worker
6 changed files with 271 additions and 78 deletions

View File

@@ -1415,8 +1415,10 @@ package android.app.usage {
package android.apphibernation { package android.apphibernation {
public final class AppHibernationManager { public final class AppHibernationManager {
method public boolean isHibernating(@NonNull String); method public boolean isHibernatingForUser(@NonNull String);
method public void setHibernating(@NonNull String, boolean); method public boolean isHibernatingGlobally(@NonNull String);
method public void setHibernatingForUser(@NonNull String, boolean);
method public void setHibernatingGlobally(@NonNull String, boolean);
} }
} }

View File

@@ -49,31 +49,61 @@ public final class AppHibernationManager {
} }
/** /**
* Returns true if the package is hibernating, false otherwise. * Returns true if the package is hibernating for this context's user, false otherwise.
* *
* @hide * @hide
*/ */
@SystemApi @SystemApi
public boolean isHibernating(@NonNull String packageName) { public boolean isHibernatingForUser(@NonNull String packageName) {
try { try {
return mIAppHibernationService.isHibernating(packageName, mContext.getUserId()); return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId());
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/** /**
* Set whether the package is hibernating. * Set whether the package is hibernating for this context's user.
* *
* @hide * @hide
*/ */
@SystemApi @SystemApi
public void setHibernating(@NonNull String packageName, boolean isHibernating) { public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) {
try { try {
mIAppHibernationService.setHibernating(packageName, mContext.getUserId(), mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(),
isHibernating); isHibernating);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/**
* Returns true if app is hibernating globally / at the package level.
*
* @hide
*/
@SystemApi
public boolean isHibernatingGlobally(@NonNull String packageName) {
try {
return mIAppHibernationService.isHibernatingGlobally(packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set whether a package should be globally hibernating. This hibernates the package at a
* package level. User-level hibernation (e.g.. {@link #isHibernatingForUser} is independent
* from global hibernation.
*
* @hide
*/
@SystemApi
public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) {
try {
mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
} }

View File

@@ -21,6 +21,8 @@ package android.apphibernation;
* @hide * @hide
*/ */
interface IAppHibernationService { interface IAppHibernationService {
boolean isHibernating(String packageName, int userId); boolean isHibernatingForUser(String packageName, int userId);
void setHibernating(String packageName, int userId, boolean isHibernating); void setHibernatingForUser(String packageName, int userId, boolean isHibernating);
boolean isHibernatingGlobally(String packageName);
void setHibernatingGlobally(String packageName, boolean isHibernating);
} }

View File

@@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
import static android.content.Intent.EXTRA_REPLACING; import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ALL;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
@@ -46,6 +47,7 @@ import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
@@ -55,6 +57,7 @@ import com.android.server.SystemService;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* System service that manages app hibernation state, a state apps can enter that means they are * System service that manages app hibernation state, a state apps can enter that means they are
@@ -74,6 +77,8 @@ public final class AppHibernationService extends SystemService {
private final UserManager mUserManager; private final UserManager mUserManager;
@GuardedBy("mLock") @GuardedBy("mLock")
private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>(); private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
@GuardedBy("mLock")
private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
/** /**
* Initializes the system service. * Initializes the system service.
@@ -138,7 +143,7 @@ public final class AppHibernationService extends SystemService {
* @param userId the user to check * @param userId the user to check
* @return true if package is hibernating for the user * @return true if package is hibernating for the user
*/ */
public boolean isHibernating(String packageName, int userId) { boolean isHibernatingForUser(String packageName, int userId) {
userId = handleIncomingUser(userId, "isHibernating"); userId = handleIncomingUser(userId, "isHibernating");
synchronized (mLock) { synchronized (mLock) {
final Map<String, UserPackageState> packageStates = mUserStates.get(userId); final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
@@ -151,7 +156,19 @@ public final class AppHibernationService extends SystemService {
String.format("Package %s is not installed for user %s", String.format("Package %s is not installed for user %s",
packageName, userId)); packageName, userId));
} }
return pkgState != null ? pkgState.hibernated : null; return pkgState.hibernated;
}
}
/**
* Whether a package is hibernated globally. This only occurs when a package is hibernating for
* all users and allows us to make optimizations at the package or APK level.
*
* @param packageName package to check
*/
boolean isHibernatingGlobally(String packageName) {
synchronized (mLock) {
return mGloballyHibernatedPackages.contains(packageName);
} }
} }
@@ -162,7 +179,7 @@ public final class AppHibernationService extends SystemService {
* @param userId user * @param userId user
* @param isHibernating new hibernation state * @param isHibernating new hibernation state
*/ */
public void setHibernating(String packageName, int userId, boolean isHibernating) { void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
userId = handleIncomingUser(userId, "setHibernating"); userId = handleIncomingUser(userId, "setHibernating");
synchronized (mLock) { synchronized (mLock) {
if (!mUserStates.contains(userId)) { if (!mUserStates.contains(userId)) {
@@ -180,31 +197,98 @@ public final class AppHibernationService extends SystemService {
return; return;
} }
if (isHibernating) {
final long caller = Binder.clearCallingIdentity(); hibernatePackageForUserL(packageName, userId, pkgState);
try { } else {
if (isHibernating) { unhibernatePackageForUserL(packageName, userId, pkgState);
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
mIActivityManager.forceStopPackage(packageName, userId);
mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
null /* observer */);
} else {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
mIPackageManager.setPackageStoppedState(packageName, false, userId);
}
pkgState.hibernated = isHibernating;
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to hibernate due to manager not being available", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
Binder.restoreCallingIdentity(caller);
} }
// TODO: Support package level hibernation when package is hibernating for all users
} }
} }
/**
* Set whether the package should be hibernated globally at a package level, allowing the
* the system to make optimizations at the package or APK level.
*
* @param packageName package to hibernate globally
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
synchronized (mLock) {
if (isHibernating) {
hibernatePackageGloballyL(packageName);
} else {
unhibernatePackageGloballyL(packageName);
}
}
}
}
/**
* Put an app into hibernation for a given user, allowing user-level optimizations to occur.
* The caller should hold {@link #mLock}
*
* @param pkgState package hibernation state
*/
private void hibernatePackageForUserL(@NonNull String packageName, int userId,
@NonNull UserPackageState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
mIActivityManager.forceStopPackage(packageName, userId);
mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
null /* observer */);
pkgState.hibernated = true;
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to hibernate due to manager not being available", e);
} finally {
Binder.restoreCallingIdentity(caller);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
/**
* Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
*
* @param pkgState package hibernation state
*/
private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
UserPackageState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
mIPackageManager.setPackageStoppedState(packageName, false, userId);
pkgState.hibernated = false;
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to unhibernate due to manager not being available", e);
} finally {
Binder.restoreCallingIdentity(caller);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
/**
* Put a package into global hibernation, optimizing its storage at a package / APK level.
* The caller should hold {@link #mLock}.
*/
private void hibernatePackageGloballyL(@NonNull String packageName) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
// TODO(175830194): Delete vdex/odex when DexManager API is built out
mGloballyHibernatedPackages.add(packageName);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
* Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
*/
private void unhibernatePackageGloballyL(@NonNull String packageName) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
mGloballyHibernatedPackages.remove(packageName);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/** /**
* Populates {@link #mUserStates} with the users installed packages. The caller should hold * Populates {@link #mUserStates} with the users installed packages. The caller should hold
* {@link #mLock}. * {@link #mLock}.
@@ -220,8 +304,8 @@ public final class AppHibernationService extends SystemService {
throw new IllegalStateException("Package manager not available.", e); throw new IllegalStateException("Package manager not available.", e);
} }
for (PackageInfo pkg : packageList) { for (int i = 0, size = packageList.size(); i < size; i++) {
packages.put(pkg.packageName, new UserPackageState()); packages.put(packageList.get(i).packageName, new UserPackageState());
} }
mUserStates.put(userId, packages); mUserStates.put(userId, packages);
} }
@@ -250,6 +334,12 @@ public final class AppHibernationService extends SystemService {
} }
} }
private void onPackageRemovedForAllUsers(@NonNull String packageName) {
synchronized (mLock) {
mGloballyHibernatedPackages.remove(packageName);
}
}
/** /**
* Private helper method to get the real user id and enforce permission checks. * Private helper method to get the real user id and enforce permission checks.
* *
@@ -277,13 +367,23 @@ public final class AppHibernationService extends SystemService {
} }
@Override @Override
public boolean isHibernating(String packageName, int userId) { public boolean isHibernatingForUser(String packageName, int userId) {
return mService.isHibernating(packageName, userId); return mService.isHibernatingForUser(packageName, userId);
} }
@Override @Override
public void setHibernating(String packageName, int userId, boolean isHibernating) { public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
mService.setHibernating(packageName, userId, isHibernating); mService.setHibernatingForUser(packageName, userId, isHibernating);
}
@Override
public void setHibernatingGlobally(String packageName, boolean isHibernating) {
mService.setHibernatingGlobally(packageName, isHibernating);
}
@Override
public boolean isHibernatingGlobally(String packageName) {
return mService.isHibernatingGlobally(packageName);
} }
@Override @Override
@@ -322,6 +422,9 @@ public final class AppHibernationService extends SystemService {
onPackageAdded(packageName, userId); onPackageAdded(packageName, userId);
} else if (ACTION_PACKAGE_REMOVED.equals(action)) { } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
onPackageRemoved(packageName, userId); onPackageRemoved(packageName, userId);
if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) {
onPackageRemovedForAllUsers(packageName);
}
} }
} }
} }

View File

@@ -18,7 +18,6 @@ package com.android.server.apphibernation;
import android.os.ShellCommand; import android.os.ShellCommand;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -27,6 +26,7 @@ import java.io.PrintWriter;
*/ */
final class AppHibernationShellCommand extends ShellCommand { final class AppHibernationShellCommand extends ShellCommand {
private static final String USER_OPT = "--user"; private static final String USER_OPT = "--user";
private static final String GLOBAL_OPT = "--global";
private static final int SUCCESS = 0; private static final int SUCCESS = 0;
private static final int ERROR = -1; private static final int ERROR = -1;
private final AppHibernationService mService; private final AppHibernationService mService;
@@ -51,7 +51,21 @@ final class AppHibernationShellCommand extends ShellCommand {
} }
private int runSetState() { private int runSetState() {
int userId = parseUserOption(); String opt;
boolean setsGlobal = false;
int userId = UserHandle.USER_CURRENT;
while ((opt = getNextOption()) != null) {
switch (opt) {
case USER_OPT:
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case GLOBAL_OPT:
setsGlobal = true;
break;
default:
getErrPrintWriter().println("Error: Unknown option: " + opt);
}
}
String pkg = getNextArgRequired(); String pkg = getNextArgRequired();
if (pkg == null) { if (pkg == null) {
@@ -66,32 +80,43 @@ final class AppHibernationShellCommand extends ShellCommand {
} }
boolean newState = Boolean.parseBoolean(newStateRaw); boolean newState = Boolean.parseBoolean(newStateRaw);
mService.setHibernating(pkg, userId, newState); if (setsGlobal) {
mService.setHibernatingGlobally(pkg, newState);
} else {
mService.setHibernatingForUser(pkg, userId, newState);
}
return SUCCESS; return SUCCESS;
} }
private int runGetState() { private int runGetState() {
int userId = parseUserOption(); String opt;
boolean requestsGlobal = false;
int userId = UserHandle.USER_CURRENT;
while ((opt = getNextOption()) != null) {
switch (opt) {
case USER_OPT:
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
case GLOBAL_OPT:
requestsGlobal = true;
break;
default:
getErrPrintWriter().println("Error: Unknown option: " + opt);
}
}
String pkg = getNextArgRequired(); String pkg = getNextArgRequired();
if (pkg == null) { if (pkg == null) {
getErrPrintWriter().println("Error: No package specified"); getErrPrintWriter().println("Error: No package specified");
return ERROR; return ERROR;
} }
boolean isHibernating = mService.isHibernating(pkg, userId); boolean isHibernating = requestsGlobal
? mService.isHibernatingGlobally(pkg) : mService.isHibernatingForUser(pkg, userId);
final PrintWriter pw = getOutPrintWriter(); final PrintWriter pw = getOutPrintWriter();
pw.println(isHibernating); pw.println(isHibernating);
return SUCCESS; return SUCCESS;
} }
private int parseUserOption() {
String option = getNextOption();
if (TextUtils.equals(option, USER_OPT)) {
return UserHandle.parseUserArg(getNextArgRequired());
}
return UserHandle.USER_CURRENT;
}
@Override @Override
public void onHelp() { public void onHelp() {
final PrintWriter pw = getOutPrintWriter(); final PrintWriter pw = getOutPrintWriter();
@@ -99,11 +124,13 @@ final class AppHibernationShellCommand extends ShellCommand {
pw.println(" help"); pw.println(" help");
pw.println(" Print this help text."); pw.println(" Print this help text.");
pw.println(""); pw.println("");
pw.println(" set-state [--user USER_ID] PACKAGE true|false"); pw.println(" set-state [--user USER_ID] [--global] PACKAGE true|false");
pw.println(" Sets the hibernation state of the package to value specified"); pw.println(" Sets the hibernation state of the package to value specified. Optionally");
pw.println(" may specify a user id or set global hibernation state.");
pw.println(""); pw.println("");
pw.println(" get-state [--user USER_ID] PACKAGE"); pw.println(" get-state [--user USER_ID] [--global] PACKAGE");
pw.println(" Gets the hibernation state of the package"); pw.println(" Gets the hibernation state of the package. Optionally may specify a user");
pw.println(" id or request global hibernation state.");
pw.println(""); pw.println("");
} }
} }

View File

@@ -64,6 +64,8 @@ public final class AppHibernationServiceTest {
private static final int USER_ID_1 = 1; private static final int USER_ID_1 = 1;
private static final int USER_ID_2 = 2; private static final int USER_ID_2 = 2;
private final List<UserInfo> mUserInfos = new ArrayList<>();
private AppHibernationService mAppHibernationService; private AppHibernationService mAppHibernationService;
private BroadcastReceiver mBroadcastReceiver; private BroadcastReceiver mBroadcastReceiver;
@Mock @Mock
@@ -88,47 +90,42 @@ public final class AppHibernationServiceTest {
verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any()); verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue(); mBroadcastReceiver = mReceiverCaptor.getValue();
List<UserInfo> userList = new ArrayList<>(); doReturn(mUserInfos).when(mUserManager).getUsers();
userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */));
doReturn(userList).when(mUserManager).getUsers();
List<PackageInfo> userPackages = new ArrayList<>();
userPackages.add(makePackageInfo(PACKAGE_NAME_1));
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
.getInstalledPackages(anyInt(), eq(USER_ID_1));
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(), doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any()); anyInt(), anyBoolean(), anyBoolean(), any(), any());
addUser(USER_ID_1);
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
} }
@Test @Test
public void testSetHibernating_packageIsHibernating() { public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException {
// WHEN we hibernate a package for a user // WHEN we hibernate a package for a user
mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// THEN the package is marked hibernating for the user // THEN the package is marked hibernating for the user
assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
} }
@Test @Test
public void testSetHibernating_newPackageAdded_packageIsHibernating() { public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating()
throws RemoteException {
// WHEN a new package is added and it is hibernated // WHEN a new package is added and it is hibernated
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED, Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */)); Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1); intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
mBroadcastReceiver.onReceive(mContext, intent); mBroadcastReceiver.onReceive(mContext, intent);
mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true);
// THEN the new package is hibernated // THEN the new package is hibernated
assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1));
} }
@Test @Test
public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException { public void testSetHibernatingForUser_newUserAdded_packageIsHibernating()
throws RemoteException {
// WHEN a new user is added and a package from the user is hibernated // WHEN a new user is added and a package from the user is hibernated
List<PackageInfo> userPackages = new ArrayList<>(); List<PackageInfo> userPackages = new ArrayList<>();
userPackages.add(makePackageInfo(PACKAGE_NAME_1)); userPackages.add(makePackageInfo(PACKAGE_NAME_1));
@@ -138,16 +135,17 @@ public final class AppHibernationServiceTest {
intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2); intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
mBroadcastReceiver.onReceive(mContext, intent); mBroadcastReceiver.onReceive(mContext, intent);
mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
// THEN the new user's package is hibernated // THEN the new user's package is hibernated
assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
} }
@Test @Test
public void testIsHibernating_packageReplaced_stillReturnsHibernating() { public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating()
throws RemoteException {
// GIVEN a package is currently hibernated // GIVEN a package is currently hibernated
mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// WHEN the package is removed but marked as replacing // WHEN the package is removed but marked as replacing
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED, Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
@@ -157,7 +155,38 @@ public final class AppHibernationServiceTest {
mBroadcastReceiver.onReceive(mContext, intent); mBroadcastReceiver.onReceive(mContext, intent);
// THEN the package is still hibernating // THEN the package is still hibernating
assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
}
@Test
public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException {
// WHEN we hibernate a package
mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
// THEN the package is marked hibernating for the user
assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
}
/**
* Add a mock user with one package. Must be called before
* {@link AppHibernationService#onBootPhase(int)} to work properly.
*/
private void addUser(int userId) throws RemoteException {
addUser(userId, new String[]{PACKAGE_NAME_1});
}
/**
* Add a mock user with the packages specified. Must be called before
* {@link AppHibernationService#onBootPhase(int)} to work properly
*/
private void addUser(int userId, String[] packageNames) throws RemoteException {
mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */));
List<PackageInfo> userPackages = new ArrayList<>();
for (String pkgName : packageNames) {
userPackages.add(makePackageInfo(pkgName));
}
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
.getInstalledPackages(anyInt(), eq(userId));
} }
private static PackageInfo makePackageInfo(String packageName) { private static PackageInfo makePackageInfo(String packageName) {