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

Commit 2870552d authored by Michael Wachenschwanz's avatar Michael Wachenschwanz Committed by android-build-merger
Browse files

Merge "Add Stable Charging Threshold for AppStandby" into pi-dev

am: b3972f9d

Change-Id: Iacdddabdd58c0da6b53d997b08ddf70bef583392
parents 870d327b b3972f9d
Loading
Loading
Loading
Loading
+17 −5
Original line number Original line Diff line number Diff line
@@ -10619,18 +10619,30 @@ public final class Settings {
         * App standby (app idle) specific settings.
         * App standby (app idle) specific settings.
         * This is encoded as a key=value list, separated by commas. Ex:
         * This is encoded as a key=value list, separated by commas. Ex:
         * <p>
         * <p>
         * "idle_duration=5000,parole_interval=4500"
         * "idle_duration=5000,parole_interval=4500,screen_thresholds=0/0/60000/120000"
         * <p>
         * <p>
         * All durations are in millis.
         * All durations are in millis.
         * Array values are separated by forward slashes
         * The following keys are supported:
         * The following keys are supported:
         *
         *
         * <pre>
         * <pre>
         * idle_duration2       (long)
         * wallclock_threshold  (long)
         * parole_interval                  (long)
         * parole_interval                  (long)
         * parole_window                    (long)
         * parole_duration                  (long)
         * parole_duration                  (long)
         * screen_thresholds                (long[4])
         * elapsed_thresholds               (long[4])
         * strong_usage_duration            (long)
         * notification_seen_duration       (long)
         * system_update_usage_duration     (long)
         * prediction_timeout               (long)
         * sync_adapter_duration            (long)
         * exempted_sync_duration           (long)
         * system_interaction_duration      (long)
         * stable_charging_threshold        (long)
         *
         *
         * idle_duration        (long) // This is deprecated and used to circumvent b/26355386.
         * idle_duration        (long) // This is deprecated and used to circumvent b/26355386.
         * idle_duration2       (long) // deprecated
         * wallclock_threshold  (long) // deprecated
         * </pre>
         * </pre>
         *
         *
         * <p>
         * <p>
+100 −10
Original line number Original line Diff line number Diff line
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;


import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.ContextWrapper;
@@ -74,6 +75,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
/**
 * Unit test for AppStandbyController.
 * Unit test for AppStandbyController.
@@ -101,6 +104,8 @@ public class AppStandbyControllerTests {
    private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
    private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
    private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
    private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
    private static final long RARE_THRESHOLD = 48 * HOUR_MS;
    private static final long RARE_THRESHOLD = 48 * HOUR_MS;
    // Short STABLE_CHARGING_THRESHOLD for testing purposes
    private static final long STABLE_CHARGING_THRESHOLD = 2000;


    private MyInjector mInjector;
    private MyInjector mInjector;
    private AppStandbyController mController;
    private AppStandbyController mController;
@@ -209,7 +214,8 @@ public class AppStandbyControllerTests {
            return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
            return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
                    + WORKING_SET_THRESHOLD + "/"
                    + WORKING_SET_THRESHOLD + "/"
                    + FREQUENT_THRESHOLD + "/"
                    + FREQUENT_THRESHOLD + "/"
                    + RARE_THRESHOLD;
                    + RARE_THRESHOLD + ","
                    + "stable_charging_threshold=" + STABLE_CHARGING_THRESHOLD;
        }
        }


        // Internal methods
        // Internal methods
@@ -276,6 +282,10 @@ public class AppStandbyControllerTests {
        return controller;
        return controller;
    }
    }


    private long getCurrentTime() {
        return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
    }

    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
        MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
@@ -284,21 +294,101 @@ public class AppStandbyControllerTests {
        setChargingState(mController, false);
        setChargingState(mController, false);
    }
    }


    private class TestParoleListener extends UsageStatsManagerInternal.AppIdleStateChangeListener {
        private boolean mOnParole = false;
        private CountDownLatch mLatch;
        private long mLastParoleChangeTime;

        public boolean getParoleState() {
            synchronized (this) {
                return mOnParole;
            }
        }

        public void rearmLatch() {
            synchronized (this) {
                mLatch = new CountDownLatch(1);
            }
        }

        public void awaitOnLatch(long time) throws Exception {
            mLatch.await(time, TimeUnit.MILLISECONDS);
        }

        public long getLastParoleChangeTime() {
            synchronized (this) {
                return mLastParoleChangeTime;
            }
        }

        @Override
        public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
                int bucket, int reason) {
        }

        @Override
        public void onParoleStateChanged(boolean isParoleOn) {
            synchronized (this) {
                // Only record information if it is being looked for
                if (mLatch.getCount() > 0) {
                    mOnParole = isParoleOn;
                    mLastParoleChangeTime = getCurrentTime();
                    mLatch.countDown();
                }
            }
        }
    }

    @Test
    @Test
    public void testCharging() throws Exception {
    public void testCharging() throws Exception {
        long startTime;
        TestParoleListener paroleListener = new TestParoleListener();
        long marginOfError = 200;

        // Charging
        paroleListener.rearmLatch();
        mController.addListener(paroleListener);
        startTime = getCurrentTime();
        setChargingState(mController, true);
        setChargingState(mController, true);
        mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
        assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
        assertTrue(paroleListener.mOnParole);
                mInjector.mElapsedRealtime, false));
        // Parole will only be granted after device has been charging for a sufficient amount of

        // time.
        assertEquals(STABLE_CHARGING_THRESHOLD,
                paroleListener.getLastParoleChangeTime() - startTime,
                marginOfError);

        // Discharging
        paroleListener.rearmLatch();
        startTime = getCurrentTime();
        setChargingState(mController, false);
        setChargingState(mController, false);
        mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
        mController.checkIdleStates(USER_ID);
        mController.checkIdleStates(USER_ID);
        assertTrue(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
                mInjector.mElapsedRealtime, false));
        assertFalse(paroleListener.getParoleState());
        // Parole should be revoked immediately
        assertEquals(0,
                paroleListener.getLastParoleChangeTime() - startTime,
                marginOfError);

        // Brief Charging
        paroleListener.rearmLatch();
        setChargingState(mController, true);
        setChargingState(mController, true);
        assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
        setChargingState(mController, false);
                mInjector.mElapsedRealtime, false));
        // Device stopped charging before the stable charging threshold.
        // Parole should not be granted at the end of the threshold
        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
        assertFalse(paroleListener.getParoleState());

        // Charging Again
        paroleListener.rearmLatch();
        startTime = getCurrentTime();
        setChargingState(mController, true);
        paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
        assertTrue(paroleListener.getParoleState());
        assertTrue(paroleListener.mOnParole);
        assertEquals(STABLE_CHARGING_THRESHOLD,
                paroleListener.getLastParoleChangeTime() - startTime,
                marginOfError);
    }
    }


    private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
    private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
+55 −9
Original line number Original line Diff line number Diff line
@@ -192,6 +192,7 @@ public class AppStandbyController {
    /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
    /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
    static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
    static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
    static final int MSG_REPORT_EXEMPTED_SYNC_START = 12;
    static final int MSG_REPORT_EXEMPTED_SYNC_START = 12;
    static final int MSG_UPDATE_STABLE_CHARGING= 13;


    long mCheckIdleIntervalMillis;
    long mCheckIdleIntervalMillis;
    long mAppIdleParoleIntervalMillis;
    long mAppIdleParoleIntervalMillis;
@@ -213,10 +214,13 @@ public class AppStandbyController {
    long mExemptedSyncAdapterTimeoutMillis;
    long mExemptedSyncAdapterTimeoutMillis;
    /** Maximum time a system interaction should keep the buckets elevated. */
    /** Maximum time a system interaction should keep the buckets elevated. */
    long mSystemInteractionTimeoutMillis;
    long mSystemInteractionTimeoutMillis;
    /** The length of time phone must be charging before considered stable enough to run jobs  */
    long mStableChargingThresholdMillis;


    volatile boolean mAppIdleEnabled;
    volatile boolean mAppIdleEnabled;
    boolean mAppIdleTempParoled;
    boolean mAppIdleTempParoled;
    boolean mCharging;
    boolean mCharging;
    boolean mChargingStable;
    private long mLastAppIdleParoledTime;
    private long mLastAppIdleParoledTime;
    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
@@ -297,7 +301,7 @@ public class AppStandbyController {
        mPackageManager = mContext.getPackageManager();
        mPackageManager = mContext.getPackageManager();
        mDeviceStateReceiver = new DeviceStateReceiver();
        mDeviceStateReceiver = new DeviceStateReceiver();


        IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
        deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
        deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
        deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        mContext.registerReceiver(mDeviceStateReceiver, deviceStates);
        mContext.registerReceiver(mDeviceStateReceiver, deviceStates);
@@ -405,6 +409,27 @@ public class AppStandbyController {
        synchronized (mAppIdleLock) {
        synchronized (mAppIdleLock) {
            if (mCharging != charging) {
            if (mCharging != charging) {
                mCharging = charging;
                mCharging = charging;
                if (DEBUG) Slog.d(TAG, "Setting mCharging to " + charging);
                if (charging) {
                    if (DEBUG) {
                        Slog.d(TAG, "Scheduling MSG_UPDATE_STABLE_CHARGING  delay = "
                                + mStableChargingThresholdMillis);
                    }
                    mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STABLE_CHARGING,
                            mStableChargingThresholdMillis);
                } else {
                    mHandler.removeMessages(MSG_UPDATE_STABLE_CHARGING);
                    updateChargingStableState();
                }
            }
        }
    }

    void updateChargingStableState() {
        synchronized (mAppIdleLock) {
            if (mChargingStable != mCharging) {
                if (DEBUG) Slog.d(TAG, "Setting mChargingStable to " + mCharging);
                mChargingStable = mCharging;
                postParoleStateChanged();
                postParoleStateChanged();
            }
            }
        }
        }
@@ -431,7 +456,8 @@ public class AppStandbyController {
    boolean isParoledOrCharging() {
    boolean isParoledOrCharging() {
        if (!mAppIdleEnabled) return true;
        if (!mAppIdleEnabled) return true;
        synchronized (mAppIdleLock) {
        synchronized (mAppIdleLock) {
            return mAppIdleTempParoled || mCharging;
            // Only consider stable charging when determining charge state.
            return mAppIdleTempParoled || mChargingStable;
        }
        }
    }
    }


@@ -1371,11 +1397,15 @@ public class AppStandbyController {
        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
        pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
        pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
        pw.print(" mCharging="); pw.print(mCharging);
        pw.print(" mCharging="); pw.print(mCharging);
        pw.print(" mChargingStable="); pw.print(mChargingStable);
        pw.print(" mLastAppIdleParoledTime=");
        pw.print(" mLastAppIdleParoledTime=");
        TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
        TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
        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));
        pw.print("mStableChargingThresholdMillis=");
        TimeUtils.formatDuration(mStableChargingThresholdMillis, pw);
        pw.println();
    }
    }


    /**
    /**
@@ -1549,7 +1579,7 @@ public class AppStandbyController {


                case MSG_PAROLE_STATE_CHANGED:
                case MSG_PAROLE_STATE_CHANGED:
                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
                            + ", Charging state:" + mCharging);
                            + ", Charging state:" + mChargingStable);
                    informParoleStateChanged();
                    informParoleStateChanged();
                    break;
                    break;
                case MSG_CHECK_PACKAGE_IDLE_STATE:
                case MSG_CHECK_PACKAGE_IDLE_STATE:
@@ -1561,6 +1591,10 @@ public class AppStandbyController {
                    reportExemptedSyncStart((String) msg.obj, msg.arg1);
                    reportExemptedSyncStart((String) msg.obj, msg.arg1);
                    break;
                    break;


                case MSG_UPDATE_STABLE_CHARGING:
                    updateChargingStableState();
                    break;

                default:
                default:
                    super.handleMessage(msg);
                    super.handleMessage(msg);
                    break;
                    break;
@@ -1572,11 +1606,16 @@ public class AppStandbyController {
    private class DeviceStateReceiver extends BroadcastReceiver {
    private class DeviceStateReceiver extends BroadcastReceiver {
        @Override
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            switch (intent.getAction()) {
            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
                case BatteryManager.ACTION_CHARGING:
                setChargingState(intent.getIntExtra("plugged", 0) != 0);
                    setChargingState(true);
            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
                    break;
                case BatteryManager.ACTION_DISCHARGING:
                    setChargingState(false);
                    break;
                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
                    onDeviceIdleModeChanged();
                    onDeviceIdleModeChanged();
                    break;
            }
            }
        }
        }
    }
    }
@@ -1620,9 +1659,11 @@ public class AppStandbyController {
         */
         */
        @Deprecated
        @Deprecated
        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";

        @Deprecated
        private static final String KEY_IDLE_DURATION = "idle_duration2";
        private static final String KEY_IDLE_DURATION = "idle_duration2";
        @Deprecated
        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";

        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
        private static final String KEY_PAROLE_WINDOW = "parole_window";
        private static final String KEY_PAROLE_WINDOW = "parole_window";
        private static final String KEY_PAROLE_DURATION = "parole_duration";
        private static final String KEY_PAROLE_DURATION = "parole_duration";
