Loading core/java/android/app/usage/UsageStatsManagerInternal.java +21 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,14 @@ public abstract class UsageStatsManagerInternal { * allowed to do work even if they're idle or in a low bucket. */ public abstract void onParoleStateChanged(boolean isParoleOn); /** * Optional callback to inform the listener that the app has transitioned into * an active state due to user interaction. */ public void onUserInteractionStarted(String packageName, @UserIdInt int userId) { // No-op by default } } /** Backup/Restore API */ Loading Loading @@ -212,4 +220,17 @@ public abstract class UsageStatsManagerInternal { * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}. */ public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId); /** * Report a few data points about an app's job state at the current time. * * @param packageName the app whose job state is being described * @param userId which user the app is associated with * @param numDeferredJobs the number of pending jobs that were deferred * due to bucketing policy * @param timeSinceLastJobRun number of milliseconds since the last time one of * this app's jobs was executed */ public abstract void reportAppJobState(String packageName, @UserIdInt int userId, int numDeferredJobs, long timeSinceLastJobRun); } services/core/java/com/android/server/job/JobSchedulerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,14 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); /** * The user has started interacting with the app. Take any appropriate action. */ void reportAppUsage(String packageName, int userId); /** * Report a snapshot of sync-related jobs back to the sync manager */ JobStorePersistStats getPersistStats(); /** Loading services/core/java/com/android/server/job/JobSchedulerService.java +44 −0 Original line number Diff line number Diff line Loading @@ -1071,6 +1071,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } void reportAppUsage(String packageName, int userId) { // This app just transitioned into interactive use or near equivalent, so we should // take a look at its job state for feedback purposes. } /** * Initializes the system service. * <p> Loading Loading @@ -2150,6 +2155,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } @Override public void reportAppUsage(String packageName, int userId) { JobSchedulerService.this.reportAppUsage(packageName, userId); } @Override public JobStorePersistStats getPersistStats() { synchronized (mLock) { Loading Loading @@ -2212,6 +2222,40 @@ public final class JobSchedulerService extends com.android.server.SystemService } mInParole = isParoleOn; } @Override public void onUserInteractionStarted(String packageName, int userId) { final int uid = mLocalPM.getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); if (uid < 0) { // Quietly ignore; the case is already logged elsewhere return; } final long sinceLast = sElapsedRealtimeClock.millis() - mUsageStats.getTimeSinceLastJobRun(packageName, userId); final DeferredJobCounter counter = new DeferredJobCounter(); synchronized (mLock) { mJobs.forEachJobForSourceUid(uid, counter); } mUsageStats.reportAppJobState(packageName, userId, counter.numDeferred(), sinceLast); } } static class DeferredJobCounter implements JobStatusFunctor { private int mDeferred = 0; public int numDeferred() { return mDeferred; } @Override public void process(JobStatus job) { if (job.getWhenStandbyDeferred() > 0) { mDeferred++; } } } public static int standbyBucketToBucketIndex(int bucket) { Loading services/core/java/com/android/server/job/JobServiceContext.java +7 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.job.IJobService; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobWorkItem; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; /** Loading Loading @@ -238,6 +240,11 @@ public final class JobServiceContext implements ServiceConnection { } } UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class); usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(), mExecutionStartTimeElapsed); // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. job.clearPersistedUtcTimes(); Loading services/usage/java/com/android/server/usage/AppIdleHistory.java +29 −12 Original line number Diff line number Diff line Loading @@ -36,6 +36,8 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerInternal; import libcore.io.IoUtils; Loading Loading @@ -202,27 +204,23 @@ public class AppIdleHistory { * 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 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 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); public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int newBucket, long elapsedRealtime, long timeout) { if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); } if (appUsageHistory.currentBucket > bucket) { appUsageHistory.currentBucket = bucket; if (appUsageHistory.currentBucket > newBucket) { appUsageHistory.currentBucket = newBucket; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory .currentBucket Loading @@ -235,7 +233,26 @@ public class AppIdleHistory { } appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; return appUsageHistory; } /** * 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 newBucket 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 AppUsageHistory reportUsage(String packageName, int userId, int newBucket, long nowElapsed, long timeout) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true); return reportUsage(history, packageName, newBucket, nowElapsed, timeout); } private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { Loading Loading
core/java/android/app/usage/UsageStatsManagerInternal.java +21 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,14 @@ public abstract class UsageStatsManagerInternal { * allowed to do work even if they're idle or in a low bucket. */ public abstract void onParoleStateChanged(boolean isParoleOn); /** * Optional callback to inform the listener that the app has transitioned into * an active state due to user interaction. */ public void onUserInteractionStarted(String packageName, @UserIdInt int userId) { // No-op by default } } /** Backup/Restore API */ Loading Loading @@ -212,4 +220,17 @@ public abstract class UsageStatsManagerInternal { * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}. */ public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId); /** * Report a few data points about an app's job state at the current time. * * @param packageName the app whose job state is being described * @param userId which user the app is associated with * @param numDeferredJobs the number of pending jobs that were deferred * due to bucketing policy * @param timeSinceLastJobRun number of milliseconds since the last time one of * this app's jobs was executed */ public abstract void reportAppJobState(String packageName, @UserIdInt int userId, int numDeferredJobs, long timeSinceLastJobRun); }
services/core/java/com/android/server/job/JobSchedulerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,14 @@ public interface JobSchedulerInternal { void removeBackingUpUid(int uid); void clearAllBackingUpUids(); /** * The user has started interacting with the app. Take any appropriate action. */ void reportAppUsage(String packageName, int userId); /** * Report a snapshot of sync-related jobs back to the sync manager */ JobStorePersistStats getPersistStats(); /** Loading
services/core/java/com/android/server/job/JobSchedulerService.java +44 −0 Original line number Diff line number Diff line Loading @@ -1071,6 +1071,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } void reportAppUsage(String packageName, int userId) { // This app just transitioned into interactive use or near equivalent, so we should // take a look at its job state for feedback purposes. } /** * Initializes the system service. * <p> Loading Loading @@ -2150,6 +2155,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } @Override public void reportAppUsage(String packageName, int userId) { JobSchedulerService.this.reportAppUsage(packageName, userId); } @Override public JobStorePersistStats getPersistStats() { synchronized (mLock) { Loading Loading @@ -2212,6 +2222,40 @@ public final class JobSchedulerService extends com.android.server.SystemService } mInParole = isParoleOn; } @Override public void onUserInteractionStarted(String packageName, int userId) { final int uid = mLocalPM.getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); if (uid < 0) { // Quietly ignore; the case is already logged elsewhere return; } final long sinceLast = sElapsedRealtimeClock.millis() - mUsageStats.getTimeSinceLastJobRun(packageName, userId); final DeferredJobCounter counter = new DeferredJobCounter(); synchronized (mLock) { mJobs.forEachJobForSourceUid(uid, counter); } mUsageStats.reportAppJobState(packageName, userId, counter.numDeferred(), sinceLast); } } static class DeferredJobCounter implements JobStatusFunctor { private int mDeferred = 0; public int numDeferred() { return mDeferred; } @Override public void process(JobStatus job) { if (job.getWhenStandbyDeferred() > 0) { mDeferred++; } } } public static int standbyBucketToBucketIndex(int bucket) { Loading
services/core/java/com/android/server/job/JobServiceContext.java +7 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.app.job.IJobService; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobWorkItem; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; /** Loading Loading @@ -238,6 +240,11 @@ public final class JobServiceContext implements ServiceConnection { } } UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class); usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(), mExecutionStartTimeElapsed); // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. job.clearPersistedUtcTimes(); Loading
services/usage/java/com/android/server/usage/AppIdleHistory.java +29 −12 Original line number Diff line number Diff line Loading @@ -36,6 +36,8 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerInternal; import libcore.io.IoUtils; Loading Loading @@ -202,27 +204,23 @@ public class AppIdleHistory { * 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 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 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); public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int newBucket, long elapsedRealtime, long timeout) { if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); } if (appUsageHistory.currentBucket > bucket) { appUsageHistory.currentBucket = bucket; if (appUsageHistory.currentBucket > newBucket) { appUsageHistory.currentBucket = newBucket; if (DEBUG) { Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory .currentBucket Loading @@ -235,7 +233,26 @@ public class AppIdleHistory { } appUsageHistory.bucketingReason = REASON_USAGE; return appUsageHistory.currentBucket; return appUsageHistory; } /** * 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 newBucket 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 AppUsageHistory reportUsage(String packageName, int userId, int newBucket, long nowElapsed, long timeout) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true); return reportUsage(history, packageName, newBucket, nowElapsed, timeout); } private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) { Loading