Improve reporting of bucketing reason
Keep track of main and sub reason for bucket change
Bug: 73178753
Test: atest AppIdleHistoryTests
Change-Id: I4936281ac06046bb5ffed9f3306efa24c7fd47ab
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9cd3621..c93f405 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3860,7 +3860,7 @@
final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
if (DEBUG_STANDBY) {
Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
bucket);
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index fc4d463..a6b71b7 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -678,7 +678,7 @@
final class StandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
if (DEBUG) {
Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0d6d2bd..6550d06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -85,7 +85,7 @@
private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
- private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+ private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 740866c..017fada 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2240,7 +2240,7 @@
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
final int uid = mLocalPM.getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (uid < 0) {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index bd8fe28..ed29a4c 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -186,7 +186,8 @@
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f29e0bb..ab55553 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3947,7 +3947,8 @@
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index 90db2a3..796d364 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+
import static com.android.server.AppStateTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
@@ -612,7 +615,7 @@
// Exempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -624,7 +627,7 @@
// Exempt package 1 on user-0.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -632,7 +635,7 @@
// Unexempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_ACTIVE);
+ UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -644,9 +647,9 @@
mPowerSaveObserver.accept(getPowerSaveState());
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
setAppOps(UID_1, PACKAGE_1, true);
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 7b06648..36504ac 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,10 +16,13 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+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_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -92,18 +95,18 @@
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
// ACTIVE means not idle
assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT);
+ REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
// RARE is considered idle
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
@@ -115,7 +118,7 @@
aih = new AppIdleHistory(mStorageDir, 4000);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
@@ -133,4 +136,18 @@
assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
}
+
+ public void testReason() throws Exception {
+ AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+ aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+ assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
+ aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
+ aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_TIMEOUT);
+ aih.writeAppIdleTimes(USER_ID);
+
+ aih = new AppIdleHistory(mStorageDir, 5000);
+ assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
+ }
}
\ No newline at end of file
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 cbbdca6..edf1f74 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,11 +18,11 @@
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+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.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -410,11 +410,11 @@
setChargingState(mController, false);
// Set it to timeout or usage, so that prediction can override it
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT, 1 * HOUR_MS);
+ REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
@@ -440,28 +440,28 @@
setChargingState(mController, false);
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
// Prediction can't override NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_DEFAULT, 2 * HOUR_MS);
+ REASON_MAIN_DEFAULT, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_USAGE, 2 * HOUR_MS);
+ REASON_MAIN_USAGE, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
}
@@ -474,7 +474,7 @@
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -483,7 +483,7 @@
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -498,15 +498,15 @@
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1000);
+ REASON_MAIN_PREDICTED, 1000);
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -527,18 +527,18 @@
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
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_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -561,7 +561,7 @@
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 8e5a418..fd28b65 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,10 +16,12 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -112,8 +114,10 @@
// Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
- // Reason for setting the standby bucket. TODO: Switch to int.
- String bucketingReason;
+ // Reason for setting the standby bucket. The value here is a combination of
+ // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
+ // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
+ int bucketingReason;
// In-memory only, last bucket for which the listeners were informed
int lastInformedBucket;
// The last time a job was run for this app, using elapsed timebase
@@ -212,13 +216,14 @@
* @param appUsageHistory the usage record for the app being updated
* @param packageName name of the app being updated, for logging purposes
* @param newBucket the bucket to set the app to
+ * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
* @param elapsedRealtime mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
- int newBucket, long elapsedRealtime, long timeout) {
+ int newBucket, int usageReason, long elapsedRealtime, long timeout) {
// Set the timeout if applicable
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
@@ -246,10 +251,10 @@
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
return appUsageHistory;
}
@@ -262,16 +267,17 @@
* @param packageName
* @param userId
* @param newBucket the bucket to set the app to
- * @param elapsedRealtime mark as used time if non-zero
+ * @param usageReason sub reason for usage
+ * @param nowElapsed mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
- long nowElapsed, long timeout) {
+ int usageReason, long nowElapsed, long timeout) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
- return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
+ return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -293,7 +299,7 @@
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.lastPredictedTime = getElapsedTime(0);
appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
userHistory.put(packageName, appUsageHistory);
@@ -328,18 +334,18 @@
}
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
- int bucket, String reason) {
+ int bucket, int reason) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
- if (reason.startsWith(REASON_PREDICTED)) {
+ if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
}
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
@@ -393,11 +399,11 @@
return buckets;
}
- public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
+ public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, false);
- return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
+ return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
}
public long getElapsedTime(long elapsedRealtime) {
@@ -411,11 +417,11 @@
elapsedRealtime, true);
if (idle) {
appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = REASON_FORCED;
+ appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
} else {
appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
}
return appUsageHistory.currentBucket;
}
@@ -516,7 +522,7 @@
appUsageHistory.currentBucket = currentBucketString == null
? STANDBY_BUCKET_ACTIVE
: Integer.parseInt(currentBucketString);
- appUsageHistory.bucketingReason =
+ String bucketingReason =
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
appUsageHistory.lastJobRunTime = getLongValue(parser,
ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
@@ -524,8 +530,13 @@
ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
- if (appUsageHistory.bucketingReason == null) {
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
+ if (bucketingReason != null) {
+ try {
+ appUsageHistory.bucketingReason =
+ Integer.parseInt(bucketingReason, 16);
+ } catch (NumberFormatException nfe) {
+ }
}
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
@@ -574,7 +585,8 @@
Long.toString(history.lastPredictedTime));
xml.attribute(null, ATTR_CURRENT_BUCKET,
Integer.toString(history.currentBucket));
- xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
+ xml.attribute(null, ATTR_BUCKETING_REASON,
+ Integer.toHexString(history.bucketingReason));
if (history.bucketActiveTimeoutTime > 0) {
xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
.bucketActiveTimeoutTime));
@@ -600,7 +612,7 @@
}
public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
- idpw.println("Package idle stats:");
+ idpw.println("App Standby States:");
idpw.increaseIndent();
ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -615,24 +627,25 @@
continue;
}
idpw.print("package=" + packageName);
- idpw.print(" userId=" + userId);
- idpw.print(" lastUsedElapsed=");
+ idpw.print(" u=" + userId);
+ idpw.print(" bucket=" + appUsageHistory.currentBucket
+ + " reason="
+ + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
+ idpw.print(" used=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
- idpw.print(" lastUsedScreenOn=");
+ idpw.print(" usedScr=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
- idpw.print(" lastPredictedTime=");
+ idpw.print(" lastPred=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
- idpw.print(" bucketActiveTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
+ idpw.print(" activeLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" bucketWorkingSetTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
+ idpw.print(" wsLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" lastJobRunTime=");
+ idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
- idpw.print(" bucket=" + appUsageHistory.currentBucket
- + " reason=" + appUsageHistory.bucketingReason);
idpw.println();
}
idpw.println();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index f40aa5b..e836677 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,11 +16,20 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+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_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -129,8 +138,8 @@
STANDBY_BUCKET_RARE
};
- // Expiration time for predicted bucket
- private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
+ private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
/**
* Indicates the maximum wait time for admin data to be available;
@@ -187,6 +196,8 @@
long mNotificationSeenTimeoutMillis;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis;
+ /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
+ long mPredictionTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
@@ -223,24 +234,30 @@
// Whether the bucket change is because the user has started interacting with the app
boolean isUserInteraction;
- StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) {
+ // Reason for bucket change
+ int reason;
+
+ StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
+ boolean isInteraction) {
this.packageName = pkgName;
this.userId = userId;
this.bucket = bucket;
+ this.reason = reason;
this.isUserInteraction = isInteraction;
}
public static StandbyUpdateRecord obtain(String pkgName, int userId,
- int bucket, boolean isInteraction) {
+ int bucket, int reason, boolean isInteraction) {
synchronized (sStandbyUpdatePool) {
final int size = sStandbyUpdatePool.size();
if (size < 1) {
- return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction);
+ return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
}
StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
r.packageName = pkgName;
r.userId = userId;
r.bucket = bucket;
+ r.reason = reason;
r.isUserInteraction = isInteraction;
return r;
}
@@ -339,10 +356,11 @@
if (!packageName.equals(providerPkgName)) {
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_ACTIVE, elapsedRealtime,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+ elapsedRealtime,
elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, false);
+ appUsage.currentBucket, appUsage.bucketingReason, false);
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -497,50 +515,54 @@
if (isSpecial) {
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, false);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- String reason = app.bucketingReason;
+ int reason = app.bucketingReason;
+ final int oldMainReason = reason & REASON_MAIN_MASK;
// If the bucket was forced by the user/developer, leave it alone.
// A usage event will be the only way to bring it out of this forced state
- if (REASON_FORCED.equals(app.bucketingReason)) {
+ if (oldMainReason == REASON_MAIN_FORCED) {
return;
}
final int oldBucket = app.currentBucket;
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
boolean predictionLate = false;
// Compute age-based bucket
- if (REASON_DEFAULT.equals(app.bucketingReason)
- || REASON_USAGE.equals(app.bucketingReason)
- || REASON_TIMEOUT.equals(app.bucketingReason)
+ if (oldMainReason == REASON_MAIN_DEFAULT
+ || oldMainReason == REASON_MAIN_USAGE
+ || oldMainReason == REASON_MAIN_TIMEOUT
|| (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
newBucket = getBucketForLocked(packageName, userId,
elapsedRealtime);
if (DEBUG) {
Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
}
- reason = REASON_TIMEOUT;
+ reason = REASON_MAIN_TIMEOUT;
}
// Check if the app is within one of the timeouts for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
if (newBucket >= STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket >= STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ // If it was already there, keep the reason, else assume timeout to WS
+ reason = (newBucket == oldBucket)
+ ? app.bucketingReason
+ : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -553,43 +575,41 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket, false);
+ newBucket, reason, false);
}
}
}
}
+ /** Returns true if there hasn't been a prediction for the app in a while. */
private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
- return app.bucketingReason != null
- && app.bucketingReason.startsWith(REASON_PREDICTED)
+ return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
&& app.lastPredictedTime > 0
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
- - app.lastPredictedTime > PREDICTION_TIMEOUT;
+ - app.lastPredictedTime > mPredictionTimeoutMillis;
}
- private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
- long elapsedRealtime) {
- final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
- && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
- }
-
+ /** Inform listeners if the bucket has changed since it was last reported to listeners */
private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, int bucket, boolean userStartedInteracting) {
+ long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
synchronized (mAppIdleLock) {
- // TODO: fold these into one call + lookup for efficiency if needed
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
elapsedRealtime, bucket)) {
- StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting);
+ final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
+ bucket, reason, userStartedInteracting);
if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting)));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
}
}
}
+ /**
+ * Evaluates next bucket based on time since last used and the bucketing thresholds.
+ * @param packageName the app
+ * @param userId the user
+ * @param elapsedRealtime as the name suggests, current elapsed time
+ * @return the bucket for the app, based on time since last used
+ */
@GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
@@ -675,17 +695,20 @@
final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
event.mPackage, userId, elapsedRealtime);
final int prevBucket = appHistory.currentBucket;
- final String prevBucketReason = appHistory.bucketingReason;
+ final int prevBucketReason = appHistory.bucketingReason;
final long nextCheckTime;
+ final int subReason = usageEventToSubReason(event.mEventType);
+ final int reason = REASON_MAIN_USAGE | subReason;
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_WORKING_SET,
+ STANDBY_BUCKET_WORKING_SET, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckTime = mNotificationSeenTimeoutMillis;
+
} else {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE,
+ STANDBY_BUCKET_ACTIVE, subReason,
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckTime = mStrongUsageTimeoutMillis;
}
@@ -695,9 +718,9 @@
final boolean userStartedInteracting =
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
prevBucket != appHistory.currentBucket &&
- prevBucketReason != REASON_USAGE;
+ (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- appHistory.currentBucket, userStartedInteracting);
+ appHistory.currentBucket, reason, userStartedInteracting);
if (previouslyIdle) {
notifyBatteryStats(event.mPackage, userId, false);
@@ -706,6 +729,17 @@
}
}
+ private int usageEventToSubReason(int eventType) {
+ switch (eventType) {
+ case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+ case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
+ case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
+ case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+ default: return 0;
+ }
+ }
+
/**
* Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
* then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
@@ -731,7 +765,8 @@
userId, elapsedRealtime);
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
- maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
+ REASON_MAIN_FORCED, false);
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
@@ -962,11 +997,11 @@
}
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- String reason, long elapsedRealtime) {
+ int reason, long elapsedRealtime) {
synchronized (mAppIdleLock) {
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+ boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
@@ -979,7 +1014,7 @@
}
// If the bucket was forced, don't allow prediction to override
- if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
@@ -989,14 +1024,18 @@
if (newBucket > STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket > STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ if (app.currentBucket != newBucket) {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+ } else {
+ reason = app.bucketingReason;
+ }
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -1006,7 +1045,7 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason);
}
- maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
@VisibleForTesting
@@ -1106,11 +1145,12 @@
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, int bucket, boolean userInteraction) {
+ void informListeners(String packageName, int userId, int bucket, int reason,
+ boolean userInteraction) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
synchronized (mPackageAccessListeners) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
if (userInteraction) {
listener.onUserInteractionStarted(packageName, userId);
}
@@ -1188,7 +1228,8 @@
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
// Mark app as used for 2 hours. After that it can timeout to whatever the
// past usage pattern was.
- mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
+ mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
@@ -1369,7 +1410,8 @@
switch (msg.what) {
case MSG_INFORM_LISTENERS:
StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
- informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction);
+ informListeners(r.packageName, r.userId, r.bucket, r.reason,
+ r.isUserInteraction);
r.recycle();
break;
@@ -1477,7 +1519,7 @@
"notification_seen_duration";
private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
"system_update_usage_duration";
-
+ private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1547,6 +1589,9 @@
mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
(KEY_SYSTEM_UPDATE_HOLD_DURATION,
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
+ mPredictionTimeoutMillis = mParser.getDurationMillis
+ (KEY_PREDICTION_TIMEOUT,
+ COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dedf967..43ac58a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -120,10 +120,10 @@
new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
Event event = new Event();
event.mEventType = Event.STANDBY_BUCKET_CHANGED;
- event.mBucket = bucket;
+ event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
@@ -741,9 +741,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
// Caller cannot set their own standby state
@@ -798,9 +798,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d1ed599..5e7e80d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,10 +26,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Log;
-import android.util.LogWriter;
import java.io.IOException;
import java.net.ProtocolException;
@@ -175,7 +172,7 @@
event.mShortcutId = (id != null) ? id.intern() : null;
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+ event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
break;
}
@@ -281,8 +278,8 @@
}
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- if (event.mBucket != 0) {
- XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+ if (event.mBucketAndReason != 0) {
+ XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 8afc511..3fbcd81 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -520,7 +520,8 @@
pw.printPair("shortcutId", event.mShortcutId);
}
if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
- pw.printPair("standbyBucket", event.mBucket);
+ pw.printPair("standbyBucket", event.getStandbyBucket());
+ pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
}
pw.printHexPair("flags", event.mFlags);
pw.println();