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