Merge "Improve reporting of bucketing reason"

This commit is contained in:
TreeHugger Robot
2018-02-24 00:24:03 +00:00
committed by Android (Google) Code Review
17 changed files with 327 additions and 173 deletions

View File

@@ -109,7 +109,8 @@ public final class UsageEvents implements Parcelable {
public static final int NOTIFICATION_SEEN = 10;
/**
* An event type denoting a change in App Standby Bucket.
* An event type denoting a change in App Standby Bucket. Additional bucket information
* is contained in mBucketAndReason.
* @hide
*/
@SystemApi
@@ -180,11 +181,12 @@ public final class UsageEvents implements Parcelable {
public String[] mContentAnnotations;
/**
* The app standby bucket assigned.
* The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
* is the low order 16 bits.
* Only present for {@link #STANDBY_BUCKET_CHANGED} event types
* {@hide}
*/
public int mBucket;
public int mBucketAndReason;
/** @hide */
@EventFlags
@@ -205,7 +207,7 @@ public final class UsageEvents implements Parcelable {
mContentType = orig.mContentType;
mContentAnnotations = orig.mContentAnnotations;
mFlags = orig.mFlags;
mBucket = orig.mBucket;
mBucketAndReason = orig.mBucketAndReason;
}
/**
@@ -268,7 +270,19 @@ public final class UsageEvents implements Parcelable {
*/
@SystemApi
public int getStandbyBucket() {
return mBucket;
return (mBucketAndReason & 0xFFFF0000) >>> 16;
}
/**
* Returns the reason for the bucketing, if the event is of type
* {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
* the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
* are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
* is REASON_MAIN_USAGE.
* @hide
*/
public int getStandbyReason() {
return mBucketAndReason & 0x0000FFFF;
}
/** @hide */
@@ -428,7 +442,7 @@ public final class UsageEvents implements Parcelable {
p.writeStringArray(event.mContentAnnotations);
break;
case Event.STANDBY_BUCKET_CHANGED:
p.writeInt(event.mBucket);
p.writeInt(event.mBucketAndReason);
break;
}
}
@@ -474,7 +488,7 @@ public final class UsageEvents implements Parcelable {
eventOut.mContentAnnotations = p.createStringArray();
break;
case Event.STANDBY_BUCKET_CHANGED:
eventOut.mBucket = p.readInt();
eventOut.mBucketAndReason = p.readInt();
break;
}
}

View File

