diff options
3 files changed, 46 insertions, 3 deletions
diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java index 4e7490858fc8..569e9ec995d7 100644 --- a/services/core/java/com/android/server/job/JobSchedulerInternal.java +++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java @@ -16,6 +16,7 @@ package com.android.server.job; +import android.annotation.UserIdInt; import android.app.job.JobInfo; import java.util.List; @@ -39,6 +40,14 @@ public interface JobSchedulerInternal { long nextHeartbeatForBucket(int bucket); /** + * Heartbeat ordinal for the given app. This is typically the heartbeat at which + * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run + * jobs in a long time is immediately runnable even if the app is bucketed into + * an infrequent time allocation. + */ + public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, int appBucket); + + /** * Returns a list of pending jobs scheduled by the system service. */ List<JobInfo> getSystemScheduledPendingJobs(); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 5da470e68875..2066f2af46dd 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -19,6 +19,7 @@ package com.android.server.job; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; +import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -39,6 +40,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.Intent.UriFlags; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -2065,6 +2067,33 @@ public final class JobSchedulerService extends com.android.server.SystemService } /** + * Heartbeat ordinal for the given app. This is typically the heartbeat at which + * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run + * jobs in a long time is immediately runnable even if the app is bucketed into + * an infrequent time allocation. + */ + public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, + final int appStandbyBucket) { + if (appStandbyBucket == 0 || + appStandbyBucket >= mConstants.STANDBY_BEATS.length) { + // ACTIVE => everything can be run right away + // NEVER => we won't run them anyway, so let them go in the future + // as soon as the app enters normal use + return 0; + } + + final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun( + packageName, userId); + final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket]; + final long bucketsAgo = timeSinceLastJob / bucketLength; + + // If we haven't run any jobs for more than the app's current bucket period, just + // consider anything new to be immediately runnable. Otherwise, base it on the + // bucket at which we last ran jobs. + return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo); + } + + /** * Returns a list of all pending jobs. A running job is not considered pending. Periodic * jobs are always considered pending. */ @@ -2139,10 +2168,14 @@ public final class JobSchedulerService extends com.android.server.SystemService mUsageStats = usageStats; } + public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) { + return mUsageStats.getTimeSinceLastJobRun(packageName, userId); + } + // AppIdleStateChangeListener interface for live updates @Override - public void onAppIdleStateChanged(final String packageName, final int userId, + public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, boolean idle, int bucket) { final int uid = mLocalPM.getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index d9a5ff675475..08ff7bdb0eb8 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -414,8 +414,9 @@ public final class JobStatus { int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, sourceUserId, elapsedNow); JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); - long currentHeartbeat = js != null ? js.currentHeartbeat() : 0; - + long currentHeartbeat = js != null + ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket) + : 0; return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId, standbyBucket, currentHeartbeat, tag, 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, |