Merge "Move disk reads to background"
This commit is contained in:
@@ -69,6 +69,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
@@ -101,6 +102,7 @@ public final class AppHibernationService extends SystemService {
|
|||||||
private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
|
private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
|
||||||
private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
|
private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
|
||||||
private final Injector mInjector;
|
private final Injector mInjector;
|
||||||
|
private final Executor mBackgroundExecutor;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean mIsServiceEnabled;
|
boolean mIsServiceEnabled;
|
||||||
@@ -126,6 +128,7 @@ public final class AppHibernationService extends SystemService {
|
|||||||
mIActivityManager = injector.getActivityManager();
|
mIActivityManager = injector.getActivityManager();
|
||||||
mUserManager = injector.getUserManager();
|
mUserManager = injector.getUserManager();
|
||||||
mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
|
mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
|
||||||
|
mBackgroundExecutor = injector.getBackgroundExecutor();
|
||||||
mInjector = injector;
|
mInjector = injector;
|
||||||
|
|
||||||
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
|
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
|
||||||
@@ -147,11 +150,13 @@ public final class AppHibernationService extends SystemService {
|
|||||||
@Override
|
@Override
|
||||||
public void onBootPhase(int phase) {
|
public void onBootPhase(int phase) {
|
||||||
if (phase == PHASE_BOOT_COMPLETED) {
|
if (phase == PHASE_BOOT_COMPLETED) {
|
||||||
|
mBackgroundExecutor.execute(() -> {
|
||||||
List<GlobalLevelState> states =
|
List<GlobalLevelState> states =
|
||||||
mGlobalLevelHibernationDiskStore.readHibernationStates();
|
mGlobalLevelHibernationDiskStore.readHibernationStates();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
initializeGlobalHibernationStates(states);
|
initializeGlobalHibernationStates(states);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
|
||||||
mIsServiceEnabled = isAppHibernationEnabled();
|
mIsServiceEnabled = isAppHibernationEnabled();
|
||||||
@@ -170,16 +175,15 @@ public final class AppHibernationService extends SystemService {
|
|||||||
* @return true if package is hibernating for the user
|
* @return true if package is hibernating for the user
|
||||||
*/
|
*/
|
||||||
boolean isHibernatingForUser(String packageName, int userId) {
|
boolean isHibernatingForUser(String packageName, int userId) {
|
||||||
if (!checkHibernationEnabled("isHibernatingForUser")) {
|
String methodName = "isHibernatingForUser";
|
||||||
|
if (!checkHibernationEnabled(methodName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
getContext().enforceCallingOrSelfPermission(
|
getContext().enforceCallingOrSelfPermission(
|
||||||
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
||||||
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
||||||
userId = handleIncomingUser(userId, "isHibernating");
|
userId = handleIncomingUser(userId, methodName);
|
||||||
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
|
if (!checkUserStatesExist(userId, methodName)) {
|
||||||
Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
|
|
||||||
+ userId);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -225,16 +229,15 @@ public final class AppHibernationService extends SystemService {
|
|||||||
* @param isHibernating new hibernation state
|
* @param isHibernating new hibernation state
|
||||||
*/
|
*/
|
||||||
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
|
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
|
||||||
if (!checkHibernationEnabled("setHibernatingForUser")) {
|
String methodName = "setHibernatingForUser";
|
||||||
|
if (!checkHibernationEnabled(methodName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getContext().enforceCallingOrSelfPermission(
|
getContext().enforceCallingOrSelfPermission(
|
||||||
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
||||||
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
||||||
userId = handleIncomingUser(userId, "setHibernating");
|
userId = handleIncomingUser(userId, methodName);
|
||||||
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
|
if (!checkUserStatesExist(userId, methodName)) {
|
||||||
Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
|
|
||||||
+ userId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -298,16 +301,15 @@ public final class AppHibernationService extends SystemService {
|
|||||||
*/
|
*/
|
||||||
@NonNull List<String> getHibernatingPackagesForUser(int userId) {
|
@NonNull List<String> getHibernatingPackagesForUser(int userId) {
|
||||||
ArrayList<String> hibernatingPackages = new ArrayList<>();
|
ArrayList<String> hibernatingPackages = new ArrayList<>();
|
||||||
if (!checkHibernationEnabled("getHibernatingPackagesForUser")) {
|
String methodName = "getHibernatingPackagesForUser";
|
||||||
|
if (!checkHibernationEnabled(methodName)) {
|
||||||
return hibernatingPackages;
|
return hibernatingPackages;
|
||||||
}
|
}
|
||||||
getContext().enforceCallingOrSelfPermission(
|
getContext().enforceCallingOrSelfPermission(
|
||||||
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
android.Manifest.permission.MANAGE_APP_HIBERNATION,
|
||||||
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
"Caller does not have MANAGE_APP_HIBERNATION permission.");
|
||||||
userId = handleIncomingUser(userId, "getHibernatingPackagesForUser");
|
userId = handleIncomingUser(userId, methodName);
|
||||||
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
|
if (!checkUserStatesExist(userId, methodName)) {
|
||||||
Slog.w(TAG, "Attempt to get hibernating packages for a stopped or nonexistent user "
|
|
||||||
+ userId);
|
|
||||||
return hibernatingPackages;
|
return hibernatingPackages;
|
||||||
}
|
}
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -477,11 +479,16 @@ public final class AppHibernationService extends SystemService {
|
|||||||
HibernationStateDiskStore<UserLevelState> diskStore =
|
HibernationStateDiskStore<UserLevelState> diskStore =
|
||||||
mInjector.getUserLevelDiskStore(userId);
|
mInjector.getUserLevelDiskStore(userId);
|
||||||
mUserDiskStores.put(userId, diskStore);
|
mUserDiskStores.put(userId, diskStore);
|
||||||
|
mBackgroundExecutor.execute(() -> {
|
||||||
List<UserLevelState> storedStates = diskStore.readHibernationStates();
|
List<UserLevelState> storedStates = diskStore.readHibernationStates();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
|
// Ensure user hasn't stopped in the time to execute.
|
||||||
|
if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
|
||||||
initializeUserHibernationStates(userId, storedStates);
|
initializeUserHibernationStates(userId, storedStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUserStopping(@NonNull TargetUser user) {
|
public void onUserStopping(@NonNull TargetUser user) {
|
||||||
@@ -550,6 +557,20 @@ public final class AppHibernationService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkUserStatesExist(int userId, String methodName) {
|
||||||
|
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
|
||||||
|
Slog.e(TAG, String.format(
|
||||||
|
"Attempt to call %s on stopped or nonexistent user %d", methodName, userId));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mUserStates.contains(userId)) {
|
||||||
|
Slog.w(TAG, String.format(
|
||||||
|
"Attempt to call %s before states have been read from disk", methodName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkHibernationEnabled(String methodName) {
|
private boolean checkHibernationEnabled(String methodName) {
|
||||||
if (!mIsServiceEnabled) {
|
if (!mIsServiceEnabled) {
|
||||||
Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
|
Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
|
||||||
@@ -720,6 +741,8 @@ public final class AppHibernationService extends SystemService {
|
|||||||
|
|
||||||
UserManager getUserManager();
|
UserManager getUserManager();
|
||||||
|
|
||||||
|
Executor getBackgroundExecutor();
|
||||||
|
|
||||||
HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
|
HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
|
||||||
|
|
||||||
HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
|
HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
|
||||||
@@ -757,6 +780,11 @@ public final class AppHibernationService extends SystemService {
|
|||||||
return mContext.getSystemService(UserManager.class);
|
return mContext.getSystemService(UserManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Executor getBackgroundExecutor() {
|
||||||
|
return mScheduledExecutorService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
|
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
|
||||||
File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
|
File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class HibernationStateDiskStore<T> {
|
|||||||
* @return the parsed list of hibernation states, null if file does not exist
|
* @return the parsed list of hibernation states, null if file does not exist
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@WorkerThread
|
||||||
List<T> readHibernationStates() {
|
List<T> readHibernationStates() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!mHibernationFile.exists()) {
|
if (!mHibernationFile.exists()) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link com.android.server.apphibernation.AppHibernationService}
|
* Tests for {@link com.android.server.apphibernation.AppHibernationService}
|
||||||
@@ -116,8 +117,8 @@ public final class AppHibernationServiceTest {
|
|||||||
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
|
||||||
|
|
||||||
UserInfo userInfo = addUser(USER_ID_1);
|
UserInfo userInfo = addUser(USER_ID_1);
|
||||||
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
|
|
||||||
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
|
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
|
||||||
|
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
|
||||||
|
|
||||||
mAppHibernationService.mIsServiceEnabled = true;
|
mAppHibernationService.mIsServiceEnabled = true;
|
||||||
}
|
}
|
||||||
@@ -150,8 +151,8 @@ public final class AppHibernationServiceTest {
|
|||||||
throws RemoteException {
|
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
|
||||||
UserInfo user2 = addUser(USER_ID_2);
|
UserInfo user2 = addUser(USER_ID_2);
|
||||||
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
|
|
||||||
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
|
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
|
||||||
|
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
|
||||||
mAppHibernationService.setHibernatingForUser(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
|
||||||
@@ -188,8 +189,8 @@ public final class AppHibernationServiceTest {
|
|||||||
// GIVEN an unlocked user with all packages installed
|
// GIVEN an unlocked user with all packages installed
|
||||||
UserInfo userInfo =
|
UserInfo userInfo =
|
||||||
addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
|
addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
|
||||||
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
|
|
||||||
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
|
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
|
||||||
|
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
|
||||||
|
|
||||||
// WHEN packages are hibernated for the user
|
// WHEN packages are hibernated for the user
|
||||||
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
|
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
|
||||||
@@ -258,6 +259,12 @@ public final class AppHibernationServiceTest {
|
|||||||
return mUserManager;
|
return mUserManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Executor getBackgroundExecutor() {
|
||||||
|
// Just execute immediately in tests.
|
||||||
|
return r -> r.run();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
|
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
|
||||||
return Mockito.mock(HibernationStateDiskStore.class);
|
return Mockito.mock(HibernationStateDiskStore.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user