@@ -1638,12 +1679,14 @@ public class AppStandbyController {
        private static final String KEY_EXEMPTED_SYNC_HOLD_DURATION = "exempted_sync_duration";
        private static final String KEY_EXEMPTED_SYNC_HOLD_DURATION = "exempted_sync_duration";
        private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
        private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
                "system_interaction_duration";
                "system_interaction_duration";
        private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold";
        public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
        public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
        public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
        public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
        public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
        public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
        public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_EXEMPTED_SYNC_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_EXEMPTED_SYNC_TIMEOUT = 10 * ONE_MINUTE;
        public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE;


        private final KeyValueListParser mParser = new KeyValueListParser(',');
        private final KeyValueListParser mParser = new KeyValueListParser(',');


@@ -1733,6 +1776,9 @@ public class AppStandbyController {
                mSystemInteractionTimeoutMillis = mParser.getDurationMillis
                mSystemInteractionTimeoutMillis = mParser.getDurationMillis
                        (KEY_SYSTEM_INTERACTION_HOLD_DURATION,
                        (KEY_SYSTEM_INTERACTION_HOLD_DURATION,
                                COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
                                COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
                mStableChargingThresholdMillis = mParser.getDurationMillis
                        (KEY_STABLE_CHARGING_THRESHOLD,
                                COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
            }
            }
        }
        }