Loading core/java/android/app/usage/UsageStatsManagerInternal.java +48 −16 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.usage; import android.annotation.UserIdInt; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.content.ComponentName; import android.content.res.Configuration; Loading @@ -37,7 +38,7 @@ public abstract class UsageStatsManagerInternal { * @param eventType The event that occurred. Valid values can be found at * {@link UsageEvents} */ public abstract void reportEvent(ComponentName component, int userId, int eventType); public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType); /** * Reports an event to the UsageStatsManager. Loading @@ -47,14 +48,14 @@ public abstract class UsageStatsManagerInternal { * @param eventType The event that occurred. Valid values can be found at * {@link UsageEvents} */ public abstract void reportEvent(String packageName, int userId, int eventType); public abstract void reportEvent(String packageName, @UserIdInt int userId, int eventType); /** * Reports a configuration change to the UsageStatsManager. * * @param config The new device configuration. */ public abstract void reportConfigurationChange(Configuration config, int userId); public abstract void reportConfigurationChange(Configuration config, @UserIdInt int userId); /** * Reports that an action equivalent to a ShortcutInfo is taken by the user. Loading @@ -65,7 +66,8 @@ public abstract class UsageStatsManagerInternal { * * @see android.content.pm.ShortcutManager#reportShortcutUsed(String) */ public abstract void reportShortcutUsage(String packageName, String shortcutId, int userId); public abstract void reportShortcutUsage(String packageName, String shortcutId, @UserIdInt int userId); /** * Reports that a content provider has been accessed by a foreground app. Loading @@ -73,7 +75,8 @@ public abstract class UsageStatsManagerInternal { * @param pkgName The package name of the content provider * @param userId The user in which the content provider was accessed. */ public abstract void reportContentProviderUsage(String name, String pkgName, int userId); public abstract void reportContentProviderUsage(String name, String pkgName, @UserIdInt int userId); /** * Prepares the UsageStatsService for shutdown. Loading @@ -89,7 +92,7 @@ public abstract class UsageStatsManagerInternal { * @param userId * @return */ public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId); public abstract boolean isAppIdle(String packageName, int uidForAppId, @UserIdInt int userId); /** * Returns the app standby bucket that the app is currently in. This accessor does Loading @@ -101,15 +104,15 @@ public abstract class UsageStatsManagerInternal { * @return the AppStandby bucket code the app currently resides in. If the app is * unknown in the given user, STANDBY_BUCKET_NEVER is returned. */ @StandbyBuckets public abstract int getAppStandbyBucket(String packageName, int userId, long nowElapsed); @StandbyBuckets public abstract int getAppStandbyBucket(String packageName, @UserIdInt int userId, long nowElapsed); /** * Returns all of the uids for a given user where all packages associating with that uid * are in the app idle state -- there are no associated apps that are not idle. This means * all of the returned uids can be safely considered app idle. */ public abstract int[] getIdleUidsForUser(int userId); public abstract int[] getIdleUidsForUser(@UserIdInt int userId); /** * @return True if currently app idle parole mode is on. This means all idle apps are allow to Loading @@ -134,8 +137,8 @@ public abstract class UsageStatsManagerInternal { public static abstract class AppIdleStateChangeListener { /** Callback to inform listeners that the idle state has changed to a new bucket. */ public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket); public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId, boolean idle, int bucket); /** * Callback to inform listeners that the parole state has changed. This means apps are Loading @@ -144,10 +147,16 @@ public abstract class UsageStatsManagerInternal { public abstract void onParoleStateChanged(boolean isParoleOn); } /* Backup/Restore API */ public abstract byte[] getBackupPayload(int user, String key); /** Backup/Restore API */ public abstract byte[] getBackupPayload(@UserIdInt int userId, String key); public abstract void applyRestoredPayload(int user, String key, byte[] payload); /** * ? * @param userId * @param key * @param payload */ public abstract void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload); /** * Return usage stats. Loading @@ -155,6 +164,29 @@ public abstract class UsageStatsManagerInternal { * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the * result. */ public abstract List<UsageStats> queryUsageStatsForUser( int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps); public abstract List<UsageStats> queryUsageStatsForUser(@UserIdInt int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps); /** * Used to persist the last time a job was run for this app, in order to make decisions later * whether a job should be deferred until later. The time passed in should be in elapsed * realtime since boot. * @param packageName the app that executed a job. * @param userId the user associated with the job. * @param elapsedRealtime the time when the job was executed, in elapsed realtime millis since * boot. */ public abstract void setLastJobRunTime(String packageName, @UserIdInt int userId, long elapsedRealtime); /** * Returns the time in millis since a job was executed for this app, in elapsed realtime * timebase. This value can be larger than the current elapsed realtime if the job was executed * before the device was rebooted. The default value is {@link Long#MAX_VALUE}. * @param packageName the app you're asking about. * @param userId the user associated with the job. * @return the time in millis since a job was last executed for the app, provided it was * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}. */ public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId); } services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +16 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.app.usage.UsageStatsManager; import android.os.FileUtils; import android.test.AndroidTestCase; Loading Loading @@ -117,4 +121,16 @@ public class AppIdleHistoryTests extends AndroidTestCase { assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT)); } public void testJobRunTime() throws Exception { AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000); aih.setLastJobRunTime(PACKAGE_1, USER_ID, 2000); assertEquals(Long.MAX_VALUE, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 0)); assertEquals(4000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 6000)); aih.setLastJobRunTime(PACKAGE_2, USER_ID, 6000); assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000)); assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000)); } } No newline at end of file services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +31 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.view.Display; Loading @@ -69,6 +71,8 @@ import java.util.List; * Unit test for AppStandbyController. */ @RunWith(AndroidJUnit4.class) @Presubmit @SmallTest public class AppStandbyControllerTests { private static final String PACKAGE_1 = "com.example.foo"; Loading Loading @@ -285,6 +289,10 @@ public class AppStandbyControllerTests { true); } private void assertBucket(int bucket) { assertEquals(bucket, getStandbyBucket(mController)); } @Test public void testBuckets() throws Exception { assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); Loading Loading @@ -425,4 +433,27 @@ public class AppStandbyControllerTests { REASON_PREDICTED, 2 * HOUR_MS); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); } @Test public void testTimeout() throws Exception { setChargingState(mController, false); reportEvent(mController, USER_INTERACTION, 0); assertBucket(STANDBY_BUCKET_ACTIVE); mInjector.mElapsedRealtime = 2000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, REASON_PREDICTED, mInjector.mElapsedRealtime); assertBucket(STANDBY_BUCKET_ACTIVE); // bucketing works after timeout mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_WORKING_SET); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, REASON_PREDICTED, mInjector.mElapsedRealtime); assertBucket(STANDBY_BUCKET_FREQUENT); } } services/usage/java/com/android/server/usage/AppIdleHistory.java +94 −92 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.app.usage.UsageStatsManager.REASON_USAGE; 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; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import android.app.usage.UsageStatsManager; import android.os.SystemClock; Loading Loading @@ -66,13 +65,7 @@ public class AppIdleHistory { // History for all users and all packages private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); private long mLastPeriod = 0; private static final long ONE_MINUTE = 60 * 1000; private static final int HISTORY_SIZE = 100; private static final int FLAG_LAST_STATE = 2; private static final int FLAG_PARTIAL_ACTIVE = 1; private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE : 60 * ONE_MINUTE; @VisibleForTesting static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; Loading @@ -89,6 +82,10 @@ public class AppIdleHistory { private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; // The reason the app was put in the above bucket private static final String ATTR_BUCKETING_REASON = "bucketReason"; // The last time a job was run for this app private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime"; // The time when the forced active state can be overridden. private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration Loading @@ -103,8 +100,6 @@ public class AppIdleHistory { private boolean mScreenOn; static class AppUsageHistory { // Debug final byte[] recent = new byte[HISTORY_SIZE]; // Last used time using elapsed timebase long lastUsedElapsedTime; // Last used time using screen_on timebase Loading @@ -118,6 +113,13 @@ public class AppIdleHistory { String 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 long lastJobRunTime; // When should the bucket state timeout, in elapsed timebase, if greater than // lastUsedElapsedTime. // This is used to keep the app in a high bucket regardless of other timeouts and // predictions. long bucketTimeoutTime; } AppIdleHistory(File storageDir, long elapsedRealtime) { Loading Loading @@ -195,81 +197,47 @@ public class AppIdleHistory { writeScreenOnTime(); } public int reportUsage(String packageName, int userId, long elapsedRealtime) { /** * Mark the app as used and update the bucket if necessary. If there is a timeout specified * that's in the future, then the usage event is temporary and keeps the app in the specified * bucket at least until the timeout is reached. This can be used to keep the app in an * elevated bucket for a while until some important task gets to run. * @param packageName * @param userId * @param bucket the bucket to set the app to * @param elapsedRealtime mark as used time if non-zero * @param timeout set the timeout of the specified bucket, if non-zero * @return */ public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime, long timeout) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); shiftHistoryToNow(userHistory, elapsedRealtime); if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) { appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket + ", reason=" + appUsageHistory.bucketingReason); } } appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; } public int reportMildUsage(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) { appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET; if (appUsageHistory.currentBucket > bucket) { appUsageHistory.currentBucket = bucket; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory .currentBucket + ", reason=" + appUsageHistory.bucketingReason); } if (timeout > elapsedRealtime) { // Convert to elapsed timebase appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); } } // TODO: Should this be a different reason for partial usage? appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; } public void setIdle(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); shiftHistoryToNow(userHistory, elapsedRealtime); appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; } private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory, long elapsedRealtime) { long thisPeriod = elapsedRealtime / PERIOD_DURATION; // Has the period switched over? Slide all users' package histories if (mLastPeriod != 0 && mLastPeriod < thisPeriod && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { int diff = (int) (thisPeriod - mLastPeriod); final int NUSERS = mIdleHistory.size(); for (int u = 0; u < NUSERS; u++) { userHistory = mIdleHistory.valueAt(u); for (AppUsageHistory idleState : userHistory.values()) { // Shift left System.arraycopy(idleState.recent, diff, idleState.recent, 0, HISTORY_SIZE - diff); // Replicate last state across the diff for (int i = 0; i < diff; i++) { idleState.recent[HISTORY_SIZE - i - 1] = (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); } } } } mLastPeriod = thisPeriod; } private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); if (userHistory == null) { Loading @@ -291,6 +259,7 @@ public class AppIdleHistory { appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = REASON_DEFAULT; appUsageHistory.lastInformedBucket = -1; appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago userHistory.put(packageName, appUsageHistory); } return appUsageHistory; Loading Loading @@ -338,6 +307,38 @@ public class AppIdleHistory { } } /** * Marks the last time a job was run, with the given elapsedRealtime. The time stored is * based on the elapsed timebase. * @param packageName * @param userId * @param elapsedRealtime */ public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime); } /** * Returns the time since the last job was run for this app. This can be larger than the * current elapsedRealtime, in case it happened before boot or a really large value if no jobs * were ever run. * @param packageName * @param userId * @param elapsedRealtime * @return */ public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); // Don't adjust the default, else it'll wrap around to a positive value if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE; return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime; } public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = Loading Loading @@ -473,12 +474,8 @@ public class AppIdleHistory { Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); String lastPredictedTimeString = parser.getAttributeValue(null, ATTR_LAST_PREDICTED_TIME); if (lastPredictedTimeString != null) { appUsageHistory.lastPredictedTime = Long.parseLong(lastPredictedTimeString); } appUsageHistory.lastPredictedTime = getLongValue(parser, ATTR_LAST_PREDICTED_TIME, 0L); String currentBucketString = parser.getAttributeValue(null, ATTR_CURRENT_BUCKET); appUsageHistory.currentBucket = currentBucketString == null Loading @@ -486,6 +483,10 @@ public class AppIdleHistory { : Integer.parseInt(currentBucketString); appUsageHistory.bucketingReason = parser.getAttributeValue(null, ATTR_BUCKETING_REASON); appUsageHistory.lastJobRunTime = getLongValue(parser, ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE); appUsageHistory.bucketTimeoutTime = getLongValue(parser, ATTR_BUCKET_TIMEOUT_TIME, 0L); if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = REASON_DEFAULT; } Loading @@ -501,6 +502,12 @@ public class AppIdleHistory { } } private long getLongValue(XmlPullParser parser, String attrName, long defValue) { String value = parser.getAttributeValue(null, attrName); if (value == null) return defValue; return Long.parseLong(value); } public void writeAppIdleTimes(int userId) { FileOutputStream fos = null; AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); Loading Loading @@ -531,6 +538,14 @@ public class AppIdleHistory { xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); if (history.bucketTimeoutTime > 0) { xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history .bucketTimeoutTime)); } if (history.lastJobRunTime != Long.MIN_VALUE) { xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history .lastJobRunTime)); } xml.endTag(null, TAG_PACKAGE); } Loading Loading @@ -565,6 +580,10 @@ public class AppIdleHistory { TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPredictedTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); idpw.print(" bucketTimeoutTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw); idpw.print(" lastJobRunTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" bucket=" + appUsageHistory.currentBucket + " reason=" + appUsageHistory.bucketingReason); Loading @@ -579,21 +598,4 @@ public class AppIdleHistory { idpw.println(); idpw.decreaseIndent(); } public void dumpHistory(IndentingPrintWriter idpw, int userId) { ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); final long elapsedRealtime = SystemClock.elapsedRealtime(); if (userHistory == null) return; final int P = userHistory.size(); for (int p = 0; p < P; p++) { final String packageName = userHistory.keyAt(p); final byte[] history = userHistory.valueAt(p).recent; for (int i = 0; i < HISTORY_SIZE; i++) { idpw.print(history[i] == 0 ? '.' : 'A'); } idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" " + packageName); idpw.println(); } } } services/usage/java/com/android/server/usage/AppStandbyController.java +46 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/usage/UsageStatsManagerInternal.java +48 −16 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.usage; import android.annotation.UserIdInt; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.content.ComponentName; import android.content.res.Configuration; Loading @@ -37,7 +38,7 @@ public abstract class UsageStatsManagerInternal { * @param eventType The event that occurred. Valid values can be found at * {@link UsageEvents} */ public abstract void reportEvent(ComponentName component, int userId, int eventType); public abstract void reportEvent(ComponentName component, @UserIdInt int userId, int eventType); /** * Reports an event to the UsageStatsManager. Loading @@ -47,14 +48,14 @@ public abstract class UsageStatsManagerInternal { * @param eventType The event that occurred. Valid values can be found at * {@link UsageEvents} */ public abstract void reportEvent(String packageName, int userId, int eventType); public abstract void reportEvent(String packageName, @UserIdInt int userId, int eventType); /** * Reports a configuration change to the UsageStatsManager. * * @param config The new device configuration. */ public abstract void reportConfigurationChange(Configuration config, int userId); public abstract void reportConfigurationChange(Configuration config, @UserIdInt int userId); /** * Reports that an action equivalent to a ShortcutInfo is taken by the user. Loading @@ -65,7 +66,8 @@ public abstract class UsageStatsManagerInternal { * * @see android.content.pm.ShortcutManager#reportShortcutUsed(String) */ public abstract void reportShortcutUsage(String packageName, String shortcutId, int userId); public abstract void reportShortcutUsage(String packageName, String shortcutId, @UserIdInt int userId); /** * Reports that a content provider has been accessed by a foreground app. Loading @@ -73,7 +75,8 @@ public abstract class UsageStatsManagerInternal { * @param pkgName The package name of the content provider * @param userId The user in which the content provider was accessed. */ public abstract void reportContentProviderUsage(String name, String pkgName, int userId); public abstract void reportContentProviderUsage(String name, String pkgName, @UserIdInt int userId); /** * Prepares the UsageStatsService for shutdown. Loading @@ -89,7 +92,7 @@ public abstract class UsageStatsManagerInternal { * @param userId * @return */ public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId); public abstract boolean isAppIdle(String packageName, int uidForAppId, @UserIdInt int userId); /** * Returns the app standby bucket that the app is currently in. This accessor does Loading @@ -101,15 +104,15 @@ public abstract class UsageStatsManagerInternal { * @return the AppStandby bucket code the app currently resides in. If the app is * unknown in the given user, STANDBY_BUCKET_NEVER is returned. */ @StandbyBuckets public abstract int getAppStandbyBucket(String packageName, int userId, long nowElapsed); @StandbyBuckets public abstract int getAppStandbyBucket(String packageName, @UserIdInt int userId, long nowElapsed); /** * Returns all of the uids for a given user where all packages associating with that uid * are in the app idle state -- there are no associated apps that are not idle. This means * all of the returned uids can be safely considered app idle. */ public abstract int[] getIdleUidsForUser(int userId); public abstract int[] getIdleUidsForUser(@UserIdInt int userId); /** * @return True if currently app idle parole mode is on. This means all idle apps are allow to Loading @@ -134,8 +137,8 @@ public abstract class UsageStatsManagerInternal { public static abstract class AppIdleStateChangeListener { /** Callback to inform listeners that the idle state has changed to a new bucket. */ public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket); public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId, boolean idle, int bucket); /** * Callback to inform listeners that the parole state has changed. This means apps are Loading @@ -144,10 +147,16 @@ public abstract class UsageStatsManagerInternal { public abstract void onParoleStateChanged(boolean isParoleOn); } /* Backup/Restore API */ public abstract byte[] getBackupPayload(int user, String key); /** Backup/Restore API */ public abstract byte[] getBackupPayload(@UserIdInt int userId, String key); public abstract void applyRestoredPayload(int user, String key, byte[] payload); /** * ? * @param userId * @param key * @param payload */ public abstract void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload); /** * Return usage stats. Loading @@ -155,6 +164,29 @@ public abstract class UsageStatsManagerInternal { * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the * result. */ public abstract List<UsageStats> queryUsageStatsForUser( int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps); public abstract List<UsageStats> queryUsageStatsForUser(@UserIdInt int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps); /** * Used to persist the last time a job was run for this app, in order to make decisions later * whether a job should be deferred until later. The time passed in should be in elapsed * realtime since boot. * @param packageName the app that executed a job. * @param userId the user associated with the job. * @param elapsedRealtime the time when the job was executed, in elapsed realtime millis since * boot. */ public abstract void setLastJobRunTime(String packageName, @UserIdInt int userId, long elapsedRealtime); /** * Returns the time in millis since a job was executed for this app, in elapsed realtime * timebase. This value can be larger than the current elapsed realtime if the job was executed * before the device was rebooted. The default value is {@link Long#MAX_VALUE}. * @param packageName the app you're asking about. * @param userId the user associated with the job. * @return the time in millis since a job was last executed for the app, provided it was * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}. */ public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId); }
services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +16 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.app.usage.UsageStatsManager; import android.os.FileUtils; import android.test.AndroidTestCase; Loading Loading @@ -117,4 +121,16 @@ public class AppIdleHistoryTests extends AndroidTestCase { assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT)); } public void testJobRunTime() throws Exception { AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000); aih.setLastJobRunTime(PACKAGE_1, USER_ID, 2000); assertEquals(Long.MAX_VALUE, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 0)); assertEquals(4000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 6000)); aih.setLastJobRunTime(PACKAGE_2, USER_ID, 6000); assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000)); assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000)); } } No newline at end of file
services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +31 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.view.Display; Loading @@ -69,6 +71,8 @@ import java.util.List; * Unit test for AppStandbyController. */ @RunWith(AndroidJUnit4.class) @Presubmit @SmallTest public class AppStandbyControllerTests { private static final String PACKAGE_1 = "com.example.foo"; Loading Loading @@ -285,6 +289,10 @@ public class AppStandbyControllerTests { true); } private void assertBucket(int bucket) { assertEquals(bucket, getStandbyBucket(mController)); } @Test public void testBuckets() throws Exception { assertTimeout(mController, 0, STANDBY_BUCKET_NEVER); Loading Loading @@ -425,4 +433,27 @@ public class AppStandbyControllerTests { REASON_PREDICTED, 2 * HOUR_MS); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController)); } @Test public void testTimeout() throws Exception { setChargingState(mController, false); reportEvent(mController, USER_INTERACTION, 0); assertBucket(STANDBY_BUCKET_ACTIVE); mInjector.mElapsedRealtime = 2000; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, REASON_PREDICTED, mInjector.mElapsedRealtime); assertBucket(STANDBY_BUCKET_ACTIVE); // bucketing works after timeout mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100; mController.checkIdleStates(USER_ID); assertBucket(STANDBY_BUCKET_WORKING_SET); mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, REASON_PREDICTED, mInjector.mElapsedRealtime); assertBucket(STANDBY_BUCKET_FREQUENT); } }
services/usage/java/com/android/server/usage/AppIdleHistory.java +94 −92 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.app.usage.UsageStatsManager.REASON_USAGE; 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; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import android.app.usage.UsageStatsManager; import android.os.SystemClock; Loading Loading @@ -66,13 +65,7 @@ public class AppIdleHistory { // History for all users and all packages private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>(); private long mLastPeriod = 0; private static final long ONE_MINUTE = 60 * 1000; private static final int HISTORY_SIZE = 100; private static final int FLAG_LAST_STATE = 2; private static final int FLAG_PARTIAL_ACTIVE = 1; private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE : 60 * ONE_MINUTE; @VisibleForTesting static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; Loading @@ -89,6 +82,10 @@ public class AppIdleHistory { private static final String ATTR_CURRENT_BUCKET = "appLimitBucket"; // The reason the app was put in the above bucket private static final String ATTR_BUCKETING_REASON = "bucketReason"; // The last time a job was run for this app private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime"; // The time when the forced active state can be overridden. private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration Loading @@ -103,8 +100,6 @@ public class AppIdleHistory { private boolean mScreenOn; static class AppUsageHistory { // Debug final byte[] recent = new byte[HISTORY_SIZE]; // Last used time using elapsed timebase long lastUsedElapsedTime; // Last used time using screen_on timebase Loading @@ -118,6 +113,13 @@ public class AppIdleHistory { String 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 long lastJobRunTime; // When should the bucket state timeout, in elapsed timebase, if greater than // lastUsedElapsedTime. // This is used to keep the app in a high bucket regardless of other timeouts and // predictions. long bucketTimeoutTime; } AppIdleHistory(File storageDir, long elapsedRealtime) { Loading Loading @@ -195,81 +197,47 @@ public class AppIdleHistory { writeScreenOnTime(); } public int reportUsage(String packageName, int userId, long elapsedRealtime) { /** * Mark the app as used and update the bucket if necessary. If there is a timeout specified * that's in the future, then the usage event is temporary and keeps the app in the specified * bucket at least until the timeout is reached. This can be used to keep the app in an * elevated bucket for a while until some important task gets to run. * @param packageName * @param userId * @param bucket the bucket to set the app to * @param elapsedRealtime mark as used time if non-zero * @param timeout set the timeout of the specified bucket, if non-zero * @return */ public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime, long timeout) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); shiftHistoryToNow(userHistory, elapsedRealtime); if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) { appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket + ", reason=" + appUsageHistory.bucketingReason); } } appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; } public int reportMildUsage(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) { appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET; if (appUsageHistory.currentBucket > bucket) { appUsageHistory.currentBucket = bucket; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory .currentBucket + ", reason=" + appUsageHistory.bucketingReason); } if (timeout > elapsedRealtime) { // Convert to elapsed timebase appUsageHistory.bucketTimeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); } } // TODO: Should this be a different reason for partial usage? appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; } public void setIdle(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); shiftHistoryToNow(userHistory, elapsedRealtime); appUsageHistory.recent[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; } private void shiftHistoryToNow(ArrayMap<String, AppUsageHistory> userHistory, long elapsedRealtime) { long thisPeriod = elapsedRealtime / PERIOD_DURATION; // Has the period switched over? Slide all users' package histories if (mLastPeriod != 0 && mLastPeriod < thisPeriod && (thisPeriod - mLastPeriod) < HISTORY_SIZE - 1) { int diff = (int) (thisPeriod - mLastPeriod); final int NUSERS = mIdleHistory.size(); for (int u = 0; u < NUSERS; u++) { userHistory = mIdleHistory.valueAt(u); for (AppUsageHistory idleState : userHistory.values()) { // Shift left System.arraycopy(idleState.recent, diff, idleState.recent, 0, HISTORY_SIZE - diff); // Replicate last state across the diff for (int i = 0; i < diff; i++) { idleState.recent[HISTORY_SIZE - i - 1] = (byte) (idleState.recent[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); } } } } mLastPeriod = thisPeriod; } private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); if (userHistory == null) { Loading @@ -291,6 +259,7 @@ public class AppIdleHistory { appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = REASON_DEFAULT; appUsageHistory.lastInformedBucket = -1; appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago userHistory.put(packageName, appUsageHistory); } return appUsageHistory; Loading Loading @@ -338,6 +307,38 @@ public class AppIdleHistory { } } /** * Marks the last time a job was run, with the given elapsedRealtime. The time stored is * based on the elapsed timebase. * @param packageName * @param userId * @param elapsedRealtime */ public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); appUsageHistory.lastJobRunTime = getElapsedTime(elapsedRealtime); } /** * Returns the time since the last job was run for this app. This can be larger than the * current elapsedRealtime, in case it happened before boot or a really large value if no jobs * were ever run. * @param packageName * @param userId * @param elapsedRealtime * @return */ public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); // Don't adjust the default, else it'll wrap around to a positive value if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE; return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime; } public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = Loading Loading @@ -473,12 +474,8 @@ public class AppIdleHistory { Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); String lastPredictedTimeString = parser.getAttributeValue(null, ATTR_LAST_PREDICTED_TIME); if (lastPredictedTimeString != null) { appUsageHistory.lastPredictedTime = Long.parseLong(lastPredictedTimeString); } appUsageHistory.lastPredictedTime = getLongValue(parser, ATTR_LAST_PREDICTED_TIME, 0L); String currentBucketString = parser.getAttributeValue(null, ATTR_CURRENT_BUCKET); appUsageHistory.currentBucket = currentBucketString == null Loading @@ -486,6 +483,10 @@ public class AppIdleHistory { : Integer.parseInt(currentBucketString); appUsageHistory.bucketingReason = parser.getAttributeValue(null, ATTR_BUCKETING_REASON); appUsageHistory.lastJobRunTime = getLongValue(parser, ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE); appUsageHistory.bucketTimeoutTime = getLongValue(parser, ATTR_BUCKET_TIMEOUT_TIME, 0L); if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = REASON_DEFAULT; } Loading @@ -501,6 +502,12 @@ public class AppIdleHistory { } } private long getLongValue(XmlPullParser parser, String attrName, long defValue) { String value = parser.getAttributeValue(null, attrName); if (value == null) return defValue; return Long.parseLong(value); } public void writeAppIdleTimes(int userId) { FileOutputStream fos = null; AtomicFile appIdleFile = new AtomicFile(getUserFile(userId)); Loading Loading @@ -531,6 +538,14 @@ public class AppIdleHistory { xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); if (history.bucketTimeoutTime > 0) { xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history .bucketTimeoutTime)); } if (history.lastJobRunTime != Long.MIN_VALUE) { xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history .lastJobRunTime)); } xml.endTag(null, TAG_PACKAGE); } Loading Loading @@ -565,6 +580,10 @@ public class AppIdleHistory { TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPredictedTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); idpw.print(" bucketTimeoutTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw); idpw.print(" lastJobRunTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" bucket=" + appUsageHistory.currentBucket + " reason=" + appUsageHistory.bucketingReason); Loading @@ -579,21 +598,4 @@ public class AppIdleHistory { idpw.println(); idpw.decreaseIndent(); } public void dumpHistory(IndentingPrintWriter idpw, int userId) { ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); final long elapsedRealtime = SystemClock.elapsedRealtime(); if (userHistory == null) return; final int P = userHistory.size(); for (int p = 0; p < P; p++) { final String packageName = userHistory.keyAt(p); final byte[] history = userHistory.valueAt(p).recent; for (int i = 0; i < HISTORY_SIZE; i++) { idpw.print(history[i] == 0 ? '.' : 'A'); } idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.print(" " + packageName); idpw.println(); } } }
services/usage/java/com/android/server/usage/AppStandbyController.java +46 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes