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
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 f8b598a..6d9e3ed 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.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 @@
* 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 fc29c9c..819f253 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 @@
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 249bc52..2b54ca3 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.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 @@
}
}
+ @VisibleForTesting
+ int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
+ }
+ }
+
@Override
public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
synchronized (mAppIdleLock) {
@@ -1161,7 +1169,8 @@
}
@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 @@
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 2c701b4..0d66198 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -288,25 +288,25 @@
*/
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 @@
@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 @@
/** @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 @@
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 @@
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 @@
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 7af3ec6..88f368e 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_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 @@
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 @@
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 @@
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 387e62d..eb4eb60 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_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 @@
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));
}
@@ -987,6 +996,79 @@
}
@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);
assertActiveAdmins(USER_ID2, (String[]) null);