summaryrefslogtreecommitdiff
path: root/android/app/job/JobParameters.java
blob: c71bf2e65731fc1ea811f6caee6c17970561d799 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.app.job;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.IJobCallback;
import android.content.ClipData;
import android.net.Network;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;

/**
 * Contains the parameters used to configure/identify your job. You do not create this object
 * yourself, instead it is handed in to your application by the System.
 */
public class JobParameters implements Parcelable {

    /** @hide */
    public static final int REASON_CANCELED = 0;
    /** @hide */
    public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
    /** @hide */
    public static final int REASON_PREEMPT = 2;
    /** @hide */
    public static final int REASON_TIMEOUT = 3;
    /** @hide */
    public static final int REASON_DEVICE_IDLE = 4;

    /** @hide */
    public static String getReasonName(int reason) {
        switch (reason) {
            case REASON_CANCELED: return "canceled";
            case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
            case REASON_PREEMPT: return "preempt";
            case REASON_TIMEOUT: return "timeout";
            case REASON_DEVICE_IDLE: return "device_idle";
            default: return "unknown:" + reason;
        }
    }

    private final int jobId;
    private final PersistableBundle extras;
    private final Bundle transientExtras;
    private final ClipData clipData;
    private final int clipGrantFlags;
    private final IBinder callback;
    private final boolean overrideDeadlineExpired;
    private final Uri[] mTriggeredContentUris;
    private final String[] mTriggeredContentAuthorities;
    private final Network network;

    private int stopReason; // Default value of stopReason is REASON_CANCELED
    private String debugStopReason; // Human readable stop reason for debugging.

