From 3d64c041f4072a2b7359be492f8e0a92f16097ac Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Mon, 5 Nov 2018 18:06:13 -0800 Subject: Informing app idle listeners on enabled state changes. Currently, if AppStandbyController is enabled after some listeners have already registered, the listeners will be told that the system is in a state of parole even though AppStandbyController thinks it's not in parole. This change informs AppIdleStateChangeListeners when AppStandbyController's enabled state changes as well so that they can be in the correct state. I also removed the call to setAppIdleEnabled() in onBootPhase() since updateSettings() is called right afterwards and that also calls setAppIdleEnabled(). Bug: 112329453 Test: atest AppStandbyControllerTests also check logs to confirm that NetworkPolicyManagerService and JobSchedulerService's listeners switch from ON to OFF when the system is ready. Change-Id: I88bc293c70b459f54f75f92126ad306d6ab8d9b7 Merged-In: I88bc293c70b459f54f75f92126ad306d6ab8d9b7 --- .../server/usage/AppStandbyControllerTests.java | 80 +++++++++++++++++++++- .../android/server/usage/AppStandbyController.java | 22 ++++-- 2 files changed, 94 insertions(+), 8 deletions(-) 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 84611667d113..39fc715c7770 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -124,6 +124,7 @@ public class AppStandbyControllerTests { static class MyInjector extends AppStandbyController.Injector { long mElapsedRealtime; + boolean mIsAppIdleEnabled = true; boolean mIsCharging; List mPowerSaveWhitelistExceptIdle = new ArrayList<>(); boolean mDisplayOn; @@ -155,7 +156,7 @@ public class AppStandbyControllerTests { @Override boolean isAppIdleEnabled() { - return true; + return mIsAppIdleEnabled; } @Override @@ -266,6 +267,13 @@ public class AppStandbyControllerTests { } } + private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) { + mInjector.mIsAppIdleEnabled = enabled; + if (controller != null) { + controller.setAppIdleEnabled(enabled); + } + } + private AppStandbyController setupController() throws Exception { mInjector.mElapsedRealtime = 0; setupPm(mInjector.getContext().getPackageManager()); @@ -335,7 +343,7 @@ public class AppStandbyControllerTests { public void onParoleStateChanged(boolean isParoleOn) { synchronized (this) { // Only record information if it is being looked for - if (mLatch.getCount() > 0) { + if (mLatch != null && mLatch.getCount() > 0) { mOnParole = isParoleOn; mLastParoleChangeTime = getCurrentTime(); mLatch.countDown(); @@ -396,6 +404,74 @@ public class AppStandbyControllerTests { marginOfError); } + @Test + public void testEnabledState() throws Exception { + TestParoleListener paroleListener = new TestParoleListener(); + mController.addListener(paroleListener); + long lastUpdateTime; + + // Test that listeners are notified if enabled changes when the device is not in parole. + setChargingState(mController, false); + + // Start off not enabled. Device is effectively on permanent parole. + setAppIdleEnabled(mController, false); + + // Enable controller + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, true); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertFalse(paroleListener.mOnParole); + lastUpdateTime = paroleListener.getLastParoleChangeTime(); + + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, true); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertFalse(paroleListener.mOnParole); + // Make sure AppStandbyController doesn't notify listeners when there's no change. + assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime()); + + // Disable controller + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, false); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertTrue(paroleListener.mOnParole); + lastUpdateTime = paroleListener.getLastParoleChangeTime(); + + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, false); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertTrue(paroleListener.mOnParole); + // Make sure AppStandbyController doesn't notify listeners when there's no change. + assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime()); + + + // Test that listeners aren't notified if enabled status changes when the device is already + // in parole. + + // A device is in parole whenever it's charging. + setChargingState(mController, true); + + // Start off not enabled. + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, false); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertTrue(paroleListener.mOnParole); + lastUpdateTime = paroleListener.getLastParoleChangeTime(); + + // Test that toggling doesn't notify the listener. + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, true); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertTrue(paroleListener.mOnParole); + assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime()); + + paroleListener.rearmLatch(); + setAppIdleEnabled(mController, false); + paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2); + assertTrue(paroleListener.mOnParole); + assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime()); + } + private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) { mInjector.mElapsedRealtime = elapsedTime; controller.checkIdleStates(USER_ID); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 9c62700a9118..e380d7a6808b 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -30,18 +30,19 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC 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_SLICE_PINNED; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV; 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.REASON_SUB_USAGE_SLICE_PINNED; -import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV; 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; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; + import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; @@ -340,14 +341,21 @@ public class AppStandbyController { } void setAppIdleEnabled(boolean enabled) { - mAppIdleEnabled = enabled; + synchronized (mAppIdleLock) { + if (mAppIdleEnabled != enabled) { + final boolean oldParoleState = isParoledOrCharging(); + mAppIdleEnabled = enabled; + if (isParoledOrCharging() != oldParoleState) { + postParoleStateChanged(); + } + } + } } public void onBootPhase(int phase) { mInjector.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { Slog.d(TAG, "Setting app idle enabled state"); - setAppIdleEnabled(mInjector.isAppIdleEnabled()); // Observe changes to the threshold SettingsObserver settingsObserver = new SettingsObserver(mHandler); settingsObserver.registerObserver(); @@ -1807,8 +1815,6 @@ public class AppStandbyController { mContext.getContentResolver(), Global.APP_IDLE_CONSTANTS)); } - // Check if app_idle_enabled has changed - setAppIdleEnabled(mInjector.isAppIdleEnabled()); // Look at global settings for this. // TODO: Maybe apply different thresholds for different users. @@ -1880,6 +1886,10 @@ public class AppStandbyController { (KEY_STABLE_CHARGING_THRESHOLD, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD); } + + // Check if app_idle_enabled has changed. Do this after getting the rest of the settings + // in case we need to change something based on the new values. + setAppIdleEnabled(mInjector.isAppIdleEnabled()); } long[] parseLongArray(String values, long[] defaults) { -- cgit v1.2.3-59-g8ed1b