@@ -132,24 +132,37 @@ public final class UsageStatsManager {
@SystemApi
public static final int STANDBY_BUCKET_NEVER = 50;
/** {@hide} Reason for bucketing -- default initial state */
public static final String REASON_DEFAULT = "default";
/** @hide */
public static final int REASON_MAIN_MASK = 0xFF00;
/** @hide */
public static final int REASON_MAIN_DEFAULT = 0x0100;
/** @hide */
public static final int REASON_MAIN_TIMEOUT = 0x0200;
/** @hide */
public static final int REASON_MAIN_USAGE = 0x0300;
/** @hide */
public static final int REASON_MAIN_FORCED = 0x0400;
/** @hide */
public static final int REASON_MAIN_PREDICTED = 0x0500;
/** {@hide} Reason for bucketing -- timeout */
public static final String REASON_TIMEOUT = "timeout";
/** {@hide} Reason for bucketing -- usage */
public static final String REASON_USAGE = "usage";
/** {@hide} Reason for bucketing -- forced by user / shell command */
public static final String REASON_FORCED = "forced";
/**
* {@hide}
* Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
* be appended.
*/
public static final String REASON_PREDICTED = "predicted";
/** @hide */
public static final int REASON_SUB_MASK = 0x00FF;
/** @hide */
public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
/** @hide */
public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN = 0x0002;
/** @hide */
public static final int REASON_SUB_USAGE_USER_INTERACTION = 0x0003;
/** @hide */
public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
/** @hide */
public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
/** @hide */
public static final int REASON_SUB_USAGE_SYSTEM_UPDATE = 0x0006;
/** @hide */
public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT = 0x0007;
/** @hide */
public static final int REASON_SUB_USAGE_SYNC_ADAPTER = 0x0008;
/** @hide */
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -427,6 +440,55 @@ public final class UsageStatsManager {
}
}
/** @hide */
public static String reasonToString(int standbyReason) {
StringBuilder sb = new StringBuilder();
switch (standbyReason & REASON_MAIN_MASK) {
case REASON_MAIN_DEFAULT:
sb.append("d");
break;
case REASON_MAIN_FORCED:
sb.append("f");
break;
case REASON_MAIN_PREDICTED:
sb.append("p");
break;
case REASON_MAIN_TIMEOUT:
sb.append("t");
break;
case REASON_MAIN_USAGE:
sb.append("u-");
switch (standbyReason & REASON_SUB_MASK) {
case REASON_SUB_USAGE_SYSTEM_INTERACTION:
sb.append("si");
break;
case REASON_SUB_USAGE_NOTIFICATION_SEEN:
sb.append("ns");
break;
case REASON_SUB_USAGE_USER_INTERACTION:
sb.append("ui");
break;
case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
sb.append("mf");
break;
case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
sb.append("mb");
break;
case REASON_SUB_USAGE_SYSTEM_UPDATE:
sb.append("su");
break;
case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
sb.append("at");
break;
case REASON_SUB_USAGE_SYNC_ADAPTER:
sb.append("sa");
break;
}
break;
}
return sb.toString();
}
/**
* {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app

View File

@@ -139,7 +139,7 @@ public abstract class UsageStatsManagerInternal {
/** Callback to inform listeners that the idle state has changed to a new bucket. */
public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
boolean idle, int bucket);
boolean idle, int bucket, int reason);
/**
* Callback to inform listeners that the parole state has changed. This means apps are

View File

@@ -3860,7 +3860,7 @@ class AlarmManagerService extends SystemService {
final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket) {
boolean idle, int bucket, int reason) {
if (DEBUG_STANDBY) {
Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
bucket);

View File

@@ -678,7 +678,7 @@ public class AppStateTracker {
final class StandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket) {
int bucket, int reason) {
if (DEBUG) {
Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);

View File

@@ -85,7 +85,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;

View File

@@ -2240,7 +2240,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket) {
boolean idle, int bucket, int reason) {
final int uid = mLocalPM.getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (uid < 0) {

View File

@@ -186,7 +186,8 @@ public final class AppIdleController extends StateController {
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
int reason) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {

View File

@@ -3947,7 +3947,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
int reason) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);

View File

@@ -15,6 +15,9 @@
*/
package com.android.server;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static com.android.server.AppStateTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
@@ -612,7 +615,7 @@ public class AppStateTrackerTest {
// Exempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -624,7 +627,7 @@ public class AppStateTrackerTest {
// Exempt package 1 on user-0.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -632,7 +635,7 @@ public class AppStateTrackerTest {
// Unexempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
UsageStatsManager.STANDBY_BUCKET_ACTIVE);
UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -644,9 +647,9 @@ public class AppStateTrackerTest {
mPowerSaveObserver.accept(getPowerSaveState());
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
setAppOps(UID_1, PACKAGE_1, true);

View File

@@ -16,10 +16,13 @@
package com.android.server.usage;
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
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_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -92,18 +95,18 @@ public class AppIdleHistoryTests extends AndroidTestCase {
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
UsageStatsManager.REASON_USAGE);
REASON_MAIN_USAGE);
// ACTIVE means not idle
assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
UsageStatsManager.REASON_USAGE);
REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
REASON_TIMEOUT);
REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
// RARE is considered idle
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
@@ -115,7 +118,7 @@ public class AppIdleHistoryTests extends AndroidTestCase {
aih = new AppIdleHistory(mStorageDir, 4000);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
@@ -133,4 +136,18 @@ public class AppIdleHistoryTests extends AndroidTestCase {
assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
}
public void testReason() throws Exception {
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_TIMEOUT);
aih.writeAppIdleTimes(USER_ID);
aih = new AppIdleHistory(mStorageDir, 5000);
assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
}
}