    /** @hide */
    public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
            Bundle transientExtras, ClipData clipData, int clipGrantFlags,
            boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
            String[] triggeredContentAuthorities, Network network) {
        this.jobId = jobId;
        this.extras = extras;
        this.transientExtras = transientExtras;
        this.clipData = clipData;
        this.clipGrantFlags = clipGrantFlags;
        this.callback = callback;
        this.overrideDeadlineExpired = overrideDeadlineExpired;
        this.mTriggeredContentUris = triggeredContentUris;
        this.mTriggeredContentAuthorities = triggeredContentAuthorities;
        this.network = network;
    }

    /**
     * @return The unique id of this job, specified at creation time.
     */
    public int getJobId() {
        return jobId;
    }

    /**
     * Reason onStopJob() was called on this job.
     * @hide
     */
    public int getStopReason() {
        return stopReason;
    }

    /**
     * Reason onStopJob() was called on this job.
     * @hide
     */
    public String getDebugStopReason() {
        return debugStopReason;
    }

    /**
     * @return The extras you passed in when constructing this job with
     * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
     * never be null. If you did not set any extras this will be an empty bundle.
     */
    public @NonNull PersistableBundle getExtras() {
        return extras;
    }

    /**
     * @return The transient extras you passed in when constructing this job with
     * {@link android.app.job.JobInfo.Builder#setTransientExtras(android.os.Bundle)}. This will
     * never be null. If you did not set any extras this will be an empty bundle.
     */
    public @NonNull Bundle getTransientExtras() {
        return transientExtras;
    }

    /**
     * @return The clip you passed in when constructing this job with
     * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null
     * if it was not set.
     */
    public @Nullable ClipData getClipData() {
        return clipData;
    }

    /**
     * @return The clip grant flags you passed in when constructing this job with
     * {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be 0
     * if it was not set.
     */
    public int getClipGrantFlags() {
        return clipGrantFlags;
    }

    /**
     * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
     * provides an easy way to tell whether the job is being executed due to the deadline
     * expiring. Note: If the job is running because its deadline expired, it implies that its
     * constraints will not be met.
     */
    public boolean isOverrideDeadlineExpired() {
        return overrideDeadlineExpired;
    }

    /**
     * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
     * reports which URIs have triggered the job.  This will be null if either no URIs have
     * triggered it (it went off due to a deadline or other reason), or the number of changed
     * URIs is too large to report.  Whether or not the number of URIs is too large, you can
     * always use {@link #getTriggeredContentAuthorities()} to determine whether the job was
     * triggered due to any content changes and the authorities they are associated with.
     */
    public @Nullable Uri[] getTriggeredContentUris() {
        return mTriggeredContentUris;
    }

    /**
     * For jobs with {@link android.app.job.JobInfo.Builder#addTriggerContentUri} set, this
     * reports which content authorities have triggered the job.  It will only be null if no
     * authorities have triggered it -- that is, the job executed for some other reason, such
     * as a deadline expiring.  If this is non-null, you can use {@link #getTriggeredContentUris()}
     * to retrieve the details of which URIs changed (as long as that has not exceeded the maximum
     * number it can reported).
     */
    public @Nullable String[] getTriggeredContentAuthorities() {
        return mTriggeredContentAuthorities;
    }

    /**
     * Return the network that should be used to perform any network requests
     * for this job.
     * <p>
     * Devices may have multiple active network connections simultaneously, or
     * they may not have a default network route at all. To correctly handle all
     * situations like this, your job should always use the network returned by
     * this method instead of implicitly using the default network route.
     * <p>
     * Note that the system may relax the constraints you originally requested,
     * such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
     * a metered network when there is a surplus of metered data available.
     *
     * @return the network that should be used to perform any network requests
     *         for this job, or {@code null} if this job didn't set any required
     *         network type.
     * @see JobInfo.Builder#setRequiredNetworkType(int)
     */
    public @Nullable Network getNetwork() {
        return network;
    }

    /**
     * Dequeue the next pending {@link JobWorkItem} from these JobParameters associated with their
     * currently running job.  Calling this method when there is no more work available and all
     * previously dequeued work has been completed will result in the system taking care of
     * stopping the job for you --
     * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
     * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
     *
     * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
     * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
     * executing the work.  The job will not be finished until all dequeued work has been
     * completed.  You do not, however, have to complete each returned work item before deqeueing
     * the next one -- you can use {@link #dequeueWork()} multiple times before completing
     * previous work if you want to process work in parallel, and you can complete the work
     * in whatever order you want.</p>
     *
     * <p>If the job runs to the end of its available time period before all work has been
     * completed, it will stop as normal.  You should return true from
     * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
     * 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
     * completed other work, to check either for more work or allow the system to stop the job.)
     */
    public @Nullable JobWorkItem dequeueWork() {
        try {
            return getCallback().dequeueWork(getJobId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Report the completion of executing a {@link JobWorkItem} previously returned by
     * {@link #dequeueWork()}.  This tells the system you are done with the
     * work associated with that item, so it will not be returned again.  Note that if this
     * is the last work in the queue, completing it here will <em>not</em> finish the overall
     * job -- for that to happen, you still need to call {@link #dequeueWork()}
     * again.
     *
     * <p>If you are enqueueing work into a job, you must call this method for each piece
     * of work you process.  Do <em>not</em> call
     * {@link JobService#jobFinished(JobParameters, boolean)}
     * or else you can lose work in your queue.</p>
     *
     * @param work The work you have completed processing, as previously returned by
     * {@link #dequeueWork()}
     */
    public void completeWork(@NonNull JobWorkItem work) {
        try {
            if (!getCallback().completeWork(getJobId(), work.getWorkId())) {
                throw new IllegalArgumentException("Given work is not active: " + work);
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** @hide */
    public IJobCallback getCallback() {
        return IJobCallback.Stub.asInterface(callback);
    }

    private JobParameters(Parcel in) {
        jobId = in.readInt();
        extras = in.readPersistableBundle();
        transientExtras = in.readBundle();
        if (in.readInt() != 0) {
            clipData = ClipData.CREATOR.createFromParcel(in);
            clipGrantFlags = in.readInt();
        } else {
            clipData = null;
            clipGrantFlags = 0;
        }
        callback = in.readStrongBinder();
        overrideDeadlineExpired = in.readInt() == 1;
        mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
        mTriggeredContentAuthorities = in.createStringArray();
        if (in.readInt() != 0) {
            network = Network.CREATOR.createFromParcel(in);
        } else {
            network = null;
        }
        stopReason = in.readInt();
        debugStopReason = in.readString();
    }

    /** @hide */
    public void setStopReason(int reason, String debugStopReason) {
        stopReason = reason;
        this.debugStopReason = debugStopReason;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(jobId);
        dest.writePersistableBundle(extras);
        dest.writeBundle(transientExtras);
        if (clipData != null) {
            dest.writeInt(1);
            clipData.writeToParcel(dest, flags);
            dest.writeInt(clipGrantFlags);
        } else {
            dest.writeInt(0);
        }
        dest.writeStrongBinder(callback);
        dest.writeInt(overrideDeadlineExpired ? 1 : 0);
        dest.writeTypedArray(mTriggeredContentUris, flags);
        dest.writeStringArray(mTriggeredContentAuthorities);
        if (network != null) {
            dest.writeInt(1);
            network.writeToParcel(dest, flags);
        } else {
            dest.writeInt(0);
        }
        dest.writeInt(stopReason);
        dest.writeString(debugStopReason);
    }

    public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
        @Override
        public JobParameters createFromParcel(Parcel in) {
            return new JobParameters(in);
        }

        @Override
        public JobParameters[] newArray(int size) {
            return new JobParameters[size];
        }
    };
}