Home | History | Annotate | Download | only in job
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.app.job;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.app.job.IJobCallback;
     22 import android.content.ClipData;
     23 import android.net.Network;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.IBinder;
     27 import android.os.Parcel;
     28 import android.os.Parcelable;
     29 import android.os.PersistableBundle;
     30 import android.os.RemoteException;
     31 
     32 /**
     33  * Contains the parameters used to configure/identify your job. You do not create this object
     34  * yourself, instead it is handed in to your application by the System.
     35  */
     36 public class JobParameters implements Parcelable {
     37 
     38     /** @hide */
     39     public static final int REASON_CANCELED = JobProtoEnums.STOP_REASON_CANCELLED; // 0.
     40     /** @hide */
     41     public static final int REASON_CONSTRAINTS_NOT_SATISFIED =
     42             JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; //1.
     43     /** @hide */
     44     public static final int REASON_PREEMPT = JobProtoEnums.STOP_REASON_PREEMPT; // 2.
     45     /** @hide */
     46     public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
     47     /** @hide */
     48     public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
     49 
     50     /** @hide */
     51     public static String getReasonName(int reason) {
     52         switch (reason) {
     53             case REASON_CANCELED: return "canceled";
     54             case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
     55             case REASON_PREEMPT: return "preempt";
     56             case REASON_TIMEOUT: return "timeout";
     57             case REASON_DEVICE_IDLE: return "device_idle";
     58             default: return "unknown:" + reason;
     59         }
     60     }
     61 
     62     private final int jobId;
     63     private final PersistableBundle extras;
     64     private final Bundle transientExtras;
     65     private final ClipData clipData;
     66     private final int clipGrantFlags;
     67     private final IBinder callback;
     68     private final boolean overrideDeadlineExpired;
     69     private final Uri[] mTriggeredContentUris;
     70     private final String[] mTriggeredContentAuthorities;
     71     private final Network network;
     72 
     73     private int stopReason; // Default value of stopReason is REASON_CANCELED
     74     private String debugStopReason; // Human readable stop reason for debugging.
     75 
     76     /** @hide */
     77     public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
     78             Bundle transientExtras, ClipData clipData, int clipGrantFlags,
     79             boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
     80             String[] triggeredContentAuthorities, Network network) {
     81         this.jobId = jobId;
     82         this.extras = extras;
     83         this.transientExtras = transientExtras;
     84         this.clipData = clipData;
     85         this.clipGrantFlags = clipGrantFlags;
     86         this.callback = callback;
     87         this.overrideDeadlineExpired = overrideDeadlineExpired;
     88         this.mTriggeredContentUris = triggeredContentUris;
     89         this.mTriggeredContentAuthorities = triggeredContentAuthorities;
     90         this.network = network;
     91     }
     92 
     93     /**
     94      * @return The unique id of this job, specified at creation time.
     95      */
     96     public int getJobId() {
     97         return jobId;
     98     }
     99 
    100     /**
    101      * Reason onStopJob() was called on this job.
    102      * @hide
    103      */
    104     public int getStopReason() {
    105         return stopReason;
    106     }
    107 
    108     /**
    109      * Reason onStopJob() was called on this job.
    110      * @hide
    111      */
    112     public String getDebugStopReason() {
    113         return debugStopReason;
    114     }
    115 
    116     /**
    117      * @return The extras you passed in when constructing this job with
    118      * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
    119      * never be null. If you did not set any extras this will be an empty bundle.
    120      */
    121     public @NonNull PersistableBundle getExtras() {
    122         return extras;
    123     }
    124 
    125     /**
    126      * @return The transient extras you passed in when constructing this job with
    127      * {@link android.app.job.JobInfo.Builder#setTransientExtras(android.os.Bundle)}. This will
    128      * never be null. If you did not set any extras this will be an empty bundle.
    129      */
    130     public @NonNull Bundle getTransientExtras() {
    131         return transientExtras;
    132     }
    133 
    134     /**
    135      * @return The clip you passed in when constructing this job with
    136      * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null
    137      * if it was not set.
    138      */
    139     public @Nullable ClipData getClipData() {
    140         return clipData;
    141     }
    142 
    143     /**
    144      * @return The clip grant flags you passed in when constructing this job with
    145      * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be 0
    146      * if it was not set.
    147      */
    148     public int getClipGrantFlags() {
    149         return clipGrantFlags;
    150     }
    151 
    152     /**
    153      * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
    154      * provides an easy way to tell whether the job is being executed due to the deadline
    155      * expiring. Note: If the job is running because its deadline expired, it implies that its
    156      * constraints will not be met.
    157      */
    158     public boolean isOverrideDeadlineExpired() {
    159         return overrideDeadlineExpired;
    160     }
    161 
    162     /**
    163      * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
    164      * reports which URIs have triggered the job.  This will be null if either no URIs have
    165      * triggered it (it went off due to a deadline or other reason), or the number of changed
    166      * URIs is too large to report.  Whether or not the number of URIs is too large, you can
    167      * always use {@link #getTriggeredContentAuthorities()} to determine whether the job was
    168      * triggered due to any content changes and the authorities they are associated with.
    169      */
    170     public @Nullable Uri[] getTriggeredContentUris() {
    171         return mTriggeredContentUris;
    172     }
    173 
    174     /**
    175      * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
    176      * reports which content authorities have triggered the job.  It will only be null if no
    177      * authorities have triggered it -- that is, the job executed for some other reason, such
    178      * as a deadline expiring.  If this is non-null, you can use {@link #getTriggeredContentUris()}
    179      * to retrieve the details of which URIs changed (as long as that has not exceeded the maximum
    180      * number it can reported).
    181      */
    182     public @Nullable String[] getTriggeredContentAuthorities() {
    183         return mTriggeredContentAuthorities;
    184     }
    185 
    186     /**
    187      * Return the network that should be used to perform any network requests
    188      * for this job.
    189      * <p>
    190      * Devices may have multiple active network connections simultaneously, or
    191      * they may not have a default network route at all. To correctly handle all
    192      * situations like this, your job should always use the network returned by
    193      * this method instead of implicitly using the default network route.
    194      * <p>
    195      * Note that the system may relax the constraints you originally requested,
    196      * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
    197      * a metered network when there is a surplus of metered data available.
    198      *
    199      * @return the network that should be used to perform any network requests
    200      *         for this job, or {@code null} if this job didn't set any required
    201      *         network type.
    202      * @see JobInfo.Builder#setRequiredNetworkType(int)
    203      */
    204     public @Nullable Network getNetwork() {
    205         return network;
    206     }
    207 
    208     /**
    209      * Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their
    210      * currently running job.  Calling this method when there is no more work available and all
    211      * previously dequeued work has been completed will result in the system taking care of
    212      * stopping the job for you --
    213      * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
    214      * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
    215      *
    216      * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
    217      * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
    218      * executing the work.  The job will not be finished until all dequeued work has been
    219      * completed.  You do not, however, have to complete each returned work item before deqeueing
    220      * the next one -- you can use {@link #dequeueWork()} multiple times before completing
    221      * previous work if you want to process work in parallel, and you can complete the work
    222      * in whatever order you want.</p>
    223      *
    224      * <p>If the job runs to the end of its available time period before all work has been
    225      * completed, it will stop as normal.  You should return true from
    226      * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
    227      * doing so any pending as well as remaining uncompleted work will be re-queued
    228      * for the next time the job runs.</p>
    229      *
    230      * <p>This example shows how to construct a JobService that will serially dequeue and
    231      * process work that is available for it:</p>
    232      *
    233      * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/JobWorkService.java
    234      *      service}
    235      *
    236      * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
    237      * If null is returned, the system will also stop the job if all work has also been completed.
    238      * (This means that for correct operation, you must always call dequeueWork() after you have
    239      * completed other work, to check either for more work or allow the system to stop the job.)
    240      */
    241     public @Nullable JobWorkItem dequeueWork() {
    242         try {
    243             return getCallback().dequeueWork(getJobId());
    244         } catch (RemoteException e) {
    245             throw e.rethrowFromSystemServer();
    246         }
    247     }
    248 
    249     /**
    250      * Report the completion of executing a {@link JobWorkItem} previously returned by
    251      * {@link #dequeueWork()}.  This tells the system you are done with the
    252      * work associated with that item, so it will not be returned again.  Note that if this
    253      * is the last work in the queue, completing it here will <em>not</em> finish the overall
    254      * job -- for that to happen, you still need to call {@link #dequeueWork()}
    255      * again.
    256      *
    257      * <p>If you are enqueueing work into a job, you must call this method for each piece
    258      * of work you process.  Do <em>not</em> call
    259      * {@link JobService#jobFinished(JobParameters, boolean)}
    260      * or else you can lose work in your queue.</p>
    261      *
    262      * @param work The work you have completed processing, as previously returned by
    263      * {@link #dequeueWork()}
    264      */
    265     public void completeWork(@NonNull JobWorkItem work) {
    266         try {
    267             if (!getCallback().completeWork(getJobId(), work.getWorkId())) {
    268                 throw new IllegalArgumentException("Given work is not active: " + work);
    269             }
    270         } catch (RemoteException e) {
    271             throw e.rethrowFromSystemServer();
    272         }
    273     }
    274 
    275     /** @hide */
    276     public IJobCallback getCallback() {
    277         return IJobCallback.Stub.asInterface(callback);
    278     }
    279 
    280     private JobParameters(Parcel in) {
    281         jobId = in.readInt();
    282         extras = in.readPersistableBundle();
    283         transientExtras = in.readBundle();
    284         if (in.readInt() != 0) {
    285             clipData = ClipData.CREATOR.createFromParcel(in);
    286             clipGrantFlags = in.readInt();
    287         } else {
    288             clipData = null;
    289             clipGrantFlags = 0;
    290         }
    291         callback = in.readStrongBinder();
    292         overrideDeadlineExpired = in.readInt() == 1;
    293         mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
    294         mTriggeredContentAuthorities = in.createStringArray();
    295         if (in.readInt() != 0) {
    296             network = Network.CREATOR.createFromParcel(in);
    297         } else {
    298             network = null;
    299         }
    300         stopReason = in.readInt();
    301         debugStopReason = in.readString();
    302     }
    303 
    304     /** @hide */
    305     public void setStopReason(int reason, String debugStopReason) {
    306         stopReason = reason;
    307         this.debugStopReason = debugStopReason;
    308     }
    309 
    310     @Override
    311     public int describeContents() {
    312         return 0;
    313     }
    314 
    315     @Override
    316     public void writeToParcel(Parcel dest, int flags) {
    317         dest.writeInt(jobId);
    318         dest.writePersistableBundle(extras);
    319         dest.writeBundle(transientExtras);
    320         if (clipData != null) {
    321             dest.writeInt(1);
    322             clipData.writeToParcel(dest, flags);
    323             dest.writeInt(clipGrantFlags);
    324         } else {
    325             dest.writeInt(0);
    326         }
    327         dest.writeStrongBinder(callback);
    328         dest.writeInt(overrideDeadlineExpired ? 1 : 0);
    329         dest.writeTypedArray(mTriggeredContentUris, flags);
    330         dest.writeStringArray(mTriggeredContentAuthorities);
    331         if (network != null) {
    332             dest.writeInt(1);
    333             network.writeToParcel(dest, flags);
    334         } else {
    335             dest.writeInt(0);
    336         }
    337         dest.writeInt(stopReason);
    338         dest.writeString(debugStopReason);
    339     }
    340 
    341     public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
    342         @Override
    343         public JobParameters createFromParcel(Parcel in) {
    344             return new JobParameters(in);
    345         }
    346 
    347         @Override
    348         public JobParameters[] newArray(int size) {
    349             return new JobParameters[size];
    350         }
    351     };
    352 }
    353