diff options
4 files changed, 100 insertions, 89 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 041825c235d0..6109b713de24 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -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); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 2f8b5130edb0..58eb58961ac4 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -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 { } } + @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 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, long elapsedRealtime) { - setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false); + int reason) { + setAppStandbyBucket( + packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false); } - @Override - public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, + 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); } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 12ba219d0365..1dd7e64690c7 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -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 diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index c900f386b438..8397aa485595 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -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); } |