From 84cd7b7a9e5ad6a604c075bc620f6bd9ab6b1486 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Tue, 7 Nov 2017 13:59:37 -0800 Subject: [PATCH] Allow standby timeouts to occur after usage And inform listeners when the bucket changes, not just when going in and out of RARE bucket. Avoid redundant callbacks when informing listeners. Bug: 63527785 Test: runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java Change-Id: Icd98d59f597147fbf8ea4bf44edf4b3b3d5c8e14 --- .../app/usage/UsageStatsManagerInternal.java | 10 +++- .../server/job/controllers/AppIdleController.java | 2 +- .../server/net/NetworkPolicyManagerService.java | 2 +- .../android/server/usage/AppIdleHistoryTests.java | 10 ++-- .../server/usage/AppStandbyControllerTests.java | 28 +++++---- .../com/android/server/usage/AppIdleHistory.java | 9 ++- .../android/server/usage/AppStandbyController.java | 69 ++++++++++++---------- 7 files changed, 78 insertions(+), 52 deletions(-) diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index dbaace2f0ac9..29e7439f69e2 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -118,7 +118,15 @@ public abstract class UsageStatsManagerInternal { AppIdleStateChangeListener listener); public static abstract class AppIdleStateChangeListener { - public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle); + + /** Callback to inform listeners that the idle state has changed to a new bucket. */ + public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle, + int bucket); + + /** + * Callback to inform listeners that the parole state has changed. This means apps are + * allowed to do work even if they're idle or in a low bucket. + */ public abstract void onParoleStateChanged(boolean isParoleOn); } 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 39f2a96b30e3..caa8522089c6 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -174,7 +174,7 @@ public final class AppIdleController extends StateController { private final class AppIdleStateChangeListener extends UsageStatsManagerInternal.AppIdleStateChangeListener { @Override - public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { + public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) { 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 551cb10ffc03..3fa3cd49d393 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3746,7 +3746,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { extends UsageStatsManagerInternal.AppIdleStateChangeListener { @Override - public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { + public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) { try { final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); 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 67ffe5847cbc..39d256a21f16 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -16,16 +16,14 @@ package com.android.server.usage; -import static android.app.usage.AppStandby.REASON_TIMEOUT; -import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE; -import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE; - import android.app.usage.AppStandby; import android.os.FileUtils; import android.test.AndroidTestCase; import java.io.File; +import static android.app.usage.AppStandby.*; + public class AppIdleHistoryTests extends AndroidTestCase { File mStorageDir; @@ -111,5 +109,9 @@ public class AppIdleHistoryTests extends AndroidTestCase { 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); + + assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); + assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); + assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT)); } } \ 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 9846d6f6f346..8531baf5f329 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -16,11 +16,7 @@ package com.android.server.usage; -import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE; -import static android.app.usage.AppStandby.STANDBY_BUCKET_FREQUENT; -import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE; -import static android.app.usage.AppStandby.STANDBY_BUCKET_WORKING_SET; - +import static android.app.usage.AppStandby.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -251,10 +247,22 @@ public class AppStandbyControllerTests { false)); } + private void reportEvent(AppStandbyController controller, long elapsedTime) { + // Back to ACTIVE on event + UsageEvents.Event ev = new UsageEvents.Event(); + ev.mPackage = PACKAGE_1; + ev.mEventType = UsageEvents.Event.USER_INTERACTION; + controller.reportEvent(ev, elapsedTime, USER_ID); + } + @Test public void testBuckets() throws Exception { AppStandbyController controller = setupController(); + assertTimeout(controller, 0, STANDBY_BUCKET_NEVER); + + reportEvent(controller, 0); + // ACTIVE bucket assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE); @@ -270,11 +278,7 @@ public class AppStandbyControllerTests { // RARE bucket assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE); - // Back to ACTIVE on event - UsageEvents.Event ev = new UsageEvents.Event(); - ev.mPackage = PACKAGE_1; - ev.mEventType = UsageEvents.Event.USER_INTERACTION; - controller.reportEvent(ev, mInjector.mElapsedRealtime, USER_ID); + reportEvent(controller, 9 * DAY_MS); assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE); @@ -287,6 +291,10 @@ public class AppStandbyControllerTests { AppStandbyController controller = setupController(); mInjector.setDisplayOn(false); + assertTimeout(controller, 0, STANDBY_BUCKET_NEVER); + + reportEvent(controller, 0); + // ACTIVE bucket assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE); diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index e5d3915dde4c..c5ca33041f41 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -103,7 +103,7 @@ public class AppIdleHistory { long lastUsedScreenTime; @StandbyBuckets int currentBucket; String bucketingReason; - int lastInformedState; + int lastInformedBucket; } AppIdleHistory(File storageDir, long elapsedRealtime) { @@ -333,13 +333,12 @@ public class AppIdleHistory { } boolean shouldInformListeners(String packageName, int userId, - long elapsedRealtime, boolean isIdle) { + long elapsedRealtime, int bucket) { ArrayMap userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); - int targetState = isIdle? STATE_IDLE : STATE_ACTIVE; - if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) { - appUsageHistory.lastInformedState = targetState; + if (appUsageHistory.lastInformedBucket != bucket) { + appUsageHistory.lastInformedBucket = bucket; return true; } return false; diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 17fde57907d7..5623a68f158d 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -160,7 +160,6 @@ public class AppStandbyController { private final Context mContext; // TODO: Provide a mechanism to set an external bucketing service - private boolean mUseInternalBucketingHeuristics = true; private AppWidgetManager mAppWidgetManager; private PowerManager mPowerManager; @@ -367,29 +366,33 @@ public class AppStandbyController { Slog.d(TAG, " Checking idle state for " + packageName); } if (isSpecial) { - maybeInformListeners(packageName, userId, elapsedRealtime, false); - } else if (mUseInternalBucketingHeuristics) { + maybeInformListeners(packageName, userId, elapsedRealtime, + AppStandby.STANDBY_BUCKET_ACTIVE); + } else { synchronized (mAppIdleLock) { - int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, - elapsedRealtime); String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime); - if (bucketingReason != null - && (bucketingReason.equals(AppStandby.REASON_FORCED) - || bucketingReason.startsWith(AppStandby.REASON_PREDICTED))) { + // If the bucket was forced by the developer, leave it alone + if (AppStandby.REASON_FORCED.equals(bucketingReason)) { continue; } - int newBucket = getBucketForLocked(packageName, userId, - elapsedRealtime); - if (DEBUG) { - Slog.d(TAG, " Old bucket=" + oldBucket - + ", newBucket=" + newBucket); - } - if (oldBucket != newBucket) { - mAppIdleHistory.setAppStandbyBucket(packageName, userId, - elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT); - maybeInformListeners(packageName, userId, elapsedRealtime, - newBucket >= AppStandby.STANDBY_BUCKET_RARE); + // If the bucket was moved up due to usage, let the timeouts apply. + if (AppStandby.REASON_USAGE.equals(bucketingReason) + || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) { + int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId, + elapsedRealtime); + int newBucket = getBucketForLocked(packageName, userId, + elapsedRealtime); + if (DEBUG) { + Slog.d(TAG, " Old bucket=" + oldBucket + + ", newBucket=" + newBucket); + } + if (oldBucket < newBucket) { + mAppIdleHistory.setAppStandbyBucket(packageName, userId, + elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT); + maybeInformListeners(packageName, userId, elapsedRealtime, + newBucket); + } } } } @@ -403,12 +406,12 @@ public class AppStandbyController { } private void maybeInformListeners(String packageName, int userId, - long elapsedRealtime, boolean isIdle) { + long elapsedRealtime, int bucket) { synchronized (mAppIdleLock) { if (mAppIdleHistory.shouldInformListeners(packageName, userId, - elapsedRealtime, isIdle)) { + elapsedRealtime, bucket)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, - userId, isIdle ? 1 : 0, packageName)); + userId, bucket, packageName)); } } } @@ -461,11 +464,13 @@ public class AppStandbyController { if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle); boolean paroled = false; synchronized (mAppIdleLock) { - final long timeSinceLastParole = mInjector.currentTimeMillis() - mLastAppIdleParoledTime; + final long timeSinceLastParole = + mInjector.currentTimeMillis() - mLastAppIdleParoledTime; if (!deviceIdle && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { if (DEBUG) { - Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); + Slog.i(TAG, + "Bringing idle apps out of inactive state due to deviceIdleMode=false"); } paroled = true; } else if (deviceIdle) { @@ -491,7 +496,8 @@ public class AppStandbyController { || event.mEventType == UsageEvents.Event.USER_INTERACTION)) { mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime); if (previouslyIdle) { - maybeInformListeners(event.mPackage, userId, elapsedRealtime, false); + maybeInformListeners(event.mPackage, userId, elapsedRealtime, + AppStandby.STANDBY_BUCKET_ACTIVE); notifyBatteryStats(event.mPackage, userId, false); } } @@ -729,7 +735,8 @@ public class AppStandbyController { void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, String reason, long elapsedRealtime) { - mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason); + mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, + reason); } private boolean isActiveDeviceAdmin(String packageName, int userId) { @@ -786,9 +793,10 @@ public class AppStandbyController { return packageName != null && packageName.equals(activeScorer); } - void informListeners(String packageName, int userId, boolean isIdle) { + void informListeners(String packageName, int userId, int bucket) { + final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE; for (AppIdleStateChangeListener listener : mPackageAccessListeners) { - listener.onAppIdleStateChanged(packageName, userId, isIdle); + listener.onAppIdleStateChanged(packageName, userId, idle, bucket); } } @@ -1037,7 +1045,7 @@ public class AppStandbyController { public void handleMessage(Message msg) { switch (msg.what) { case MSG_INFORM_LISTENERS: - informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1); + informListeners((String) msg.obj, msg.arg1, msg.arg2); break; case MSG_FORCE_IDLE_STATE: @@ -1187,7 +1195,8 @@ public class AppStandbyController { mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue, SCREEN_TIME_THRESHOLDS); - String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null); + String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, + null); mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue, ELAPSED_TIME_THRESHOLDS); } -- 2.11.0