Move setAppStandby logic inside AppStandbyController.

Since we've moved AppStandbyController inside the JobScheduler mainline
module boundary, we need to move the logic of setAppStandby over so that
it's consistent for callers and easy to do the right thing.

Bug: 137763703
Bug: 145551233
Test: atest AppIdleHistoryTests
Test: atest AppStandbyControllerTests
Test: atest UsageStatsTest
Change-Id: Ie2ccb0508c3a3887ab3debc47e78427477d2d77b
This commit is contained in:
Kweku Adams
2020-01-08 11:14:39 -08:00
parent 2a4b15a30f
commit c4ee998be8
4 changed files with 101 additions and 90 deletions

View File

@@ -1,5 +1,6 @@
package com.android.server.usage;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
@@ -99,8 +100,18 @@ public interface AppStandbyInternal {
List<AppStandbyInfo> getAppStandbyBuckets(int userId);
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime, boolean resetTimeout);
/**
* Changes an app's standby bucket to the provided value. The caller can only set the standby
* bucket for a different app than itself.
*/
void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid,
int callingPid);
/**
* Changes the app standby bucket for multiple apps at once.
*/
void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid,
int callingPid);
void addActiveDeviceAdmin(String adminPkg, int userId);

View File

@@ -47,6 +47,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -101,7 +102,6 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.File;
import java.io.PrintWriter;
@@ -109,6 +109,7 @@ import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -1014,14 +1015,57 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
@VisibleForTesting
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime) {
setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false);
@Override
public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
int callingUid, int callingPid) {
setAppStandbyBuckets(
Collections.singletonList(new AppStandbyInfo(packageName, bucket)),
userId, callingUid, callingPid);
}
@Override
public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId,
int callingUid, int callingPid) {
userId = ActivityManager.handleIncomingUser(
callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
final boolean shellCaller = callingUid == Process.ROOT_UID
|| callingUid == Process.SHELL_UID;
final boolean systemCaller = UserHandle.isCore(callingUid);
final int reason = systemCaller ? REASON_MAIN_FORCED : REASON_MAIN_PREDICTED;
final int packageFlags = PackageManager.MATCH_ANY_USER
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE;
final int numApps = appBuckets.size();
final long elapsedRealtime = mInjector.elapsedRealtime();
for (int i = 0; i < numApps; ++i) {
final AppStandbyInfo bucketInfo = appBuckets.get(i);
final String packageName = bucketInfo.mPackageName;
final int bucket = bucketInfo.mStandbyBucket;
if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) {
throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
}
final int packageUid = mInjector.getPackageManagerInternal()
.getPackageUid(packageName, packageFlags, userId);
// Caller cannot set their own standby state
if (packageUid == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
}
if (packageUid < 0) {
throw new IllegalArgumentException(
"Cannot set standby bucket for non existent package (" + packageName + ")");
}
setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller);
}
}
@VisibleForTesting
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason) {
setAppStandbyBucket(
packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false);
}
private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime, boolean resetTimeout) {
synchronized (mAppIdleLock) {
// If the package is not installed, don't allow the bucket to be set.
@@ -1444,6 +1488,10 @@ public class AppStandbyController implements AppStandbyInternal {
mBatteryStats.noteEvent(event, packageName, uid);
}
PackageManagerInternal getPackageManagerInternal() {
return mPackageManagerInternal;
}
boolean isPackageEphemeral(int userId, String packageName) {
return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
}

View File

@@ -365,8 +365,9 @@ public class AppStandbyControllerTests {
public void testSetAppStandbyBucket() throws Exception {
// For a known package, standby bucket should be set properly
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_TIMEOUT, HOUR_MS);
REASON_MAIN_TIMEOUT);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// For an unknown package, standby bucket should not be set, hence NEVER is returned
@@ -374,7 +375,7 @@ public class AppStandbyControllerTests {
mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
isPackageInstalled = false; // Mock package is not installed
mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_TIMEOUT, HOUR_MS);
REASON_MAIN_TIMEOUT);
isPackageInstalled = true; // Reset mocked variable for other tests
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
}
@@ -468,12 +469,13 @@ public class AppStandbyControllerTests {
@Test
public void testPredictionTimedout() throws Exception {
// Set it to timeout or usage, so that prediction can override it
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT, HOUR_MS);
REASON_MAIN_TIMEOUT);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED, HOUR_MS);
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// Fast forward 12 hours
@@ -497,29 +499,31 @@ public class AppStandbyControllerTests {
@Test
public void testOverrides() throws Exception {
// Can force to NEVER
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED, 1 * HOUR_MS);
REASON_MAIN_FORCED);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_FORCED, 1 * HOUR_MS);
REASON_MAIN_FORCED);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED, 1 * HOUR_MS);
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override NEVER
mInjector.mElapsedRealtime = 2 * HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_DEFAULT, 2 * HOUR_MS);
REASON_MAIN_DEFAULT);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE, 2 * HOUR_MS);
REASON_MAIN_USAGE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
@@ -530,7 +534,7 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -554,15 +558,17 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED, 1000);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
mInjector.mElapsedRealtime = 2000 + mController.mStrongUsageTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = 2000 + mController.mNotificationSeenTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -582,18 +588,18 @@ public class AppStandbyControllerTests {
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -625,7 +631,7 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
// Make sure app is in NEVER bucket
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_FORCED, mInjector.mElapsedRealtime);
REASON_MAIN_FORCED);
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_NEVER);
@@ -670,7 +676,7 @@ public class AppStandbyControllerTests {
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction
@@ -687,7 +693,7 @@ public class AppStandbyControllerTests {
// Predict to FREQUENT
mInjector.mElapsedRealtime = RARE_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_FREQUENT);
// Add a short timeout event

View File

@@ -1568,44 +1568,16 @@ public class UsageStatsService extends SystemService implements
}
@Override
public void setAppStandbyBucket(String packageName,
int bucket, int userId) {
public void setAppStandbyBucket(String packageName, int bucket, int userId) {
getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
"No permission to change app standby state");
if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
|| bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
}
final int callingUid = Binder.getCallingUid();
try {
userId = ActivityManager.getService().handleIncomingUser(
Binder.getCallingPid(), callingUid, userId, false, true,
"setAppStandbyBucket", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
final boolean systemCaller = UserHandle.isCore(callingUid);
final int reason = systemCaller
? UsageStatsManager.REASON_MAIN_FORCED
: UsageStatsManager.REASON_MAIN_PREDICTED;
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE, userId);
// Caller cannot set their own standby state
if (packageUid == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
}
if (packageUid < 0) {
throw new IllegalArgumentException(
"Cannot set standby bucket for non existent package (" + packageName
+ ")");
}
mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
SystemClock.elapsedRealtime(), shellCaller);
mAppStandby.setAppStandbyBucket(packageName, bucket, userId,
callingUid, callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1643,37 +1615,11 @@ public class UsageStatsService extends SystemService implements
"No permission to change app standby state");
final int callingUid = Binder.getCallingUid();
try {
userId = ActivityManager.getService().handleIncomingUser(
Binder.getCallingPid(), callingUid, userId, false, true,
"setAppStandbyBucket", null);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
final int reason = shellCaller
? UsageStatsManager.REASON_MAIN_FORCED
: UsageStatsManager.REASON_MAIN_PREDICTED;
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
final long elapsedRealtime = SystemClock.elapsedRealtime();
List<AppStandbyInfo> bucketList = appBuckets.getList();
for (AppStandbyInfo bucketInfo : bucketList) {
final String packageName = bucketInfo.mPackageName;
final int bucket = bucketInfo.mStandbyBucket;
if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
|| bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
throw new IllegalArgumentException(
"Cannot set the standby bucket to " + bucket);
}
// Caller cannot set their own standby state
if (mPackageManagerInternal.getPackageUid(packageName,
PackageManager.MATCH_ANY_USER, userId) == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
}
mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
elapsedRealtime, shellCaller);
}
mAppStandby.setAppStandbyBuckets(appBuckets.getList(), userId,
callingUid, callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}