Merge "Address various JobScheduler API feedback." into oc-dev

am: e3cc9287ac

Change-Id: I8801bb55abacae1aa2f1cf4d109087dc0209e4cf
This commit is contained in:
Dianne Hackborn
2017-04-22 00:39:09 +00:00
committed by android-build-merger
10 changed files with 109 additions and 34 deletions

View File

@@ -6893,15 +6893,15 @@ package android.app.job {
public abstract class JobServiceEngine {
ctor public JobServiceEngine(android.app.Service);
method public final android.os.IBinder getBinder();
method public final void jobFinished(android.app.job.JobParameters, boolean);
method public void jobFinished(android.app.job.JobParameters, boolean);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
}
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
method public int describeContents();
method public int getDeliveryCount();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;

View File

@@ -7329,15 +7329,15 @@ package android.app.job {
public abstract class JobServiceEngine {
ctor public JobServiceEngine(android.app.Service);
method public final android.os.IBinder getBinder();
method public final void jobFinished(android.app.job.JobParameters, boolean);
method public void jobFinished(android.app.job.JobParameters, boolean);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
}
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
method public int describeContents();
method public int getDeliveryCount();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;

View File

@@ -6923,15 +6923,15 @@ package android.app.job {
public abstract class JobServiceEngine {
ctor public JobServiceEngine(android.app.Service);
method public final android.os.IBinder getBinder();
method public final void jobFinished(android.app.job.JobParameters, boolean);
method public void jobFinished(android.app.job.JobParameters, boolean);
method public abstract boolean onStartJob(android.app.job.JobParameters);
method public abstract boolean onStopJob(android.app.job.JobParameters);
}
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
method public int describeContents();
method public int getDeliveryCount();
method public android.content.Intent getIntent();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;

View File

@@ -837,8 +837,12 @@ public class JobInfo implements Parcelable {
}
/**
* Set optional transient extras. This is incompatible with jobs that are also
* persisted with {@link #setPersisted(boolean)}; mixing the two is not allowed.
* Set optional transient extras.
*
* <p>Because setting this property is not compatible with persisted
* jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
* {@link android.app.job.JobInfo.Builder#build()} is called.</p>
*
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
*/
public Builder setTransientExtras(@NonNull Bundle extras) {

View File

@@ -180,6 +180,12 @@ public class JobParameters implements Parcelable {
* doing so any pending as well as remaining uncompleted work will be re-queued
* for the next time the job runs.</p>
*
* <p>This example shows how to construct a JobService that will serially dequeue and
* process work that is available for it:</p>
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/JobWorkService.java
* service}
*
* @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
* If null is returned, the system will also stop the job if all work has also been completed.
* (This means that for correct operation, you must always call dequeueWork() after you have

View File

@@ -75,21 +75,22 @@ public abstract class JobScheduler {
public abstract int schedule(@NonNull JobInfo job);
/**
* Similar to {@link #schedule}, but allows you to enqueue work for an existing job. If a job
* with the same ID is already scheduled, it will be replaced with the new {@link JobInfo}, but
* any previously enqueued work will remain and be dispatched the next time it runs. If a job
* with the same ID is already running, the new work will be enqueued for it.
* Similar to {@link #schedule}, but allows you to enqueue work for a new <em>or existing</em>
* job. If a job with the same ID is already scheduled, it will be replaced with the
* new {@link JobInfo}, but any previously enqueued work will remain and be dispatched the
* next time it runs. If a job with the same ID is already running, the new work will be
* enqueued for it.
*
* <p>The work you enqueue is later retrieved through
* {@link JobParameters#dequeueWork() JobParameters.dequeueWork()}. Be sure to see there
* {@link JobParameters#dequeueWork() JobParameters.dequeueWork}. Be sure to see there
* about how to process work; the act of enqueueing work changes how you should handle the
* overall lifecycle of an executing job.</p>
*
* <p>It is strongly encouraged that you use the same {@link JobInfo} for all work you
* enqueue. This will allow the system to optimal schedule work along with any pending
* enqueue. This will allow the system to optimally schedule work along with any pending
* and/or currently running work. If the JobInfo changes from the last time the job was
* enqueued, the system will need to update the associated JobInfo, which can cause a disruption
* in exection. In particular, this can result in any currently running job that is processing
* in execution. In particular, this can result in any currently running job that is processing
* previous work to be stopped and restarted with the new JobInfo.</p>
*
* <p>It is recommended that you avoid using
@@ -100,7 +101,7 @@ public abstract class JobScheduler {
* (That said, you should be relatively safe with a simple set of consistent data in these
* fields.) You should never use {@link JobInfo.Builder#setClipData(ClipData, int)} with
* work you are enqueue, since currently this will always be treated as a different JobInfo,
* even if the ClipData contents is exactly the same.</p>
* even if the ClipData contents are exactly the same.</p>
*
* @param job The job you wish to enqueue work for. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs

View File

@@ -32,7 +32,11 @@ import java.lang.ref.WeakReference;
/**
* Helper for implementing a {@link android.app.Service} that interacts with
* {@link JobScheduler}.
* {@link JobScheduler}. This is not intended for use by regular applications, but
* allows frameworks built on top of the platform to create their own
* {@link android.app.Service} that interact with {@link JobScheduler} as well as
* add in additional functionality. If you just want to execute jobs normally, you
* should instead be looking at {@link JobService}.
*/
public abstract class JobServiceEngine {
private static final String TAG = "JobServiceEngine";
@@ -215,7 +219,7 @@ public abstract class JobServiceEngine {
* {@link JobService#jobFinished(JobParameters, boolean)} JobService.jobFinished} for more
* information.
*/
public final void jobFinished(JobParameters params, boolean needsReschedule) {
public void jobFinished(JobParameters params, boolean needsReschedule) {
Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
m.arg2 = needsReschedule ? 1 : 0;
m.sendToTarget();

View File

@@ -22,15 +22,19 @@ import android.os.Parcelable;
/**
* A unit of work that can be enqueued for a job using
* {@link JobScheduler#enqueue JobScheduler.enqueue}.
* {@link JobScheduler#enqueue JobScheduler.enqueue}. See
* {@link JobParameters#dequeueWork() JobParameters.dequeueWork} for more details.
*/
final public class JobWorkItem implements Parcelable {
final Intent mIntent;
int mDeliveryCount;
int mWorkId;
Object mGrants;
/**
* Create a new piece of work.
* Create a new piece of work, which can be submitted to
* {@link JobScheduler#enqueue JobScheduler.enqueue}.
*
* @param intent The general Intent describing this work.
*/
public JobWorkItem(Intent intent) {
@@ -44,6 +48,23 @@ final public class JobWorkItem implements Parcelable {
return mIntent;
}
/**
* Return the count of the number of times this work item has been delivered
* to the job. The value will be > 1 if it has been redelivered because the job
* was stopped or crashed while it had previously been delivered but before the
* job had called {@link JobParameters#completeWork JobParameters.completeWork} for it.
*/
public int getDeliveryCount() {
return mDeliveryCount;
}
/**
* @hide
*/
public void bumpDeliveryCount() {
mDeliveryCount++;
}
/**
* @hide
*/
@@ -73,7 +94,17 @@ final public class JobWorkItem implements Parcelable {
}
public String toString() {
return "JobWorkItem{id=" + mWorkId + " intent=" + mIntent + "}";
StringBuilder sb = new StringBuilder(64);
sb.append("JobWorkItem{id=");
sb.append(mWorkId);
sb.append(" intent=");
sb.append(mIntent);
if (mDeliveryCount != 0) {
sb.append(" dcount=");
sb.append(mDeliveryCount);
}
sb.append("}");
return sb.toString();
}
public int describeContents() {
@@ -87,6 +118,7 @@ final public class JobWorkItem implements Parcelable {
} else {
out.writeInt(0);
}
out.writeInt(mDeliveryCount);
out.writeInt(mWorkId);
}
@@ -101,12 +133,13 @@ final public class JobWorkItem implements Parcelable {
}
};
public JobWorkItem(Parcel in) {
JobWorkItem(Parcel in) {
if (in.readInt() != 0) {
mIntent = Intent.CREATOR.createFromParcel(in);
} else {
mIntent = null;
}
mDeliveryCount = in.readInt();
mWorkId = in.readInt();
}
}

View File

@@ -1367,26 +1367,48 @@ public final class JobSchedulerService extends com.android.server.SystemService
* - The component is enabled and runnable.
*/
private boolean isReadyToBeExecutedLocked(JobStatus job) {
final boolean jobExists = mJobs.containsJob(job);
final boolean jobReady = job.isReady();
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
final boolean jobBackingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " ready=" + jobReady);
}
// This is a condition that is very likely to be false (most jobs that are
// scheduled are sitting there, not ready yet) and very cheap to check (just
// a few conditions on data in JobStatus).
if (!jobReady) {
return false;
}
final boolean jobExists = mJobs.containsJob(job);
final int userId = job.getUserId();
final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " exists=" + jobExists
+ " ready=" + jobReady + " pending=" + jobPending
+ " active=" + jobActive + " backingup=" + jobBackingUp
+ " userStarted=" + userStarted);
+ " exists=" + jobExists + " userStarted=" + userStarted);
}
// Short circuit: don't do the expensive PM check unless we really think
// we might need to run this job now.
if (!jobExists || !userStarted || !jobReady || jobPending || jobActive || jobBackingUp) {
// These are also fairly cheap to check, though they typically will not
// be conditions we fail.
if (!jobExists || !userStarted) {
return false;
}
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " pending=" + jobPending + " active=" + jobActive);
}
// These can be a little more expensive (especially jobActive, since we need to
// go through the array of all potentially active jobs), so we are doing them
// later... but still before checking with the package manager!
if (jobPending || jobActive) {
return false;
}

View File

@@ -339,6 +339,7 @@ public final class JobStatus {
executingWork = new ArrayList<>();
}
executingWork.add(work);
work.bumpDeliveryCount();
}
return work;
}
@@ -661,6 +662,9 @@ public final class JobStatus {
/**
* @return Whether or not this job is ready to run, based on its requirements. This is true if
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
* TODO: This function is called a *lot*. We should probably just have it check an
* already-computed boolean, which we updated whenever we see one of the states it depends
* on here change.
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
@@ -856,7 +860,8 @@ public final class JobStatus {
private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #");
pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
pw.print("x "); pw.println(work.getIntent());
if (work.getGrants() != null) {
pw.print(prefix); pw.println(" URI grants:");
((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " ");