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 13a2a946ffcc9..9909764edc377 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -965,17 +965,21 @@ public class AppStandbyController Slog.d(TAG, " Checking idle state for " + packageName + " minBucket=" + standbyBucketToString(minBucket)); } + final boolean previouslyIdle, stillIdle; if (minBucket <= STANDBY_BUCKET_ACTIVE) { // No extra processing needed for ACTIVE or higher since apps can't drop into lower // buckets. synchronized (mAppIdleLock) { + previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, minBucket, REASON_MAIN_DEFAULT); + stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); } maybeInformListeners(packageName, userId, elapsedRealtime, minBucket, REASON_MAIN_DEFAULT, false); } else { synchronized (mAppIdleLock) { + previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); final AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime); @@ -1073,11 +1077,17 @@ public class AppStandbyController if (oldBucket != newBucket || predictionLate) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason); + stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false); + } else { + stillIdle = previouslyIdle; } } } + if (previouslyIdle != stillIdle) { + notifyBatteryStats(packageName, userId, stillIdle); + } } /** Returns true if there hasn't been a prediction for the app in a while. */ @@ -1234,8 +1244,9 @@ public class AppStandbyController appHistory.currentBucket, reason, userStartedInteracting); } - if (previouslyIdle) { - notifyBatteryStats(pkg, userId, false); + final boolean stillIdle = appHistory.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF; + if (previouslyIdle != stillIdle) { + notifyBatteryStats(pkg, userId, stillIdle); } } @@ -1808,8 +1819,14 @@ public class AppStandbyController reason = REASON_MAIN_FORCED_BY_SYSTEM | (app.bucketingReason & REASON_SUB_MASK) | (reason & REASON_SUB_MASK); + final boolean previouslyIdle = + app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF; mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, resetTimeout); + final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF; + if (previouslyIdle != stillIdle) { + notifyBatteryStats(packageName, userId, stillIdle); + } return; } @@ -1910,8 +1927,13 @@ public class AppStandbyController // Make sure we don't put the app in a lower bucket than it's supposed to be in. newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId)); + final boolean previouslyIdle = app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF; mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, resetTimeout); + final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF; + if (previouslyIdle != stillIdle) { + notifyBatteryStats(packageName, userId, stillIdle); + } } maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false); } @@ -2668,7 +2690,9 @@ public class AppStandbyController } void noteEvent(int event, String packageName, int uid) throws RemoteException { - mBatteryStats.noteEvent(event, packageName, uid); + if (mBatteryStats != null) { + mBatteryStats.noteEvent(event, packageName, uid); + } } PackageManagerInternal getPackageManagerInternal() { 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 dcf1b35068c66..9570ff6323d28 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -87,6 +87,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.hardware.display.DisplayManager; +import android.os.BatteryStats; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -119,6 +120,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -249,6 +251,8 @@ public class AppStandbyControllerTests { .setLong("elapsed_threshold_rare", RARE_THRESHOLD) .setLong("elapsed_threshold_restricted", RESTRICTED_THRESHOLD); DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener; + String mExpectedNoteEventPackage = null; + int mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE; MyInjector(Context context, Looper looper) { super(context, looper); @@ -320,6 +324,9 @@ public class AppStandbyControllerTests { @Override void noteEvent(int event, String packageName, int uid) throws RemoteException { + if (Objects.equals(mExpectedNoteEventPackage, packageName)) { + mLastNoteEvent = event; + } } @Override @@ -2103,6 +2110,50 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_FREQUENT, PACKAGE_BACKGROUND_LOCATION); } + @Test + public void testBatteryStatsNoteEvent() throws Exception { + mInjector.mExpectedNoteEventPackage = PACKAGE_1; + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent); + + // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called. + // Reset the last event to confirm the method isn't called. + mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent); + + // Since we're staying on the PACKAGE_ACTIVE side, noteEvent shouldn't be called. + // Reset the last event to confirm the method isn't called. + mInjector.mLastNoteEvent = BatteryStats.HistoryItem.EVENT_NONE; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_NONE, mInjector.mLastNoteEvent); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, mInjector.mLastNoteEvent); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_EXEMPTED, + REASON_MAIN_FORCED_BY_USER); + assertEquals(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, mInjector.mLastNoteEvent); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); }