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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user