Loading core/java/android/app/job/JobInfo.java +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ import android.os.PersistableBundle; * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed * using the {@link JobInfo.Builder}. * You must specify at least one sort of constraint on the JobInfo object that you are creating. * The goal here is to provide the scheduler with high-level semantics about the work you want to * accomplish. Doing otherwise with throw an exception in your app. */ public class JobInfo implements Parcelable { public interface NetworkType { Loading Loading @@ -434,7 +437,7 @@ public class JobInfo implements Parcelable { * @return The job object to hand to the JobScheduler. This object is immutable. */ public JobInfo build() { // Allow tasks with no constraints. What am I, a database? // Allow jobs with no constraints - What am I, a database? if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging && !mRequiresDeviceIdle && mNetworkCapabilities == NetworkType.NONE) { throw new IllegalArgumentException("You're trying to build a job with no " + Loading services/core/java/com/android/server/job/JobSchedulerService.java +31 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ public class JobSchedulerService extends com.android.server.SystemService */ final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>(); final ArrayList<Integer> mStartedUsers = new ArrayList(); final JobHandler mHandler; final JobSchedulerStub mJobSchedulerStub; Loading Loading @@ -151,6 +153,18 @@ public class JobSchedulerService extends com.android.server.SystemService } }; @Override public void onStartUser(int userHandle) { mStartedUsers.add(userHandle); // Let's kick any outstanding jobs for this user. mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } @Override public void onStopUser(int userHandle) { mStartedUsers.remove(Integer.valueOf(userHandle)); } /** * Entry point from client to schedule the provided job. * This cancels the job if it's already been scheduled, and replaces it with the one provided. Loading Loading @@ -610,9 +624,20 @@ public class JobSchedulerService extends com.android.server.SystemService * - It's ready. * - It's not pending. * - It's not already running on a JSC. * - The user that requested the job is running. */ private boolean isReadyToBeExecutedLocked(JobStatus job) { return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job); final boolean jobReady = job.isReady(); final boolean jobPending = mPendingJobs.contains(job); final boolean jobActive = isCurrentlyActiveLocked(job); final boolean userRunning = mStartedUsers.contains(job.getUserId()); if (DEBUG) { Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() + " ready=" + jobReady + " pending=" + jobPending + " active=" + jobActive + " userRunning=" + userRunning); } return userRunning && jobReady && !jobPending && !jobActive; } /** Loading Loading @@ -795,6 +820,11 @@ public class JobSchedulerService extends com.android.server.SystemService void dumpInternal(PrintWriter pw) { synchronized (mJobs) { pw.print("Started users: "); for (int i=0; i<mStartedUsers.size(); i++) { pw.print("u" + mStartedUsers.get(i) + " "); } pw.println(); pw.println("Registered jobs:"); if (mJobs.size() > 0) { ArraySet<JobStatus> jobs = mJobs.getJobs(); Loading services/core/java/com/android/server/job/controllers/JobStatus.java +30 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.os.PersistableBundle; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -41,6 +42,7 @@ public class JobStatus { public static final long NO_EARLIEST_RUNTIME = 0L; final JobInfo job; /** Uid of the package requesting this job. */ final int uId; final String name; final String tag; Loading Loading @@ -214,12 +216,39 @@ public class JobStatus { return String.valueOf(hashCode()).substring(0, 3) + ".." + ":[" + job.getService() + ",jId=" + job.getId() + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")" + ",u" + getUserId() + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME) + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")" + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging() + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures + ",P=" + job.isPersisted() + (isReady() ? "(READY)" : "") + "]"; } private String formatRunTime(long runtime, long defaultValue) { if (runtime == defaultValue) { return "none"; } else { long elapsedNow = SystemClock.elapsedRealtime(); long nextRuntime = runtime - elapsedNow; if (nextRuntime > 0) { return DateUtils.formatElapsedTime(nextRuntime / 1000); } else { return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000); } } } /** * Convenience function to identify a job uniquely without pulling all the data that * {@link #toString()} returns. */ public String toShortString() { return job.getService().flattenToShortString() + " jId=" + job.getId() + ", u" + getUserId(); } // Dumpsys infrastructure public void dump(PrintWriter pw, String prefix) { pw.println(this.toString()); Loading tests/JobSchedulerTestApp/res/layout/activity_main.xml +14 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,20 @@ android:id="@+id/checkbox_idle" android:text="@string/idle_mode_text"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/persisted_caption" android:layout_marginRight="15dp"/> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/checkbox_persisted" android:text="@string/persisted_mode_text"/> </LinearLayout> </LinearLayout> <Button Loading tests/JobSchedulerTestApp/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ limitations under the License. <string name="charging_caption">Charging:</string> <string name="charging_text">Requires device plugged in.</string> <string name="idle_caption">Idle:</string> <string name="persisted_caption">Persisted:</string> <string name="constraints">Constraints</string> <string name="connectivity">Connectivity:</string> <string name="any">Any</string> Loading @@ -34,4 +35,5 @@ limitations under the License. <string name="timing">Timing:</string> <string name="delay">Delay:</string> <string name="deadline">Deadline:</string> <string name="persisted_mode_text">Persisted:</string> </resources> Loading
core/java/android/app/job/JobInfo.java +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ import android.os.PersistableBundle; * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed * using the {@link JobInfo.Builder}. * You must specify at least one sort of constraint on the JobInfo object that you are creating. * The goal here is to provide the scheduler with high-level semantics about the work you want to * accomplish. Doing otherwise with throw an exception in your app. */ public class JobInfo implements Parcelable { public interface NetworkType { Loading Loading @@ -434,7 +437,7 @@ public class JobInfo implements Parcelable { * @return The job object to hand to the JobScheduler. This object is immutable. */ public JobInfo build() { // Allow tasks with no constraints. What am I, a database? // Allow jobs with no constraints - What am I, a database? if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging && !mRequiresDeviceIdle && mNetworkCapabilities == NetworkType.NONE) { throw new IllegalArgumentException("You're trying to build a job with no " + Loading
services/core/java/com/android/server/job/JobSchedulerService.java +31 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ public class JobSchedulerService extends com.android.server.SystemService */ final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>(); final ArrayList<Integer> mStartedUsers = new ArrayList(); final JobHandler mHandler; final JobSchedulerStub mJobSchedulerStub; Loading Loading @@ -151,6 +153,18 @@ public class JobSchedulerService extends com.android.server.SystemService } }; @Override public void onStartUser(int userHandle) { mStartedUsers.add(userHandle); // Let's kick any outstanding jobs for this user. mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } @Override public void onStopUser(int userHandle) { mStartedUsers.remove(Integer.valueOf(userHandle)); } /** * Entry point from client to schedule the provided job. * This cancels the job if it's already been scheduled, and replaces it with the one provided. Loading Loading @@ -610,9 +624,20 @@ public class JobSchedulerService extends com.android.server.SystemService * - It's ready. * - It's not pending. * - It's not already running on a JSC. * - The user that requested the job is running. */ private boolean isReadyToBeExecutedLocked(JobStatus job) { return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job); final boolean jobReady = job.isReady(); final boolean jobPending = mPendingJobs.contains(job); final boolean jobActive = isCurrentlyActiveLocked(job); final boolean userRunning = mStartedUsers.contains(job.getUserId()); if (DEBUG) { Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString() + " ready=" + jobReady + " pending=" + jobPending + " active=" + jobActive + " userRunning=" + userRunning); } return userRunning && jobReady && !jobPending && !jobActive; } /** Loading Loading @@ -795,6 +820,11 @@ public class JobSchedulerService extends com.android.server.SystemService void dumpInternal(PrintWriter pw) { synchronized (mJobs) { pw.print("Started users: "); for (int i=0; i<mStartedUsers.size(); i++) { pw.print("u" + mStartedUsers.get(i) + " "); } pw.println(); pw.println("Registered jobs:"); if (mJobs.size() > 0) { ArraySet<JobStatus> jobs = mJobs.getJobs(); Loading
services/core/java/com/android/server/job/controllers/JobStatus.java +30 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.os.PersistableBundle; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -41,6 +42,7 @@ public class JobStatus { public static final long NO_EARLIEST_RUNTIME = 0L; final JobInfo job; /** Uid of the package requesting this job. */ final int uId; final String name; final String tag; Loading Loading @@ -214,12 +216,39 @@ public class JobStatus { return String.valueOf(hashCode()).substring(0, 3) + ".." + ":[" + job.getService() + ",jId=" + job.getId() + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")" + ",u" + getUserId() + ",R=(" + formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME) + "," + formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME) + ")" + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging() + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures + ",P=" + job.isPersisted() + (isReady() ? "(READY)" : "") + "]"; } private String formatRunTime(long runtime, long defaultValue) { if (runtime == defaultValue) { return "none"; } else { long elapsedNow = SystemClock.elapsedRealtime(); long nextRuntime = runtime - elapsedNow; if (nextRuntime > 0) { return DateUtils.formatElapsedTime(nextRuntime / 1000); } else { return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000); } } } /** * Convenience function to identify a job uniquely without pulling all the data that * {@link #toString()} returns. */ public String toShortString() { return job.getService().flattenToShortString() + " jId=" + job.getId() + ", u" + getUserId(); } // Dumpsys infrastructure public void dump(PrintWriter pw, String prefix) { pw.println(this.toString()); Loading
tests/JobSchedulerTestApp/res/layout/activity_main.xml +14 −0 Original line number Diff line number Diff line Loading @@ -141,6 +141,20 @@ android:id="@+id/checkbox_idle" android:text="@string/idle_mode_text"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/persisted_caption" android:layout_marginRight="15dp"/> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/checkbox_persisted" android:text="@string/persisted_mode_text"/> </LinearLayout> </LinearLayout> <Button Loading
tests/JobSchedulerTestApp/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ limitations under the License. <string name="charging_caption">Charging:</string> <string name="charging_text">Requires device plugged in.</string> <string name="idle_caption">Idle:</string> <string name="persisted_caption">Persisted:</string> <string name="constraints">Constraints</string> <string name="connectivity">Connectivity:</string> <string name="any">Any</string> Loading @@ -34,4 +35,5 @@ limitations under the License. <string name="timing">Timing:</string> <string name="delay">Delay:</string> <string name="deadline">Deadline:</string> <string name="persisted_mode_text">Persisted:</string> </resources>