Move AppHibernationServices dependencies to injector

Similar to other system services, we move AppHibernationService's
dependencies to an injector class that can be mocked for testing so that
we minimize side-effects in unit tests

Also, add @GuardedBy annotations instead of having an L at the end of
method names since this is exactly what the annotation is for.

Bug: 175829330
Test: atest AppHibernationServiceTest
Change-Id: Iec51a349ff898923bacf6040c3ebccefa8831a68
This commit is contained in:
Kevin Han
2021-02-01 13:29:50 -08:00
parent 89575897ca
commit 6669dec038
2 changed files with 95 additions and 28 deletions

View File

@@ -88,19 +88,16 @@ public final class AppHibernationService extends SystemService {
* @param context The system server context.
*/
public AppHibernationService(@NonNull Context context) {
this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
ActivityManager.getService(),
context.getSystemService(UserManager.class));
this(new InjectorImpl(context));
}
@VisibleForTesting
AppHibernationService(@NonNull Context context, IPackageManager packageManager,
IActivityManager activityManager, UserManager userManager) {
super(context);
mContext = context;
mIPackageManager = packageManager;
mIActivityManager = activityManager;
mUserManager = userManager;
AppHibernationService(@NonNull Injector injector) {
super(injector.getContext());
mContext = injector.getContext();
mIPackageManager = injector.getPackageManager();
mIActivityManager = injector.getActivityManager();
mUserManager = injector.getUserManager();
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
@@ -182,9 +179,9 @@ public final class AppHibernationService extends SystemService {
}
if (isHibernating) {
hibernatePackageForUserL(packageName, userId, pkgState);
hibernatePackageForUser(packageName, userId, pkgState);
} else {
unhibernatePackageForUserL(packageName, userId, pkgState);
unhibernatePackageForUser(packageName, userId, pkgState);
}
}
}
@@ -200,9 +197,9 @@ public final class AppHibernationService extends SystemService {
if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
synchronized (mLock) {
if (isHibernating) {
hibernatePackageGloballyL(packageName);
hibernatePackageGlobally(packageName);
} else {
unhibernatePackageGloballyL(packageName);
unhibernatePackageGlobally(packageName);
}
}
}
@@ -210,11 +207,11 @@ public final class AppHibernationService extends SystemService {
/**
* 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,
@GuardedBy("mLock")
private void hibernatePackageForUser(@NonNull String packageName, int userId,
@NonNull UserPackageState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
@@ -233,11 +230,12 @@ public final class AppHibernationService extends SystemService {
}
/**
* Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
* Remove a package from hibernation for a given user.
*
* @param pkgState package hibernation state
*/
private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
@GuardedBy("mLock")
private void unhibernatePackageForUser(@NonNull String packageName, int userId,
UserPackageState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
final long caller = Binder.clearCallingIdentity();
@@ -255,9 +253,9 @@ public final class AppHibernationService extends SystemService {
/**
* 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) {
@GuardedBy("mLock")
private void hibernatePackageGlobally(@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);
@@ -265,21 +263,22 @@ public final class AppHibernationService extends SystemService {
}
/**
* Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
* Unhibernate a package from global hibernation.
*/
private void unhibernatePackageGloballyL(@NonNull String packageName) {
@GuardedBy("mLock")
private void unhibernatePackageGlobally(@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
* {@link #mLock}.
* Populates {@link #mUserStates} with the users installed packages.
*
* @param userId user id to add installed packages for
*/
private void addUserPackageStatesL(int userId) {
@GuardedBy("mLock")
private void addUserPackageStates(int userId) {
Map<String, UserPackageState> packages = new ArrayMap<>();
List<PackageInfo> packageList;
try {
@@ -298,7 +297,7 @@ public final class AppHibernationService extends SystemService {
public void onUserUnlocking(@NonNull TargetUser user) {
// TODO: Pull from persistent disk storage. For now, just make from scratch.
synchronized (mLock) {
addUserPackageStatesL(user.getUserIdentifier());
addUserPackageStates(user.getUserIdentifier());
}
}
@@ -431,4 +430,45 @@ public final class AppHibernationService extends SystemService {
public boolean hibernated;
// TODO: Track whether hibernation is exempted by the user
}
/**
* Dependency injector for {@link #AppHibernationService)}.
*/
interface Injector {
Context getContext();
IPackageManager getPackageManager();
IActivityManager getActivityManager();
UserManager getUserManager();
}
private static final class InjectorImpl implements Injector {
private final Context mContext;
InjectorImpl(Context context) {
mContext = context;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public IPackageManager getPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
@Override
public IActivityManager getActivityManager() {
return ActivityManager.getService();
}
@Override
public UserManager getUserManager() {
return mContext.getSystemService(UserManager.class);
}
}
}

View File

@@ -83,8 +83,7 @@ public final class AppHibernationServiceTest {
MockitoAnnotations.initMocks(this);
doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
mIActivityManager, mUserManager);
mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
@@ -187,4 +186,32 @@ public final class AppHibernationServiceTest {
pkg.packageName = packageName;
return pkg;
}
private class MockInjector implements AppHibernationService.Injector {
private final Context mContext;
MockInjector(Context context) {
mContext = context;
}
@Override
public IActivityManager getActivityManager() {
return mIActivityManager;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public IPackageManager getPackageManager() {
return mIPackageManager;
}
@Override
public UserManager getUserManager() {
return mUserManager;
}
}
}