View File

@@ -18,11 +18,11 @@ package com.android.server.usage;
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_FORCED;
import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
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.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -410,11 +410,11 @@ public class AppStandbyControllerTests {
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_TIMEOUT, 1 * HOUR_MS);
REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
@@ -440,28 +440,28 @@ public class AppStandbyControllerTests {
setChargingState(mController, false);
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_FORCED, 1 * HOUR_MS);
REASON_MAIN_FORCED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_FORCED, 1 * HOUR_MS);
REASON_MAIN_FORCED, 1 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_PREDICTED, 1 * HOUR_MS);
REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
// Prediction can't override NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_DEFAULT, 2 * HOUR_MS);
REASON_MAIN_DEFAULT, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_PREDICTED, 2 * HOUR_MS);
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_USAGE, 2 * HOUR_MS);
REASON_MAIN_USAGE, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_PREDICTED, 2 * HOUR_MS);
REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
}
@@ -474,7 +474,7 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -483,7 +483,7 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -498,15 +498,15 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_PREDICTED, 1000);
REASON_MAIN_PREDICTED, 1000);
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -527,18 +527,18 @@ public class AppStandbyControllerTests {
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -561,7 +561,7 @@ public class AppStandbyControllerTests {
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_PREDICTED, mInjector.mElapsedRealtime);
REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction

View File

@@ -16,10 +16,12 @@
package com.android.server.usage;
import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_FORCED;
import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
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_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -112,8 +114,10 @@ public class AppIdleHistory {
// Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
// Reason for setting the standby bucket. TODO: Switch to int.
String bucketingReason;
// Reason for setting the standby bucket. The value here is a combination of
// one of UsageStatsManager.REASON_MAIN_* and one (or none) of
// UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
int bucketingReason;
// In-memory only, last bucket for which the listeners were informed
int lastInformedBucket;
// The last time a job was run for this app, using elapsed timebase
@@ -212,13 +216,14 @@ public class AppIdleHistory {
* @param appUsageHistory the usage record for the app being updated
* @param packageName name of the app being updated, for logging purposes
* @param newBucket the bucket to set the app to
* @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
* @param elapsedRealtime mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
int newBucket, long elapsedRealtime, long timeout) {
int newBucket, int usageReason, long elapsedRealtime, long timeout) {
// Set the timeout if applicable
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
@@ -246,10 +251,10 @@ public class AppIdleHistory {
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
.currentBucket
+ ", reason=" + appUsageHistory.bucketingReason);
+ ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
appUsageHistory.bucketingReason = REASON_USAGE;
appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
return appUsageHistory;
}
@@ -262,16 +267,17 @@ public class AppIdleHistory {
* @param packageName
* @param userId
* @param newBucket the bucket to set the app to
* @param elapsedRealtime mark as used time if non-zero
* @param usageReason sub reason for usage
* @param nowElapsed mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
long nowElapsed, long timeout) {
int usageReason, long nowElapsed, long timeout) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -293,7 +299,7 @@ public class AppIdleHistory {
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.lastPredictedTime = getElapsedTime(0);
appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
appUsageHistory.bucketingReason = REASON_DEFAULT;
appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
userHistory.put(packageName, appUsageHistory);
@@ -328,18 +334,18 @@ public class AppIdleHistory {
}
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
int bucket, String reason) {
int bucket, int reason) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
if (reason.startsWith(REASON_PREDICTED)) {
if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
}
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ ", reason=" + appUsageHistory.bucketingReason);
+ ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
@@ -393,11 +399,11 @@ public class AppIdleHistory {
return buckets;
}
public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, false);
return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
}
public long getElapsedTime(long elapsedRealtime) {
@@ -411,11 +417,11 @@ public class AppIdleHistory {
elapsedRealtime, true);
if (idle) {
appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
appUsageHistory.bucketingReason = REASON_FORCED;
appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
} else {
appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
appUsageHistory.bucketingReason = REASON_USAGE;
appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
}
return appUsageHistory.currentBucket;
}
@@ -516,7 +522,7 @@ public class AppIdleHistory {
appUsageHistory.currentBucket = currentBucketString == null
? STANDBY_BUCKET_ACTIVE
: Integer.parseInt(currentBucketString);
appUsageHistory.bucketingReason =
String bucketingReason =
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
appUsageHistory.lastJobRunTime = getLongValue(parser,
ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
@@ -524,8 +530,13 @@ public class AppIdleHistory {
ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
if (appUsageHistory.bucketingReason == null) {
appUsageHistory.bucketingReason = REASON_DEFAULT;
appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
if (bucketingReason != null) {
try {
appUsageHistory.bucketingReason =
Integer.parseInt(bucketingReason, 16);
} catch (NumberFormatException nfe) {
}
}
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
@@ -574,7 +585,8 @@ public class AppIdleHistory {
Long.toString(history.lastPredictedTime));
xml.attribute(null, ATTR_CURRENT_BUCKET,
Integer.toString(history.currentBucket));
xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
xml.attribute(null, ATTR_BUCKETING_REASON,
Integer.toHexString(history.bucketingReason));
if (history.bucketActiveTimeoutTime > 0) {
xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
.bucketActiveTimeoutTime));
@@ -600,7 +612,7 @@ public class AppIdleHistory {
}
public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
idpw.println("Package idle stats:");
idpw.println("App Standby States:");
idpw.increaseIndent();
ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -615,24 +627,25 @@ public class AppIdleHistory {
continue;
}
idpw.print("package=" + packageName);
idpw.print(" userId=" + userId);
idpw.print(" lastUsedElapsed=");
idpw.print(" u=" + userId);
idpw.print(" bucket=" + appUsageHistory.currentBucket
+ " reason="
+ UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
idpw.print(" used=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
idpw.print(" lastUsedScreenOn=");
idpw.print(" usedScr=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
idpw.print(" lastPredictedTime=");
idpw.print(" lastPred=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
idpw.print(" bucketActiveTimeoutTime=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
idpw.print(" activeLeft=");
TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
idpw);
idpw.print(" bucketWorkingSetTimeoutTime=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
idpw.print(" wsLeft=");
TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
idpw);
idpw.print(" lastJobRunTime=");
idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.print(" bucket=" + appUsageHistory.currentBucket
+ " reason=" + appUsageHistory.bucketingReason);
idpw.println();
}
idpw.println();

View File

@@ -16,11 +16,20 @@
package com.android.server.usage;
import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_FORCED;
import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
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_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;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -129,8 +138,8 @@ public class AppStandbyController {
STANDBY_BUCKET_RARE
};
// Expiration time for predicted bucket
private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
/** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
/**
* Indicates the maximum wait time for admin data to be available;
@@ -187,6 +196,8 @@ public class AppStandbyController {
long mNotificationSeenTimeoutMillis;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis;
/** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
long mPredictionTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
@@ -223,24 +234,30 @@ public class AppStandbyController {
// Whether the bucket change is because the user has started interacting with the app
boolean isUserInteraction;
StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) {
// Reason for bucket change
int reason;
StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
boolean isInteraction) {
this.packageName = pkgName;
this.userId = userId;
this.bucket = bucket;
this.reason = reason;
this.isUserInteraction = isInteraction;
}
public static StandbyUpdateRecord obtain(String pkgName, int userId,
int bucket, boolean isInteraction) {
int bucket, int reason, boolean isInteraction) {
synchronized (sStandbyUpdatePool) {
final int size = sStandbyUpdatePool.size();
if (size < 1) {
return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction);
return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
}
StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
r.packageName = pkgName;
r.userId = userId;
r.bucket = bucket;
r.reason = reason;
r.isUserInteraction = isInteraction;
return r;
}
@@ -339,10 +356,11 @@ public class AppStandbyController {
if (!packageName.equals(providerPkgName)) {
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
STANDBY_BUCKET_ACTIVE, elapsedRealtime,
STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
elapsedRealtime,
elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
appUsage.currentBucket, false);
appUsage.currentBucket, appUsage.bucketingReason, false);
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -497,50 +515,54 @@ public class AppStandbyController {
if (isSpecial) {
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
STANDBY_BUCKET_EXEMPTED, false);
STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
String reason = app.bucketingReason;
int reason = app.bucketingReason;
final int oldMainReason = reason & REASON_MAIN_MASK;
// If the bucket was forced by the user/developer, leave it alone.
// A usage event will be the only way to bring it out of this forced state
if (REASON_FORCED.equals(app.bucketingReason)) {
if (oldMainReason == REASON_MAIN_FORCED) {
return;
}
final int oldBucket = app.currentBucket;
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
boolean predictionLate = false;
// Compute age-based bucket
if (REASON_DEFAULT.equals(app.bucketingReason)
|| REASON_USAGE.equals(app.bucketingReason)
|| REASON_TIMEOUT.equals(app.bucketingReason)
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);
}
reason = REASON_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
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
reason = REASON_USAGE;
reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket >= STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
reason = REASON_USAGE;
// If it was already there, keep the reason, else assume timeout to WS
reason = (newBucket == oldBucket)
? app.bucketingReason
: REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -553,43 +575,41 @@ public class AppStandbyController {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
maybeInformListeners(packageName, userId, elapsedRealtime,
newBucket, false);
newBucket, reason, false);
}
}
}
}
/** 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 != null
&& app.bucketingReason.startsWith(REASON_PREDICTED)
return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
&& app.lastPredictedTime > 0
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
- app.lastPredictedTime > PREDICTION_TIMEOUT;
}
private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
long elapsedRealtime) {
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
&& app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
- app.lastPredictedTime > mPredictionTimeoutMillis;
}
/** Inform listeners if the bucket has changed since it was last reported to listeners */
private void maybeInformListeners(String packageName, int userId,
long elapsedRealtime, int bucket, boolean userStartedInteracting) {
long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
synchronized (mAppIdleLock) {
// TODO: fold these into one call + lookup for efficiency if needed
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
elapsedRealtime, bucket)) {
StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting);
final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
bucket, reason, userStartedInteracting);
if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
StandbyUpdateRecord.obtain(packageName, userId,
bucket, userStartedInteracting)));
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
}
}
}
/**
* Evaluates next bucket based on time since last used and the bucketing thresholds.
* @param packageName the app
* @param userId the user
* @param elapsedRealtime as the name suggests, current elapsed time
* @return the bucket for the app, based on time since last used
*/
@GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
@@ -675,17 +695,20 @@ public class AppStandbyController {
final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
event.mPackage, userId, elapsedRealtime);
final int prevBucket = appHistory.currentBucket;
final String prevBucketReason = appHistory.bucketingReason;
final int prevBucketReason = appHistory.bucketingReason;
final long nextCheckTime;
final int subReason = usageEventToSubReason(event.mEventType);
final int reason = REASON_MAIN_USAGE | subReason;
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
STANDBY_BUCKET_WORKING_SET,
STANDBY_BUCKET_WORKING_SET, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckTime = mNotificationSeenTimeoutMillis;
} else {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
STANDBY_BUCKET_ACTIVE,
STANDBY_BUCKET_ACTIVE, subReason,
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckTime = mStrongUsageTimeoutMillis;
}
@@ -695,9 +718,9 @@ public class AppStandbyController {
final boolean userStartedInteracting =
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
prevBucket != appHistory.currentBucket &&
prevBucketReason != REASON_USAGE;
(prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
maybeInformListeners(event.mPackage, userId, elapsedRealtime,
appHistory.currentBucket, userStartedInteracting);
appHistory.currentBucket, reason, userStartedInteracting);
if (previouslyIdle) {
notifyBatteryStats(event.mPackage, userId, false);
@@ -706,6 +729,17 @@ public class AppStandbyController {
}
}
private int usageEventToSubReason(int eventType) {
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
default: return 0;
}
}
/**
* Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
* then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
@@ -731,7 +765,8 @@ public class AppStandbyController {
userId, elapsedRealtime);
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false);
maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
REASON_MAIN_FORCED, false);
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
@@ -962,11 +997,11 @@ public class AppStandbyController {
}
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
String reason, long elapsedRealtime) {
int reason, long elapsedRealtime) {
synchronized (mAppIdleLock) {
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
@@ -979,7 +1014,7 @@ public class AppStandbyController {
}
// If the bucket was forced, don't allow prediction to override
if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
@@ -989,14 +1024,18 @@ public class AppStandbyController {
if (newBucket > STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
reason = REASON_USAGE;
reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket > STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
reason = REASON_USAGE;
if (app.currentBucket != newBucket) {
reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
} else {
reason = app.bucketingReason;
}
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -1006,7 +1045,7 @@ public class AppStandbyController {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason);
}
maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false);
maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
@VisibleForTesting
@@ -1106,11 +1145,12 @@ public class AppStandbyController {
return packageName != null && packageName.equals(activeScorer);
}
void informListeners(String packageName, int userId, int bucket, boolean userInteraction) {
void informListeners(String packageName, int userId, int bucket, int reason,
boolean userInteraction) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
synchronized (mPackageAccessListeners) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
if (userInteraction) {
listener.onUserInteractionStarted(packageName, userId);
}
@@ -1188,7 +1228,8 @@ public class AppStandbyController {
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
// Mark app as used for 2 hours. After that it can timeout to whatever the
// past usage pattern was.
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
@@ -1369,7 +1410,8 @@ public class AppStandbyController {
switch (msg.what) {
case MSG_INFORM_LISTENERS:
StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction);
informListeners(r.packageName, r.userId, r.bucket, r.reason,
r.isUserInteraction);
r.recycle();
break;
@@ -1477,7 +1519,7 @@ public class AppStandbyController {
"notification_seen_duration";
private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
"system_update_usage_duration";
private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1547,6 +1589,9 @@ public class AppStandbyController {
mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
(KEY_SYSTEM_UPDATE_HOLD_DURATION,
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
mPredictionTimeoutMillis = mParser.getDurationMillis
(KEY_PREDICTION_TIMEOUT,
COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
}
}

View File

@@ -120,10 +120,10 @@ public class UsageStatsService extends SystemService implements
new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket) {
int bucket, int reason) {
Event event = new Event();
event.mEventType = Event.STANDBY_BUCKET_CHANGED;
event.mBucket = bucket;
event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
@@ -741,9 +741,9 @@ public class UsageStatsService extends SystemService implements
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
final String reason = shellCaller
? UsageStatsManager.REASON_FORCED
: UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
final int reason = shellCaller
? UsageStatsManager.REASON_MAIN_FORCED
: UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
// Caller cannot set their own standby state
@@ -798,9 +798,9 @@ public class UsageStatsService extends SystemService implements
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
final String reason = shellCaller
? UsageStatsManager.REASON_FORCED
: UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
final int reason = shellCaller
? UsageStatsManager.REASON_MAIN_FORCED
: UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
final long elapsedRealtime = SystemClock.elapsedRealtime();

View File

@@ -26,10 +26,7 @@ import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
import java.io.IOException;
import java.net.ProtocolException;
@@ -175,7 +172,7 @@ final class UsageStatsXmlV1 {
event.mShortcutId = (id != null) ? id.intern() : null;
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
break;
}
@@ -281,8 +278,8 @@ final class UsageStatsXmlV1 {
}
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
if (event.mBucket != 0) {
XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
if (event.mBucketAndReason != 0) {
XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
}
}

View File

@@ -520,7 +520,8 @@ class UserUsageStatsService {
pw.printPair("shortcutId", event.mShortcutId);
}
if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
pw.printPair("standbyBucket", event.mBucket);
pw.printPair("standbyBucket", event.getStandbyBucket());
pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
}
pw.printHexPair("flags", event.mFlags);
pw.println();