From aa46194fea8846795644e1021b15554db53a44ac Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Mon, 16 Mar 2020 11:59:05 -0700 Subject: [PATCH] Convert system forced reasons to flags. Switching the reasons to flags allows us to track mulitple reasons for an app being in a bucket. Bug: 149507105 Test: atest FrameworksServicesTests:AppIdleHistoryTests Test: atest FrameworksServicesTests:AppStandbyControllerTests Change-Id: I6fb9e980652ac76e927fbba3d65da2b39b55f1b9 --- .../server/usage/AppStandbyInternal.java | 6 +- .../server/job/JobSchedulerService.java | 2 +- .../server/usage/AppStandbyController.java | 31 ++++++- .../android/app/usage/UsageStatsManager.java | 41 +++++----- .../server/usage/AppIdleHistoryTests.java | 11 ++- .../usage/AppStandbyControllerTests.java | 82 +++++++++++++++++++ 6 files changed, 144 insertions(+), 29 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 f8b598a43db65..6d9e3eddf6167 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -5,6 +5,7 @@ import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; +import android.app.usage.UsageStatsManager.SystemForcedReasons; import android.content.Context; import android.os.Looper; @@ -123,9 +124,10 @@ public interface AppStandbyInternal { * appropriate time. * * @param restrictReason The restrictReason for restricting the app. Should be one of the - * UsageStatsManager.REASON_SUB_RESTRICT_* reasons. + * UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_* reasons. */ - void restrictApp(@NonNull String packageName, int userId, int restrictReason); + void restrictApp(@NonNull String packageName, int userId, + @SystemForcedReasons int restrictReason); void addActiveDeviceAdmin(String adminPkg, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index fc29c9cf10355..819f253e0c19a 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -974,7 +974,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) { Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times"); mAppStandbyInternal.restrictApp( - pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY); + pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) { final boolean isDebuggable; synchronized (mLock) { 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 249bc524c379a..2b54ca3e78414 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -58,6 +58,7 @@ import android.app.AppGlobals; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; +import android.app.usage.UsageStatsManager.SystemForcedReasons; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -1153,6 +1154,13 @@ public class AppStandbyController implements AppStandbyInternal { } } + @VisibleForTesting + int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) { + synchronized (mAppIdleLock) { + return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime); + } + } + @Override public List getAppStandbyBuckets(int userId) { synchronized (mAppIdleLock) { @@ -1161,7 +1169,8 @@ public class AppStandbyController implements AppStandbyInternal { } @Override - public void restrictApp(@NonNull String packageName, int userId, int restrictReason) { + public void restrictApp(@NonNull String packageName, int userId, + @SystemForcedReasons int restrictReason) { // If the package is not installed, don't allow the bucket to be set. if (!mInjector.isPackageInstalled(packageName, 0, userId)) { Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName); @@ -1256,10 +1265,28 @@ public class AppStandbyController implements AppStandbyInternal { return; } + final boolean wasForcedBySystem = + (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM; + // If the bucket was forced, don't allow prediction to override if (predicted && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER - || (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM)) { + || wasForcedBySystem)) { + return; + } + + final boolean isForcedBySystem = + (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM; + + if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) { + mAppIdleHistory + .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason); + // Keep track of all restricting reasons + reason = REASON_MAIN_FORCED_BY_SYSTEM + | (app.bucketingReason & REASON_SUB_MASK) + | (reason & REASON_SUB_MASK); + mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, + newBucket, reason, resetTimeout); return; } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 2c701b48455cf..0d66198db0388 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -288,25 +288,25 @@ public final class UsageStatsManager { */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; /** - * The reason for restricting the app is unknown or undefined. + * The reason the system forced the app into the bucket is unknown or undefined. * @hide */ - public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000; + public static final int REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED = 0; /** * The app was unnecessarily using system resources (battery, memory, etc) in the background. * @hide */ - public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001; + public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE = 1 << 0; /** * The app was deemed to be intentionally abusive. * @hide */ - public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002; + public static final int REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE = 1 << 1; /** * The app was displaying buggy behavior. * @hide */ - public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003; + public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY = 1 << 2; /** @hide */ @@ -322,6 +322,17 @@ public final class UsageStatsManager { @Retention(RetentionPolicy.SOURCE) public @interface StandbyBuckets {} + /** @hide */ + @IntDef(flag = true, prefix = {"REASON_SUB_FORCED_SYSTEM_FLAG_FLAG_"}, value = { + REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED, + REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE, + REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, + REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SystemForcedReasons { + } + /** * Observer id of the registered observer for the group of packages that reached the usage * time limit. Included as an extra in the PendingIntent that was registered. @@ -1053,6 +1064,7 @@ public final class UsageStatsManager { /** @hide */ public static String reasonToString(int standbyReason) { + final int subReason = standbyReason & REASON_SUB_MASK; StringBuilder sb = new StringBuilder(); switch (standbyReason & REASON_MAIN_MASK) { case REASON_MAIN_DEFAULT: @@ -1060,19 +1072,8 @@ public final class UsageStatsManager { break; case REASON_MAIN_FORCED_BY_SYSTEM: sb.append("s"); - switch (standbyReason & REASON_SUB_MASK) { - case REASON_SUB_RESTRICT_ABUSE: - sb.append("-ra"); - break; - case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE: - sb.append("-rbru"); - break; - case REASON_SUB_RESTRICT_BUGGY: - sb.append("-rb"); - break; - case REASON_SUB_RESTRICT_UNDEFINED: - sb.append("-r"); - break; + if (subReason > 0) { + sb.append("-").append(Integer.toBinaryString(subReason)); } break; case REASON_MAIN_FORCED_BY_USER: @@ -1080,7 +1081,7 @@ public final class UsageStatsManager { break; case REASON_MAIN_PREDICTED: sb.append("p"); - switch (standbyReason & REASON_SUB_MASK) { + switch (subReason) { case REASON_SUB_PREDICTED_RESTORED: sb.append("-r"); break; @@ -1091,7 +1092,7 @@ public final class UsageStatsManager { break; case REASON_MAIN_USAGE: sb.append("u"); - switch (standbyReason & REASON_SUB_MASK) { + switch (subReason) { case REASON_SUB_USAGE_SYSTEM_INTERACTION: sb.append("-si"); break; diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java index 7af3ec62e6519..88f368e403a66 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -20,7 +20,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; -import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; @@ -103,7 +103,8 @@ public class AppIdleHistoryTests extends AndroidTestCase { aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE); aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED, - REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE); aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED, REASON_MAIN_FORCED_BY_USER); aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE, @@ -114,7 +115,8 @@ public class AppIdleHistoryTests extends AndroidTestCase { assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT); assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED); assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000), - REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE); assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000), REASON_MAIN_FORCED_BY_USER); @@ -133,7 +135,8 @@ public class AppIdleHistoryTests extends AndroidTestCase { assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT); assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED); assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000), - REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE); assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000), REASON_MAIN_FORCED_BY_USER); 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 387e62d7e2574..eb4eb606341a3 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -28,6 +28,10 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER; @@ -434,6 +438,11 @@ public class AppStandbyControllerTests { true); } + private int getStandbyBucketReason(String packageName) { + return mController.getAppStandbyBucketReason(packageName, USER_ID, + mInjector.mElapsedRealtime); + } + private void assertBucket(int bucket) { assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1)); } @@ -986,6 +995,79 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_FREQUENT); } + @Test + public void testSystemForcedFlags_NotAddedForUserForce() throws Exception { + final int expectedReason = REASON_MAIN_FORCED_BY_USER; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1)); + } + + @Test + public void testSystemForcedFlags_AddedForSystemForce() throws Exception { + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_DEFAULT); + mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD; + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE, + getStandbyBucketReason(PACKAGE_1)); + + mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + // Flags should be combined + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE + | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, getStandbyBucketReason(PACKAGE_1)); + } + + @Test + public void testSystemForcedFlags_SystemForceChangesBuckets() throws Exception { + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_DEFAULT); + mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD; + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE); + assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1)); + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE, + getStandbyBucketReason(PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE); + assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1)); + // Flags should be combined + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM + | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE + | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, + getStandbyBucketReason(PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + // Flags should be combined + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY, + getStandbyBucketReason(PACKAGE_1)); + + mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + // Flags should not be combined since the bucket changed. + assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED, + getStandbyBucketReason(PACKAGE_1)); + } + @Test public void testAddActiveDeviceAdmin() { assertActiveAdmins(USER_ID, (String[]) null);