Merge "Fallback to ML prediction after short ACTIVE states" into pi-dev
am: 50c069e489
Change-Id: Idd2b0abba22ec10e263fe3ab49ecc7346b9bc941
This commit is contained in:
@@ -184,6 +184,9 @@ public final class UsageStatsManager {
|
||||
/** @hide */
|
||||
public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV = 0x000A;
|
||||
|
||||
/** @hide */
|
||||
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
|
||||
STANDBY_BUCKET_EXEMPTED,
|
||||
@@ -620,36 +623,41 @@ public final class UsageStatsManager {
|
||||
break;
|
||||
case REASON_MAIN_PREDICTED:
|
||||
sb.append("p");
|
||||
switch (standbyReason & REASON_SUB_MASK) {
|
||||
case REASON_SUB_PREDICTED_RESTORED:
|
||||
sb.append("-r");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case REASON_MAIN_TIMEOUT:
|
||||
sb.append("t");
|
||||
break;
|
||||
case REASON_MAIN_USAGE:
|
||||
sb.append("u-");
|
||||
sb.append("u");
|
||||
switch (standbyReason & REASON_SUB_MASK) {
|
||||
case REASON_SUB_USAGE_SYSTEM_INTERACTION:
|
||||
sb.append("si");
|
||||
sb.append("-si");
|
||||
break;
|
||||
case REASON_SUB_USAGE_NOTIFICATION_SEEN:
|
||||
sb.append("ns");
|
||||
sb.append("-ns");
|
||||
break;
|
||||
case REASON_SUB_USAGE_USER_INTERACTION:
|
||||
sb.append("ui");
|
||||
sb.append("-ui");
|
||||
break;
|
||||
case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
|
||||
sb.append("mf");
|
||||
sb.append("-mf");
|
||||
break;
|
||||
case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
|
||||
sb.append("mb");
|
||||
sb.append("-mb");
|
||||
break;
|
||||
case REASON_SUB_USAGE_SYSTEM_UPDATE:
|
||||
sb.append("su");
|
||||
sb.append("-su");
|
||||
break;
|
||||
case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
|
||||
sb.append("at");
|
||||
sb.append("-at");
|
||||
break;
|
||||
case REASON_SUB_USAGE_SYNC_ADAPTER:
|
||||
sb.append("sa");
|
||||
sb.append("-sa");
|
||||
break;
|
||||
case REASON_SUB_USAGE_SLICE_PINNED:
|
||||
sb.append("slp");
|
||||
|
||||
@@ -281,6 +281,7 @@ public class AppStandbyControllerTests {
|
||||
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
|
||||
mInjector = new MyInjector(myContext, Looper.getMainLooper());
|
||||
mController = setupController();
|
||||
setChargingState(mController, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -381,8 +382,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testForcedIdle() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
mController.forceIdleState(PACKAGE_1, USER_ID, true);
|
||||
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
|
||||
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
|
||||
@@ -395,8 +394,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testNotificationEvent() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
|
||||
mInjector.mElapsedRealtime = 1;
|
||||
@@ -410,8 +407,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testSlicePinnedEvent() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
|
||||
mInjector.mElapsedRealtime = 1;
|
||||
@@ -425,8 +420,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testSlicePinnedPrivEvent() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
mController.forceIdleState(PACKAGE_1, USER_ID, true);
|
||||
reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
|
||||
@@ -434,14 +427,13 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testPredictionTimedout() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
// Set it to timeout or usage, so that prediction can override it
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
|
||||
REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
|
||||
REASON_MAIN_TIMEOUT, HOUR_MS);
|
||||
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
|
||||
REASON_MAIN_PREDICTED, 1 * HOUR_MS);
|
||||
REASON_MAIN_PREDICTED, HOUR_MS);
|
||||
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
|
||||
|
||||
// Fast forward 12 hours
|
||||
@@ -464,7 +456,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testOverrides() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
// Can force to NEVER
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
|
||||
REASON_MAIN_FORCED, 1 * HOUR_MS);
|
||||
@@ -494,8 +485,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
@@ -505,19 +494,19 @@ public class AppStandbyControllerTests {
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
// bucketing works after timeout
|
||||
mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100;
|
||||
mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertBucket(STANDBY_BUCKET_WORKING_SET);
|
||||
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
|
||||
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
|
||||
// Use recent prediction
|
||||
assertBucket(STANDBY_BUCKET_FREQUENT);
|
||||
|
||||
// Way past prediction timeout, use system thresholds
|
||||
mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertBucket(STANDBY_BUCKET_RARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCascadingTimeouts() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
@@ -539,8 +528,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testOverlappingTimeouts() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
@@ -596,8 +583,6 @@ public class AppStandbyControllerTests {
|
||||
|
||||
@Test
|
||||
public void testPredictionNotOverridden() throws Exception {
|
||||
setChargingState(mController, false);
|
||||
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
@@ -622,6 +607,31 @@ public class AppStandbyControllerTests {
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPredictionStrikesBack() throws Exception {
|
||||
reportEvent(mController, USER_INTERACTION, 0);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
// Predict to FREQUENT
|
||||
mInjector.mElapsedRealtime = RARE_THRESHOLD;
|
||||
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
|
||||
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
|
||||
assertBucket(STANDBY_BUCKET_FREQUENT);
|
||||
|
||||
// Add a short timeout event
|
||||
mInjector.mElapsedRealtime += 1000;
|
||||
reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
mInjector.mElapsedRealtime += 1000;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertBucket(STANDBY_BUCKET_ACTIVE);
|
||||
|
||||
// Verify it reverted to predicted
|
||||
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
|
||||
mController.checkIdleStates(USER_ID);
|
||||
assertBucket(STANDBY_BUCKET_FREQUENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddActiveDeviceAdmin() {
|
||||
assertActiveAdmins(USER_ID, (String[]) null);
|
||||
|
||||
@@ -70,6 +70,8 @@ public class AppIdleHistory {
|
||||
private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
|
||||
private static final long ONE_MINUTE = 60 * 1000;
|
||||
|
||||
private static final int STANDBY_BUCKET_UNKNOWN = -1;
|
||||
|
||||
@VisibleForTesting
|
||||
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
|
||||
private static final String TAG_PACKAGES = "packages";
|
||||
@@ -111,6 +113,9 @@ public class AppIdleHistory {
|
||||
long lastUsedScreenTime;
|
||||
// Last predicted time using elapsed timebase
|
||||
long lastPredictedTime;
|
||||
// Last predicted bucket
|
||||
@UsageStatsManager.StandbyBuckets
|
||||
int lastPredictedBucket = STANDBY_BUCKET_UNKNOWN;
|
||||
// Standby bucket
|
||||
@UsageStatsManager.StandbyBuckets
|
||||
int currentBucket;
|
||||
@@ -342,6 +347,7 @@ public class AppIdleHistory {
|
||||
appUsageHistory.bucketingReason = reason;
|
||||
if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
|
||||
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
|
||||
appUsageHistory.lastPredictedBucket = bucket;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
|
||||
@@ -349,6 +355,17 @@ public class AppIdleHistory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the prediction for the app but don't change the actual bucket
|
||||
* @param app The app for which the prediction was made
|
||||
* @param elapsedTimeAdjusted The elapsed time in the elapsed duration timebase
|
||||
* @param bucket The predicted bucket
|
||||
*/
|
||||
public void updateLastPrediction(AppUsageHistory app, long elapsedTimeAdjusted, int bucket) {
|
||||
app.lastPredictedTime = elapsedTimeAdjusted;
|
||||
app.lastPredictedBucket = bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the last time a job was run, with the given elapsedRealtime. The time stored is
|
||||
* based on the elapsed timebase.
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
|
||||
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
|
||||
@@ -539,19 +540,30 @@ public class AppStandbyController {
|
||||
}
|
||||
final int oldBucket = app.currentBucket;
|
||||
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
|
||||
boolean predictionLate = false;
|
||||
boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
|
||||
// Compute age-based bucket
|
||||
if (oldMainReason == REASON_MAIN_DEFAULT
|
||||
|| oldMainReason == REASON_MAIN_USAGE
|
||||
|| oldMainReason == REASON_MAIN_TIMEOUT
|
||||
|| (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
|
||||
newBucket = getBucketForLocked(packageName, userId,
|
||||
elapsedRealtime);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
|
||||
|| predictionLate) {
|
||||
|
||||
if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
|
||||
&& app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
|
||||
newBucket = app.lastPredictedBucket;
|
||||
reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
|
||||
}
|
||||
} else {
|
||||
newBucket = getBucketForLocked(packageName, userId,
|
||||
elapsedRealtime);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
|
||||
}
|
||||
reason = REASON_MAIN_TIMEOUT;
|
||||
}
|
||||
reason = REASON_MAIN_TIMEOUT;
|
||||
}
|
||||
|
||||
// Check if the app is within one of the timeouts for forced bucket elevation
|
||||
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
|
||||
if (newBucket >= STANDBY_BUCKET_ACTIVE
|
||||
@@ -588,8 +600,7 @@ public class AppStandbyController {
|
||||
|
||||
/** Returns true if there hasn't been a prediction for the app in a while. */
|
||||
private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
|
||||
return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
|
||||
&& app.lastPredictedTime > 0
|
||||
return app.lastPredictedTime > 0
|
||||
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
|
||||
- app.lastPredictedTime > mPredictionTimeoutMillis;
|
||||
}
|
||||
@@ -1035,6 +1046,10 @@ public class AppStandbyController {
|
||||
if (predicted) {
|
||||
// Check if the app is within one of the timeouts for forced bucket elevation
|
||||
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
|
||||
// In case of not using the prediction, just keep track of it for applying after
|
||||
// ACTIVE or WORKING_SET timeout.
|
||||
mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
|
||||
|
||||
if (newBucket > STANDBY_BUCKET_ACTIVE
|
||||
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
|
||||
newBucket = STANDBY_BUCKET_ACTIVE;
|
||||
|
||||
Reference in New Issue
Block a user