Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f35ed370 authored by Kweku Adams's avatar Kweku Adams
Browse files

Make apps active when charging.

This undoes part of the work to remove parole windows from
AppStandbyController. In the past, UsageStatsManager.isAppInactive would
return false when the device was charging. This wasn't documented
behavior, but was intentional, so removing it was the wrong thing to do.
I've updated the documentation to explicitly call out the behavior and
added it back so that the behavior is consistent with previous versions.

Bug: 148988544
Test: atest CtsUsageStatsTestCases:UsageStatsTest
Test: atest AppIdleHistoryTests
Test: atest AppStandbyControllerTests
Change-Id: If5cad80b693a1e043ae64b8206e28abb60f83853
parent 223c27ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,7 @@ public interface AppStandbyInternal {
    /**
    /**
     * Checks if an app has been idle for a while and filters out apps that are excluded.
     * 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.
     * 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.
     * Called by interface impls.
     */
     */
    boolean isAppIdleFiltered(String packageName, int appId, int userId,
    boolean isAppIdleFiltered(String packageName, int appId, int userId,
+44 −9
Original line number Original line Diff line number Diff line
@@ -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_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
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 static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;


import android.annotation.NonNull;
import android.annotation.NonNull;
@@ -71,9 +72,8 @@ import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Build;
import android.os.Environment;
import android.os.Environment;
@@ -285,6 +285,7 @@ public class AppStandbyController implements AppStandbyInternal {
    long mInitialForegroundServiceStartTimeoutMillis;
    long mInitialForegroundServiceStartTimeoutMillis;


    private volatile boolean mAppIdleEnabled;
    private volatile boolean mAppIdleEnabled;
    private boolean mIsCharging;
    private boolean mSystemServicesReady = false;
    private boolean mSystemServicesReady = false;
    // There was a system update, defaults need to be initialized after services are ready
    // There was a system update, defaults need to be initialized after services are ready
    private boolean mPendingInitializeDefaults;
    private boolean mPendingInitializeDefaults;
@@ -360,6 +361,11 @@ public class AppStandbyController implements AppStandbyInternal {
        mHandler = new AppStandbyHandler(mInjector.getLooper());
        mHandler = new AppStandbyHandler(mInjector.getLooper());
        mPackageManager = mContext.getPackageManager();
        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) {
        synchronized (mAppIdleLock) {
            mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
            mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
                    mInjector.elapsedRealtime());
                    mInjector.elapsedRealtime());
@@ -417,6 +423,8 @@ public class AppStandbyController implements AppStandbyInternal {
            if (mPendingOneTimeCheckIdleStates) {
            if (mPendingOneTimeCheckIdleStates) {
                postOneTimeCheckIdleStates();
                postOneTimeCheckIdleStates();
            }
            }
        } else if (phase == PHASE_BOOT_COMPLETED) {
            setChargingState(mInjector.isCharging());
        }
        }
    }
    }


@@ -515,6 +523,16 @@ public class AppStandbyController implements AppStandbyInternal {
                appUsage.bucketingReason, false);
                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
    @Override
    public void postCheckIdleStates(int userId) {
    public void postCheckIdleStates(int userId) {
        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
@@ -977,6 +995,11 @@ public class AppStandbyController implements AppStandbyInternal {
        if (isAppSpecial(packageName, appId, userId)) {
        if (isAppSpecial(packageName, appId, userId)) {
            return false;
            return false;
        } else {
        } else {
            synchronized (mAppIdleLock) {
                if (!mAppIdleEnabled || mIsCharging) {
                    return false;
                }
            }
            return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
            return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
        }
        }
    }
    }
@@ -1543,6 +1566,8 @@ public class AppStandbyController implements AppStandbyInternal {


        pw.println();
        pw.println();
        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
        pw.print(" mIsCharging=");
        pw.print(mIsCharging);
        pw.println();
        pw.println();
        pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
        pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
        pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
        pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
@@ -1560,6 +1585,7 @@ public class AppStandbyController implements AppStandbyInternal {
        private final Looper mLooper;
        private final Looper mLooper;
        private IDeviceIdleController mDeviceIdleController;
        private IDeviceIdleController mDeviceIdleController;
        private IBatteryStats mBatteryStats;
        private IBatteryStats mBatteryStats;
        private BatteryManager mBatteryManager;
        private PackageManagerInternal mPackageManagerInternal;
        private PackageManagerInternal mPackageManagerInternal;
        private DisplayManager mDisplayManager;
        private DisplayManager mDisplayManager;
        private PowerManager mPowerManager;
        private PowerManager mPowerManager;
@@ -1593,6 +1619,7 @@ public class AppStandbyController implements AppStandbyInternal {
                mDisplayManager = (DisplayManager) mContext.getSystemService(
                mDisplayManager = (DisplayManager) mContext.getSystemService(
                        Context.DISPLAY_SERVICE);
                        Context.DISPLAY_SERVICE);
                mPowerManager = mContext.getSystemService(PowerManager.class);
                mPowerManager = mContext.getSystemService(PowerManager.class);
                mBatteryManager = mContext.getSystemService(BatteryManager.class);


                final ActivityManager activityManager =
                final ActivityManager activityManager =
                        (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
                        (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -1630,6 +1657,10 @@ public class AppStandbyController implements AppStandbyInternal {
            return buildFlag && runtimeFlag;
            return buildFlag && runtimeFlag;
        }
        }


        boolean isCharging() {
            return mBatteryManager.isCharging();
        }

        boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
        boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
            return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
            return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
        }
        }
@@ -1766,15 +1797,19 @@ public class AppStandbyController implements AppStandbyInternal {
        }
        }
    };
    };


    private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
    private class DeviceStateReceiver extends BroadcastReceiver {

    private final ConnectivityManager.NetworkCallback mNetworkCallback
            = new ConnectivityManager.NetworkCallback() {
        @Override
        @Override
        public void onAvailable(Network network) {
        public void onReceive(Context context, Intent intent) {
            mConnectivityManager.unregisterNetworkCallback(this);
            switch (intent.getAction()) {
                case BatteryManager.ACTION_CHARGING:
                    setChargingState(true);
                    break;
                case BatteryManager.ACTION_DISCHARGING:
                    setChargingState(false);
                    break;
            }
        }
    }
    }
    };


    private final DisplayManager.DisplayListener mDisplayListener
    private final DisplayManager.DisplayListener mDisplayListener
            = new DisplayManager.DisplayListener() {
            = new DisplayManager.DisplayListener() {
+2 −1
Original line number Original line Diff line number Diff line
@@ -599,7 +599,8 @@ public final class UsageStatsManager {
    /**
    /**
     * Returns whether the specified app is currently considered inactive. This will be true if the
     * 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
     * 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
     * @param packageName The package name of the app to query
     * @return whether the app is currently considered inactive
     * @return whether the app is currently considered inactive
     */
     */
+54 −0
Original line number Original line Diff line number Diff line
@@ -145,6 +145,7 @@ public class AppStandbyControllerTests {
    static class MyInjector extends AppStandbyController.Injector {
    static class MyInjector extends AppStandbyController.Injector {
        long mElapsedRealtime;
        long mElapsedRealtime;
        boolean mIsAppIdleEnabled = true;
        boolean mIsAppIdleEnabled = true;
        boolean mIsCharging;
        List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
        List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
        boolean mDisplayOn;
        boolean mDisplayOn;
        DisplayManager.DisplayListener mDisplayListener;
        DisplayManager.DisplayListener mDisplayListener;
@@ -178,6 +179,11 @@ public class AppStandbyControllerTests {
            return mIsAppIdleEnabled;
            return mIsAppIdleEnabled;
        }
        }


        @Override
        boolean isCharging() {
            return mIsCharging;
        }

        @Override
        @Override
        boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
        boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
            return mPowerSaveWhitelistExceptIdle.contains(packageName);
            return mPowerSaveWhitelistExceptIdle.contains(packageName);
@@ -281,6 +287,13 @@ public class AppStandbyControllerTests {
        } catch (PackageManager.NameNotFoundException nnfe) {}
        } 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) {
    private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
        mInjector.mIsAppIdleEnabled = enabled;
        mInjector.mIsAppIdleEnabled = enabled;
        if (controller != null) {
        if (controller != null) {
@@ -297,6 +310,7 @@ public class AppStandbyControllerTests {
        controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
        controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
        mInjector.setDisplayOn(false);
        mInjector.setDisplayOn(false);
        mInjector.setDisplayOn(true);
        mInjector.setDisplayOn(true);
        setChargingState(controller, false);
        controller.checkIdleStates(USER_ID);
        controller.checkIdleStates(USER_ID);
        assertNotEquals(STANDBY_BUCKET_EXEMPTED,
        assertNotEquals(STANDBY_BUCKET_EXEMPTED,
                controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
                controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
@@ -324,6 +338,46 @@ public class AppStandbyControllerTests {
                        mInjector.mElapsedRealtime, false));
                        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) {
    private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
        mInjector.mElapsedRealtime = elapsedTime;
        mInjector.mElapsedRealtime = elapsedTime;
        controller.checkIdleStates(USER_ID);
        controller.checkIdleStates(USER_ID);