Loading apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +12 −1 Original line number Diff line number Diff line package com.android.server.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; Loading Loading @@ -71,6 +72,16 @@ public interface AppStandbyInternal { long getTimeSinceLastJobRun(String packageName, int userId); void setEstimatedLaunchTime(String packageName, int userId, @CurrentTimeMillisLong long launchTimeMs); /** * Returns the saved estimated launch time for the app. Will return {@code Long#MAX_VALUE} if no * value is saved. */ @CurrentTimeMillisLong long getEstimatedLaunchTime(String packageName, int userId); /** * Returns the time (in milliseconds) since the app was last interacted with by the user. * This can be larger than the current elapsedRealtime, in case it happened before boot or Loading apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +120 −5 Original line number Diff line number Diff line Loading @@ -25,8 +25,12 @@ import static com.android.server.job.controllers.Package.packageToString; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; Loading @@ -38,7 +42,9 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.utils.AlarmQueue; Loading @@ -53,6 +59,9 @@ public class PrefetchController extends StateController { || Log.isLoggable(TAG, Log.DEBUG); private final PcConstants mPcConstants; private final PcHandler mHandler; private final UsageStatsManagerInternal mUsageStatsManagerInternal; @GuardedBy("mLock") private final SparseArrayMap<String, ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>(); Loading @@ -72,11 +81,34 @@ public class PrefetchController extends StateController { @CurrentTimeMillisLong private long mLaunchTimeThresholdMs = PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS; @SuppressWarnings("FieldCanBeLocal") private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener = new EstimatedLaunchTimeChangedListener() { @Override public void onEstimatedLaunchTimeChanged(int userId, @NonNull String packageName, @CurrentTimeMillisLong long newEstimatedLaunchTime) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = packageName; args.argi1 = userId; args.argl1 = newEstimatedLaunchTime; mHandler.obtainMessage(MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME, args) .sendToTarget(); } }; private static final int MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME = 0; private static final int MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME = 1; public PrefetchController(JobSchedulerService service) { super(service); mPcConstants = new PcConstants(); mHandler = new PcHandler(mContext.getMainLooper()); mThresholdAlarmListener = new ThresholdAlarmListener( mContext, JobSchedulerBackgroundThread.get().getLooper()); mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); mUsageStatsManagerInternal .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); } @Override Loading Loading @@ -146,11 +178,14 @@ public class PrefetchController extends StateController { @CurrentTimeMillisLong private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); final Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) { // TODO(194532703): get estimated time from UsageStats nextEstimatedLaunchTime = now + 2 * HOUR_IN_MILLIS; mEstimatedLaunchTimes.add(userId, pkgName, nextEstimatedLaunchTime); // Don't query usage stats here because it may have to read from disk. mHandler.obtainMessage(MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME, userId, 0, pkgName) .sendToTarget(); // Store something in the cache so we don't keep posting retrieval messages. mEstimatedLaunchTimes.add(userId, pkgName, Long.MAX_VALUE); return Long.MAX_VALUE; } return nextEstimatedLaunchTime; } Loading @@ -170,6 +205,42 @@ public class PrefetchController extends StateController { return changed; } private void processUpdatedEstimatedLaunchTime(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long newEstimatedLaunchTime) { if (DEBUG) { Slog.d(TAG, "Estimated launch time for " + packageToString(userId, pkgName) + " changed to " + newEstimatedLaunchTime + " (" + TimeUtils.formatDuration(newEstimatedLaunchTime - sSystemClock.millis()) + " from now)"); } synchronized (mLock) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null) { if (DEBUG) { Slog.i(TAG, "Not caching launch time since we haven't seen any prefetch" + " jobs for " + packageToString(userId, pkgName)); } } else { // Don't bother caching the value unless the app has scheduled prefetch jobs // before. This is based on the assumption that if an app has scheduled a // prefetch job before, then it will probably schedule another one again. mEstimatedLaunchTimes.add(userId, pkgName, newEstimatedLaunchTime); if (!jobs.isEmpty()) { final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed); if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) { mStateChangedListener.onControllerStateChanged(jobs); } } } } } @GuardedBy("mLock") private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { Loading Loading @@ -289,6 +360,49 @@ public class PrefetchController extends StateController { } } private class PcHandler extends Handler { PcHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME: final int userId = msg.arg1; final String pkgName = (String) msg.obj; // It's okay to get the time without holding the lock since all updates to // the local cache go through the handler (and therefore will be sequential). final long nextEstimatedLaunchTime = mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(pkgName, userId); if (DEBUG) { Slog.d(TAG, "Retrieved launch time for " + packageToString(userId, pkgName) + " of " + nextEstimatedLaunchTime + " (" + TimeUtils.formatDuration( nextEstimatedLaunchTime - sSystemClock.millis()) + " from now)"); } synchronized (mLock) { final Long curEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); if (curEstimatedLaunchTime == null || nextEstimatedLaunchTime != curEstimatedLaunchTime) { processUpdatedEstimatedLaunchTime( userId, pkgName, nextEstimatedLaunchTime); } } break; case MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME: final SomeArgs args = (SomeArgs) msg.obj; processUpdatedEstimatedLaunchTime(args.argi1, (String) args.arg1, args.argl1); args.recycle(); break; } } } @VisibleForTesting class PcConstants { private boolean mShouldReevaluateConstraints = false; Loading Loading @@ -366,7 +480,8 @@ public class PrefetchController extends StateController { final String pkgName = mEstimatedLaunchTimes.keyAt(u, p); final long estimatedLaunchTime = mEstimatedLaunchTimes.valueAt(u, p); pw.print("<" + userId + ">" + pkgName + ": "); pw.print(packageToString(userId, pkgName)); pw.print(": "); pw.print(estimatedLaunchTime); pw.print(" ("); TimeUtils.formatDuration(estimatedLaunchTime - now, pw, Loading apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +46 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static com.android.server.usage.AppStandbyController.isUserUsage; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageStatsManager; import android.os.SystemClock; Loading Loading @@ -115,6 +117,8 @@ public class AppIdleHistory { // Reason why the app was last marked for restriction. private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON = "lastRestrictionAttemptReason"; // The next estimated launch time of the app, in ms since epoch. private static final String ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME = "nextEstimatedAppLaunchTime"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration Loading Loading @@ -151,6 +155,9 @@ public class AppIdleHistory { int lastInformedBucket; // The last time a job was run for this app, using elapsed timebase long lastJobRunTime; // The estimated time the app will be launched next, in milliseconds since epoch. @CurrentTimeMillisLong long nextEstimatedLaunchTime; // When should the bucket active 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 Loading Loading @@ -410,6 +417,17 @@ public class AppIdleHistory { app.lastPredictedBucket = bucket; } /** * Marks the next time the app is expected to be launched, in the current millis timebase. */ public void setEstimatedLaunchTime(String packageName, int userId, @ElapsedRealtimeLong long nowElapsed, @CurrentTimeMillisLong long launchTime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, nowElapsed, true); appUsageHistory.nextEstimatedLaunchTime = launchTime; } /** * Marks the last time a job was run, with the given elapsedRealtime. The time stored is * based on the elapsed timebase. Loading Loading @@ -442,6 +460,23 @@ public class AppIdleHistory { appUsageHistory.lastRestrictReason = reason; } /** * Returns the next estimated launch time of this app. Will return {@link Long#MAX_VALUE} if * there's no estimated time. */ @CurrentTimeMillisLong public long getEstimatedLaunchTime(String packageName, int userId, long nowElapsed) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, nowElapsed, false); // Don't adjust the default, else it'll wrap around to a positive value if (appUsageHistory == null || appUsageHistory.nextEstimatedLaunchTime < System.currentTimeMillis()) { return Long.MAX_VALUE; } return appUsageHistory.nextEstimatedLaunchTime; } /** * 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 Loading Loading @@ -671,6 +706,8 @@ public class AppIdleHistory { Slog.wtf(TAG, "Unable to read last restrict reason", nfe); } } appUsageHistory.nextEstimatedLaunchTime = getLongValue(parser, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0); appUsageHistory.lastInformedBucket = -1; userHistory.put(packageName, appUsageHistory); } Loading Loading @@ -753,6 +790,10 @@ public class AppIdleHistory { } xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON, Integer.toHexString(history.lastRestrictReason)); if (history.nextEstimatedLaunchTime > 0) { xml.attribute(null, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, Long.toString(history.nextEstimatedLaunchTime)); } xml.endTag(null, TAG_PACKAGE); } Loading @@ -779,6 +820,7 @@ public class AppIdleHistory { idpw.println(" App Standby States:"); idpw.increaseIndent(); ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); final long now = System.currentTimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); final long totalElapsedTime = getElapsedTime(elapsedRealtime); final long screenOnTime = getScreenOnTime(elapsedRealtime); Loading Loading @@ -819,6 +861,10 @@ public class AppIdleHistory { idpw.print(" lastRestrictReason=" + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason)); } if (appUsageHistory.nextEstimatedLaunchTime > 0) { idpw.print(" nextEstimatedLaunchTime="); TimeUtils.formatDuration(appUsageHistory.nextEstimatedLaunchTime - now, idpw); } idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.println(); } Loading apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +19 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; Loading Loading @@ -1086,6 +1087,24 @@ public class AppStandbyController } } @Override public void setEstimatedLaunchTime(String packageName, int userId, @CurrentTimeMillisLong long launchTime) { final long nowElapsed = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { mAppIdleHistory.setEstimatedLaunchTime(packageName, userId, nowElapsed, launchTime); } } @Override @CurrentTimeMillisLong public long getEstimatedLaunchTime(String packageName, int userId) { final long elapsedRealtime = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { return mAppIdleHistory.getEstimatedLaunchTime(packageName, userId, elapsedRealtime); } } @Override public long getTimeSinceLastUsedByUser(String packageName, int userId) { final long elapsedRealtime = mInjector.elapsedRealtime(); Loading core/java/android/app/usage/UsageEvents.java +5 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package android.app.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; Loading Loading @@ -584,6 +585,7 @@ public final class UsageEvents implements Parcelable { * <p/> * See {@link System#currentTimeMillis()}. */ @CurrentTimeMillisLong public long getTimeStamp() { return mTimeStamp; } Loading Loading @@ -801,6 +803,9 @@ public final class UsageEvents implements Parcelable { * @return true if an event was available, false if there are no more events. */ public boolean getNextEvent(Event eventOut) { if (eventOut == null) { throw new IllegalArgumentException("Given eventOut must not be null"); } if (mIndex >= mEventCount) { return false; } Loading Loading
apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +12 −1 Original line number Diff line number Diff line package com.android.server.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; Loading Loading @@ -71,6 +72,16 @@ public interface AppStandbyInternal { long getTimeSinceLastJobRun(String packageName, int userId); void setEstimatedLaunchTime(String packageName, int userId, @CurrentTimeMillisLong long launchTimeMs); /** * Returns the saved estimated launch time for the app. Will return {@code Long#MAX_VALUE} if no * value is saved. */ @CurrentTimeMillisLong long getEstimatedLaunchTime(String packageName, int userId); /** * Returns the time (in milliseconds) since the app was last interacted with by the user. * This can be larger than the current elapsedRealtime, in case it happened before boot or Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +120 −5 Original line number Diff line number Diff line Loading @@ -25,8 +25,12 @@ import static com.android.server.job.controllers.Package.packageToString; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; Loading @@ -38,7 +42,9 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.utils.AlarmQueue; Loading @@ -53,6 +59,9 @@ public class PrefetchController extends StateController { || Log.isLoggable(TAG, Log.DEBUG); private final PcConstants mPcConstants; private final PcHandler mHandler; private final UsageStatsManagerInternal mUsageStatsManagerInternal; @GuardedBy("mLock") private final SparseArrayMap<String, ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>(); Loading @@ -72,11 +81,34 @@ public class PrefetchController extends StateController { @CurrentTimeMillisLong private long mLaunchTimeThresholdMs = PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS; @SuppressWarnings("FieldCanBeLocal") private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener = new EstimatedLaunchTimeChangedListener() { @Override public void onEstimatedLaunchTimeChanged(int userId, @NonNull String packageName, @CurrentTimeMillisLong long newEstimatedLaunchTime) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = packageName; args.argi1 = userId; args.argl1 = newEstimatedLaunchTime; mHandler.obtainMessage(MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME, args) .sendToTarget(); } }; private static final int MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME = 0; private static final int MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME = 1; public PrefetchController(JobSchedulerService service) { super(service); mPcConstants = new PcConstants(); mHandler = new PcHandler(mContext.getMainLooper()); mThresholdAlarmListener = new ThresholdAlarmListener( mContext, JobSchedulerBackgroundThread.get().getLooper()); mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); mUsageStatsManagerInternal .registerLaunchTimeChangedListener(mEstimatedLaunchTimeChangedListener); } @Override Loading Loading @@ -146,11 +178,14 @@ public class PrefetchController extends StateController { @CurrentTimeMillisLong private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); final Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) { // TODO(194532703): get estimated time from UsageStats nextEstimatedLaunchTime = now + 2 * HOUR_IN_MILLIS; mEstimatedLaunchTimes.add(userId, pkgName, nextEstimatedLaunchTime); // Don't query usage stats here because it may have to read from disk. mHandler.obtainMessage(MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME, userId, 0, pkgName) .sendToTarget(); // Store something in the cache so we don't keep posting retrieval messages. mEstimatedLaunchTimes.add(userId, pkgName, Long.MAX_VALUE); return Long.MAX_VALUE; } return nextEstimatedLaunchTime; } Loading @@ -170,6 +205,42 @@ public class PrefetchController extends StateController { return changed; } private void processUpdatedEstimatedLaunchTime(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long newEstimatedLaunchTime) { if (DEBUG) { Slog.d(TAG, "Estimated launch time for " + packageToString(userId, pkgName) + " changed to " + newEstimatedLaunchTime + " (" + TimeUtils.formatDuration(newEstimatedLaunchTime - sSystemClock.millis()) + " from now)"); } synchronized (mLock) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null) { if (DEBUG) { Slog.i(TAG, "Not caching launch time since we haven't seen any prefetch" + " jobs for " + packageToString(userId, pkgName)); } } else { // Don't bother caching the value unless the app has scheduled prefetch jobs // before. This is based on the assumption that if an app has scheduled a // prefetch job before, then it will probably schedule another one again. mEstimatedLaunchTimes.add(userId, pkgName, newEstimatedLaunchTime); if (!jobs.isEmpty()) { final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed); if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) { mStateChangedListener.onControllerStateChanged(jobs); } } } } } @GuardedBy("mLock") private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { Loading Loading @@ -289,6 +360,49 @@ public class PrefetchController extends StateController { } } private class PcHandler extends Handler { PcHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_RETRIEVE_ESTIMATED_LAUNCH_TIME: final int userId = msg.arg1; final String pkgName = (String) msg.obj; // It's okay to get the time without holding the lock since all updates to // the local cache go through the handler (and therefore will be sequential). final long nextEstimatedLaunchTime = mUsageStatsManagerInternal .getEstimatedPackageLaunchTime(pkgName, userId); if (DEBUG) { Slog.d(TAG, "Retrieved launch time for " + packageToString(userId, pkgName) + " of " + nextEstimatedLaunchTime + " (" + TimeUtils.formatDuration( nextEstimatedLaunchTime - sSystemClock.millis()) + " from now)"); } synchronized (mLock) { final Long curEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); if (curEstimatedLaunchTime == null || nextEstimatedLaunchTime != curEstimatedLaunchTime) { processUpdatedEstimatedLaunchTime( userId, pkgName, nextEstimatedLaunchTime); } } break; case MSG_PROCESS_UPDATED_ESTIMATED_LAUNCH_TIME: final SomeArgs args = (SomeArgs) msg.obj; processUpdatedEstimatedLaunchTime(args.argi1, (String) args.arg1, args.argl1); args.recycle(); break; } } } @VisibleForTesting class PcConstants { private boolean mShouldReevaluateConstraints = false; Loading Loading @@ -366,7 +480,8 @@ public class PrefetchController extends StateController { final String pkgName = mEstimatedLaunchTimes.keyAt(u, p); final long estimatedLaunchTime = mEstimatedLaunchTimes.valueAt(u, p); pw.print("<" + userId + ">" + pkgName + ": "); pw.print(packageToString(userId, pkgName)); pw.print(": "); pw.print(estimatedLaunchTime); pw.print(" ("); TimeUtils.formatDuration(estimatedLaunchTime - now, pw, Loading
apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +46 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static com.android.server.usage.AppStandbyController.isUserUsage; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageStatsManager; import android.os.SystemClock; Loading Loading @@ -115,6 +117,8 @@ public class AppIdleHistory { // Reason why the app was last marked for restriction. private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON = "lastRestrictionAttemptReason"; // The next estimated launch time of the app, in ms since epoch. private static final String ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME = "nextEstimatedAppLaunchTime"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration Loading Loading @@ -151,6 +155,9 @@ public class AppIdleHistory { int lastInformedBucket; // The last time a job was run for this app, using elapsed timebase long lastJobRunTime; // The estimated time the app will be launched next, in milliseconds since epoch. @CurrentTimeMillisLong long nextEstimatedLaunchTime; // When should the bucket active 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 Loading Loading @@ -410,6 +417,17 @@ public class AppIdleHistory { app.lastPredictedBucket = bucket; } /** * Marks the next time the app is expected to be launched, in the current millis timebase. */ public void setEstimatedLaunchTime(String packageName, int userId, @ElapsedRealtimeLong long nowElapsed, @CurrentTimeMillisLong long launchTime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, nowElapsed, true); appUsageHistory.nextEstimatedLaunchTime = launchTime; } /** * Marks the last time a job was run, with the given elapsedRealtime. The time stored is * based on the elapsed timebase. Loading Loading @@ -442,6 +460,23 @@ public class AppIdleHistory { appUsageHistory.lastRestrictReason = reason; } /** * Returns the next estimated launch time of this app. Will return {@link Long#MAX_VALUE} if * there's no estimated time. */ @CurrentTimeMillisLong public long getEstimatedLaunchTime(String packageName, int userId, long nowElapsed) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, nowElapsed, false); // Don't adjust the default, else it'll wrap around to a positive value if (appUsageHistory == null || appUsageHistory.nextEstimatedLaunchTime < System.currentTimeMillis()) { return Long.MAX_VALUE; } return appUsageHistory.nextEstimatedLaunchTime; } /** * 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 Loading Loading @@ -671,6 +706,8 @@ public class AppIdleHistory { Slog.wtf(TAG, "Unable to read last restrict reason", nfe); } } appUsageHistory.nextEstimatedLaunchTime = getLongValue(parser, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0); appUsageHistory.lastInformedBucket = -1; userHistory.put(packageName, appUsageHistory); } Loading Loading @@ -753,6 +790,10 @@ public class AppIdleHistory { } xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON, Integer.toHexString(history.lastRestrictReason)); if (history.nextEstimatedLaunchTime > 0) { xml.attribute(null, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, Long.toString(history.nextEstimatedLaunchTime)); } xml.endTag(null, TAG_PACKAGE); } Loading @@ -779,6 +820,7 @@ public class AppIdleHistory { idpw.println(" App Standby States:"); idpw.increaseIndent(); ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId); final long now = System.currentTimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); final long totalElapsedTime = getElapsedTime(elapsedRealtime); final long screenOnTime = getScreenOnTime(elapsedRealtime); Loading Loading @@ -819,6 +861,10 @@ public class AppIdleHistory { idpw.print(" lastRestrictReason=" + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason)); } if (appUsageHistory.nextEstimatedLaunchTime > 0) { idpw.print(" nextEstimatedLaunchTime="); TimeUtils.formatDuration(appUsageHistory.nextEstimatedLaunchTime - now, idpw); } idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.println(); } Loading
apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +19 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; Loading Loading @@ -1086,6 +1087,24 @@ public class AppStandbyController } } @Override public void setEstimatedLaunchTime(String packageName, int userId, @CurrentTimeMillisLong long launchTime) { final long nowElapsed = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { mAppIdleHistory.setEstimatedLaunchTime(packageName, userId, nowElapsed, launchTime); } } @Override @CurrentTimeMillisLong public long getEstimatedLaunchTime(String packageName, int userId) { final long elapsedRealtime = mInjector.elapsedRealtime(); synchronized (mAppIdleLock) { return mAppIdleHistory.getEstimatedLaunchTime(packageName, userId, elapsedRealtime); } } @Override public long getTimeSinceLastUsedByUser(String packageName, int userId) { final long elapsedRealtime = mInjector.elapsedRealtime(); Loading
core/java/android/app/usage/UsageEvents.java +5 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package android.app.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; Loading Loading @@ -584,6 +585,7 @@ public final class UsageEvents implements Parcelable { * <p/> * See {@link System#currentTimeMillis()}. */ @CurrentTimeMillisLong public long getTimeStamp() { return mTimeStamp; } Loading Loading @@ -801,6 +803,9 @@ public final class UsageEvents implements Parcelable { * @return true if an event was available, false if there are no more events. */ public boolean getNextEvent(Event eventOut) { if (eventOut == null) { throw new IllegalArgumentException("Given eventOut must not be null"); } if (mIndex >= mEventCount) { return false; } Loading