Merge "Add Stable Charging Threshold for AppStandby" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b3972f9d23
@@ -10611,18 +10611,30 @@ public final class Settings {
|
||||
* App standby (app idle) specific settings.
|
||||
* This is encoded as a key=value list, separated by commas. Ex:
|
||||
* <p>
|
||||
* "idle_duration=5000,parole_interval=4500"
|
||||
* "idle_duration=5000,parole_interval=4500,screen_thresholds=0/0/60000/120000"
|
||||
* <p>
|
||||
* All durations are in millis.
|
||||
* Array values are separated by forward slashes
|
||||
* The following keys are supported:
|
||||
*
|
||||
* <pre>
|
||||
* idle_duration2 (long)
|
||||
* wallclock_threshold (long)
|
||||
* parole_interval (long)
|
||||
* parole_duration (long)
|
||||
* parole_interval (long)
|
||||
* parole_window (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_duration2 (long) // deprecated
|
||||
* wallclock_threshold (long) // deprecated
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
|
||||
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
@@ -74,6 +75,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 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 FREQUENT_THRESHOLD = 24 * 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 AppStandbyController mController;
|
||||
@@ -209,7 +214,8 @@ public class AppStandbyControllerTests {
|
||||
return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
|
||||
+ WORKING_SET_THRESHOLD + "/"
|
||||
+ FREQUENT_THRESHOLD + "/"
|
||||
+ RARE_THRESHOLD;
|
||||
+ RARE_THRESHOLD + ","
|
||||
+ "stable_charging_threshold=" + STABLE_CHARGING_THRESHOLD;
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
@@ -276,6 +282,10 @@ public class AppStandbyControllerTests {
|
||||
return controller;
|
||||
}
|
||||
|
||||
private long getCurrentTime() {
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
|
||||
@@ -284,21 +294,101 @@ public class AppStandbyControllerTests {
|
||||
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
|
||||
public void testCharging() throws Exception {
|
||||
setChargingState(mController, true);
|
||||
mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
|
||||
assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
|
||||
mInjector.mElapsedRealtime, false));
|
||||
long startTime;
|
||||
TestParoleListener paroleListener = new TestParoleListener();
|
||||
long marginOfError = 200;
|
||||
|
||||
setChargingState(mController, false);
|
||||
mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertTrue(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
|
||||
mInjector.mElapsedRealtime, false));
|
||||
// Charging
|
||||
paroleListener.rearmLatch();
|
||||
mController.addListener(paroleListener);
|
||||
startTime = getCurrentTime();
|
||||
setChargingState(mController, true);
|
||||
assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
|
||||
mInjector.mElapsedRealtime, false));
|
||||
paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
|
||||
assertTrue(paroleListener.mOnParole);
|
||||
// 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);
|
||||
mController.checkIdleStates(USER_ID);
|
||||
paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
|
||||
assertFalse(paroleListener.getParoleState());
|
||||
// Parole should be revoked immediately
|
||||
assertEquals(0,
|
||||
paroleListener.getLastParoleChangeTime() - startTime,
|
||||
marginOfError);
|
||||
|
||||
// Brief Charging
|
||||
paroleListener.rearmLatch();
|
||||
setChargingState(mController, true);
|
||||
setChargingState(mController, 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) {
|
||||
|
||||
@@ -192,6 +192,7 @@ public class AppStandbyController {
|
||||
/** 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_REPORT_EXEMPTED_SYNC_START = 12;
|
||||
static final int MSG_UPDATE_STABLE_CHARGING= 13;
|
||||
|
||||
long mCheckIdleIntervalMillis;
|
||||
long mAppIdleParoleIntervalMillis;
|
||||
@@ -213,10 +214,13 @@ public class AppStandbyController {
|
||||
long mExemptedSyncAdapterTimeoutMillis;
|
||||
/** Maximum time a system interaction should keep the buckets elevated. */
|
||||
long mSystemInteractionTimeoutMillis;
|
||||
/** The length of time phone must be charging before considered stable enough to run jobs */
|
||||
long mStableChargingThresholdMillis;
|
||||
|
||||
volatile boolean mAppIdleEnabled;
|
||||
boolean mAppIdleTempParoled;
|
||||
boolean mCharging;
|
||||
boolean mChargingStable;
|
||||
private long mLastAppIdleParoledTime;
|
||||
private boolean mSystemServicesReady = false;
|
||||
// There was a system update, defaults need to be initialized after services are ready
|
||||
@@ -297,7 +301,7 @@ public class AppStandbyController {
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
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(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||
mContext.registerReceiver(mDeviceStateReceiver, deviceStates);
|
||||
@@ -405,6 +409,27 @@ public class AppStandbyController {
|
||||
synchronized (mAppIdleLock) {
|
||||
if (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();
|
||||
}
|
||||
}
|
||||
@@ -431,7 +456,8 @@ public class AppStandbyController {
|
||||
boolean isParoledOrCharging() {
|
||||
if (!mAppIdleEnabled) return true;
|
||||
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(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
|
||||
pw.print(" mCharging="); pw.print(mCharging);
|
||||
pw.print(" mChargingStable="); pw.print(mChargingStable);
|
||||
pw.print(" mLastAppIdleParoledTime=");
|
||||
TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
|
||||
pw.println();
|
||||
pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
|
||||
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:
|
||||
if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
|
||||
+ ", Charging state:" + mCharging);
|
||||
+ ", Charging state:" + mChargingStable);
|
||||
informParoleStateChanged();
|
||||
break;
|
||||
case MSG_CHECK_PACKAGE_IDLE_STATE:
|
||||
@@ -1561,6 +1591,10 @@ public class AppStandbyController {
|
||||
reportExemptedSyncStart((String) msg.obj, msg.arg1);
|
||||
break;
|
||||
|
||||
case MSG_UPDATE_STABLE_CHARGING:
|
||||
updateChargingStableState();
|
||||
break;
|
||||
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
break;
|
||||
@@ -1572,11 +1606,16 @@ public class AppStandbyController {
|
||||
private class DeviceStateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
|
||||
setChargingState(intent.getIntExtra("plugged", 0) != 0);
|
||||
} else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
|
||||
onDeviceIdleModeChanged();
|
||||
switch (intent.getAction()) {
|
||||
case BatteryManager.ACTION_CHARGING:
|
||||
setChargingState(true);
|
||||
break;
|
||||
case BatteryManager.ACTION_DISCHARGING:
|
||||
setChargingState(false);
|
||||
break;
|
||||
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
|
||||
onDeviceIdleModeChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1620,9 +1659,11 @@ public class AppStandbyController {
|
||||
*/
|
||||
@Deprecated
|
||||
private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
|
||||
|
||||
@Deprecated
|
||||
private static final String KEY_IDLE_DURATION = "idle_duration2";
|
||||
@Deprecated
|
||||
private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
|
||||
|
||||
private static final String KEY_PAROLE_INTERVAL = "parole_interval";
|
||||
private static final String KEY_PAROLE_WINDOW = "parole_window";
|
||||
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_SYSTEM_INTERACTION_HOLD_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_NOTIFICATION_TIMEOUT = 12 * 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_SYNC_ADAPTER_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(',');
|
||||
|
||||
@@ -1733,6 +1776,9 @@ public class AppStandbyController {
|
||||
mSystemInteractionTimeoutMillis = mParser.getDurationMillis
|
||||
(KEY_SYSTEM_INTERACTION_HOLD_DURATION,
|
||||
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
|
||||
mStableChargingThresholdMillis = mParser.getDurationMillis
|
||||
(KEY_STABLE_CHARGING_THRESHOLD,
|
||||
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user