Merge "Fallback to ML prediction after short ACTIVE states" into pi-dev

am: 50c069e489

Change-Id: Idd2b0abba22ec10e263fe3ab49ecc7346b9bc941
This commit is contained in:
Amith Yamasani
2018-03-29 06:18:19 +00:00
committed by android-build-merger
4 changed files with 93 additions and 43 deletions

View File

@@ -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");

View File

@@ -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);

View File

@@ -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.

View File

@@ -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;