diff options
4 files changed, 101 insertions, 10 deletions
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 d2d942a4a7e5..dc72d6d9c4b3 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -85,6 +85,7 @@ public interface AppStandbyInternal { /** * Checks if an app has been idle for a while and filters out apps that are excluded. * It returns false if the current system state allows all apps to be considered active. + * This happens if the device is plugged in or otherwise temporarily allowed to make exceptions. * Called by interface impls. */ boolean isAppIdleFiltered(String packageName, int appId, int userId, 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 f1bfa0411978..e343478ec61f 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -48,6 +48,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; 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; import android.annotation.NonNull; @@ -71,9 +72,8 @@ import android.content.pm.ParceledListSlice; import android.database.ContentObserver; import android.hardware.display.DisplayManager; import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkRequest; import android.net.NetworkScoreManager; +import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; import android.os.Environment; @@ -285,6 +285,7 @@ public class AppStandbyController implements AppStandbyInternal { long mInitialForegroundServiceStartTimeoutMillis; private volatile boolean mAppIdleEnabled; + private boolean mIsCharging; private boolean mSystemServicesReady = false; // There was a system update, defaults need to be initialized after services are ready private boolean mPendingInitializeDefaults; @@ -360,6 +361,11 @@ public class AppStandbyController implements AppStandbyInternal { mHandler = new AppStandbyHandler(mInjector.getLooper()); mPackageManager = mContext.getPackageManager(); + DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver(); + IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING); + deviceStates.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(deviceStateReceiver, deviceStates); + synchronized (mAppIdleLock) { mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(), mInjector.elapsedRealtime()); @@ -417,6 +423,8 @@ public class AppStandbyController implements AppStandbyInternal { if (mPendingOneTimeCheckIdleStates) { postOneTimeCheckIdleStates(); } + } else if (phase == PHASE_BOOT_COMPLETED) { + setChargingState(mInjector.isCharging()); } } @@ -515,6 +523,16 @@ public class AppStandbyController implements AppStandbyInternal { appUsage.bucketingReason, false); } + @VisibleForTesting + void setChargingState(boolean isCharging) { + synchronized (mAppIdleLock) { + if (mIsCharging != isCharging) { + if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging); + mIsCharging = isCharging; + } + } + } + @Override public void postCheckIdleStates(int userId) { mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); @@ -977,6 +995,11 @@ public class AppStandbyController implements AppStandbyInternal { if (isAppSpecial(packageName, appId, userId)) { return false; } else { + synchronized (mAppIdleLock) { + if (!mAppIdleEnabled || mIsCharging) { + return false; + } + } return isAppIdleUnfiltered(packageName, userId, elapsedRealtime); } } @@ -1543,6 +1566,8 @@ public class AppStandbyController implements AppStandbyInternal { pw.println(); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); + pw.print(" mIsCharging="); + pw.print(mIsCharging); pw.println(); pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds)); pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds)); @@ -1560,6 +1585,7 @@ public class AppStandbyController implements AppStandbyInternal { private final Looper mLooper; private IDeviceIdleController mDeviceIdleController; private IBatteryStats mBatteryStats; + private BatteryManager mBatteryManager; private PackageManagerInternal mPackageManagerInternal; private DisplayManager mDisplayManager; private PowerManager mPowerManager; @@ -1593,6 +1619,7 @@ public class AppStandbyController implements AppStandbyInternal { mDisplayManager = (DisplayManager) mContext.getSystemService( Context.DISPLAY_SERVICE); mPowerManager = mContext.getSystemService(PowerManager.class); + mBatteryManager = mContext.getSystemService(BatteryManager.class); final ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1630,6 +1657,10 @@ public class AppStandbyController implements AppStandbyInternal { return buildFlag && runtimeFlag; } + boolean isCharging() { + return mBatteryManager.isCharging(); + } + boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException { return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName); } @@ -1766,15 +1797,19 @@ public class AppStandbyController implements AppStandbyInternal { } }; - private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build(); - - private final ConnectivityManager.NetworkCallback mNetworkCallback - = new ConnectivityManager.NetworkCallback() { + private class DeviceStateReceiver extends BroadcastReceiver { @Override - public void onAvailable(Network network) { - mConnectivityManager.unregisterNetworkCallback(this); + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case BatteryManager.ACTION_CHARGING: + setChargingState(true); + break; + case BatteryManager.ACTION_DISCHARGING: + setChargingState(false); + break; + } } - }; + } private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 5668944dfd4e..2c701b48455c 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -599,7 +599,8 @@ public final class UsageStatsManager { /** * Returns whether the specified app is currently considered inactive. This will be true if the * app hasn't been used directly or indirectly for a period of time defined by the system. This - * could be of the order of several hours or days. + * could be of the order of several hours or days. Apps are not considered inactive when the + * device is charging. * @param packageName The package name of the app to query * @return whether the app is currently considered inactive */ 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 e768205c2cf4..9e577636c1b3 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -145,6 +145,7 @@ public class AppStandbyControllerTests { static class MyInjector extends AppStandbyController.Injector { long mElapsedRealtime; boolean mIsAppIdleEnabled = true; + boolean mIsCharging; List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>(); boolean mDisplayOn; DisplayManager.DisplayListener mDisplayListener; @@ -179,6 +180,11 @@ public class AppStandbyControllerTests { } @Override + boolean isCharging() { + return mIsCharging; + } + + @Override boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException { return mPowerSaveWhitelistExceptIdle.contains(packageName); } @@ -281,6 +287,13 @@ public class AppStandbyControllerTests { } catch (PackageManager.NameNotFoundException nnfe) {} } + private void setChargingState(AppStandbyController controller, boolean charging) { + mInjector.mIsCharging = charging; + if (controller != null) { + controller.setChargingState(charging); + } + } + private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) { mInjector.mIsAppIdleEnabled = enabled; if (controller != null) { @@ -297,6 +310,7 @@ public class AppStandbyControllerTests { controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); mInjector.setDisplayOn(false); mInjector.setDisplayOn(true); + setChargingState(controller, false); controller.checkIdleStates(USER_ID); assertNotEquals(STANDBY_BUCKET_EXEMPTED, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, @@ -324,6 +338,46 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime, false)); } + @Test + public void testIsAppIdle_Charging() throws Exception { + setChargingState(mController, false); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setChargingState(mController, true); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setChargingState(mController, false); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + } + + @Test + public void testIsAppIdle_Enabled() throws Exception { + setChargingState(mController, false); + setAppIdleEnabled(mController, true); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setAppIdleEnabled(mController, false); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setAppIdleEnabled(mController, true); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + } + private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) { mInjector.mElapsedRealtime = elapsedTime; controller.checkIdleStates(USER_ID); |