blob: 2e95ad932da8dc7e598d21ecc9605ce16d197bf7 [file] [log] [blame]
Sumir Kataria904ba122017-09-25 13:05:49 -07001/*
2 * Copyright (C) 2017 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
Sumir Kataria6d55d8c2017-12-07 13:37:42 -080017package android.arch.background.workmanager.impl.model;
Sumir Kataria904ba122017-09-25 13:05:49 -070018
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080019import static android.arch.background.workmanager.Constants.MAX_BACKOFF_MILLIS;
20import static android.arch.background.workmanager.Constants.MIN_BACKOFF_MILLIS;
21import static android.arch.background.workmanager.Constants.MIN_PERIODIC_FLEX_MILLIS;
22import static android.arch.background.workmanager.Constants.MIN_PERIODIC_INTERVAL_MILLIS;
23
Sumir Kataria96d3c9d2017-12-07 10:03:51 -080024import android.arch.background.workmanager.Arguments;
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080025import android.arch.background.workmanager.Constants;
Sumir Kataria96d3c9d2017-12-07 10:03:51 -080026import android.arch.background.workmanager.Constraints;
Sumir Kataria904ba122017-09-25 13:05:49 -070027import android.arch.persistence.room.ColumnInfo;
28import android.arch.persistence.room.Embedded;
29import android.arch.persistence.room.Entity;
30import android.arch.persistence.room.PrimaryKey;
Sumir Kataria904ba122017-09-25 13:05:49 -070031import android.support.annotation.NonNull;
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080032import android.util.Log;
Sumir Kataria904ba122017-09-25 13:05:49 -070033
34/**
35 * Stores information about a logical unit of work.
36 */
37@Entity
Sumir Kataria904ba122017-09-25 13:05:49 -070038public class WorkSpec {
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070039 private static final String TAG = "WorkSpec";
Sumir Kataria904ba122017-09-25 13:05:49 -070040
41 @ColumnInfo(name = "id")
42 @PrimaryKey
43 @NonNull
44 String mId;
45
Sumir Kataria904ba122017-09-25 13:05:49 -070046 @ColumnInfo(name = "status")
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080047 @Constants.WorkStatus
48 int mStatus = Constants.STATUS_ENQUEUED;
Sumir Kataria904ba122017-09-25 13:05:49 -070049
Jan Clarinee302e12017-10-09 11:16:19 -070050 @ColumnInfo(name = "worker_class_name")
51 String mWorkerClassName;
52
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080053 @ColumnInfo(name = "input_merger_class_name")
Sumir Kataria18aa0f02017-11-29 14:17:36 -080054 String mInputMergerClassName;
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080055
Sumir Kataria9244d372017-11-30 13:39:07 -080056 @ColumnInfo(name = "arguments")
57 @NonNull
58 Arguments mArguments = Arguments.EMPTY;
59
60 @ColumnInfo(name = "output")
61 @NonNull
62 Arguments mOutput = Arguments.EMPTY;
63
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -070064 @ColumnInfo(name = "initial_delay")
65 long mInitialDelay;
66
Jan Clarinee302e12017-10-09 11:16:19 -070067 @ColumnInfo(name = "interval_duration")
68 long mIntervalDuration;
69
70 @ColumnInfo(name = "flex_duration")
71 long mFlexDuration;
Sumir Kataria904ba122017-09-25 13:05:49 -070072
73 @Embedded
Xyan Bhatnagard677da52017-12-11 15:26:21 -080074 Constraints mConstraints = Constraints.NONE;
Sumir Kataria904ba122017-09-25 13:05:49 -070075
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070076 @ColumnInfo(name = "run_attempt_count")
77 int mRunAttemptCount;
78
Sumir Kataria904ba122017-09-25 13:05:49 -070079 // TODO(sumir): Should Backoff be disabled by default?
80 @ColumnInfo(name = "backoff_policy")
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080081 @Constants.BackoffPolicy
82 int mBackoffPolicy = Constants.BACKOFF_POLICY_EXPONENTIAL;
Sumir Kataria904ba122017-09-25 13:05:49 -070083
84 @ColumnInfo(name = "backoff_delay_duration")
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080085 long mBackoffDelayDuration = Constants.DEFAULT_BACKOFF_DELAY_MILLIS;
Sumir Kataria904ba122017-09-25 13:05:49 -070086
Jan Clarin0deb5e22017-12-06 11:26:29 -080087 @ColumnInfo(name = "period_start_time")
88 long mPeriodStartTime;
89
Sumir Kataria904ba122017-09-25 13:05:49 -070090 public WorkSpec(@NonNull String id) {
91 mId = id;
92 }
93
94 @NonNull
95 public String getId() {
96 return mId;
97 }
98
99 public void setId(@NonNull String id) {
100 mId = id;
101 }
102
Sumir Kataria904ba122017-09-25 13:05:49 -0700103 public int getStatus() {
104 return mStatus;
105 }
106
107 public void setStatus(int status) {
108 mStatus = status;
109 }
110
111 public String getWorkerClassName() {
112 return mWorkerClassName;
113 }
114
115 public void setWorkerClassName(String workerClassName) {
116 mWorkerClassName = workerClassName;
117 }
118
Sumir Katariaa48d7ac2017-11-28 16:24:15 -0800119 public String getInputMergerClassName() {
120 return mInputMergerClassName;
121 }
122
123 public void setInputMergerClassName(String inputMergerClassName) {
124 mInputMergerClassName = inputMergerClassName;
125 }
126
Sumir Kataria9244d372017-11-30 13:39:07 -0800127 public @NonNull Arguments getArguments() {
128 return mArguments;
129 }
130
131 public void setArguments(@NonNull Arguments arguments) {
132 mArguments = arguments;
133 }
134
135 public @NonNull Arguments getOutput() {
136 return mOutput;
137 }
138
139 public void setOutput(@NonNull Arguments output) {
140 mOutput = output;
141 }
142
Sumir Kataria904ba122017-09-25 13:05:49 -0700143 public Constraints getConstraints() {
144 return mConstraints;
145 }
146
147 public void setConstraints(Constraints constraints) {
148 mConstraints = constraints;
149 }
150
Sumir Kataria904ba122017-09-25 13:05:49 -0700151 public int getBackoffPolicy() {
152 return mBackoffPolicy;
153 }
154
155 public void setBackoffPolicy(int backoffPolicy) {
156 mBackoffPolicy = backoffPolicy;
157 }
158
159 public long getBackoffDelayDuration() {
160 return mBackoffDelayDuration;
161 }
162
163 public void setBackoffDelayDuration(long backoffDelayDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800164 if (backoffDelayDuration > MAX_BACKOFF_MILLIS) {
165 Log.w(TAG, "Backoff delay duration exceeds maximum value");
166 backoffDelayDuration = MAX_BACKOFF_MILLIS;
167 }
168 if (backoffDelayDuration < MIN_BACKOFF_MILLIS) {
169 Log.w(TAG, "Backoff delay duration less than minimum value");
170 backoffDelayDuration = MIN_BACKOFF_MILLIS;
171 }
Sumir Kataria904ba122017-09-25 13:05:49 -0700172 mBackoffDelayDuration = backoffDelayDuration;
173 }
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -0700174
175 public long getInitialDelay() {
176 return mInitialDelay;
177 }
178
179 public void setInitialDelay(long initialDelay) {
180 mInitialDelay = initialDelay;
181 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700182
Jan Clarinee302e12017-10-09 11:16:19 -0700183 public boolean isPeriodic() {
184 return mIntervalDuration != 0L;
185 }
186
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800187 public boolean isBackedOff() {
188 return mRunAttemptCount > 0;
189 }
190
Sumir Kataria6d55d8c2017-12-07 13:37:42 -0800191 /**
192 * Sets the periodic interval for this unit of work.
193 *
194 * @param intervalDuration The interval in milliseconds
195 */
Jan Clarinee302e12017-10-09 11:16:19 -0700196 public void setPeriodic(long intervalDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800197 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
198 Log.w(TAG, "Interval duration lesser than minimum allowed value; "
199 + "Changed to " + MIN_PERIODIC_INTERVAL_MILLIS);
200 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
201 }
Jan Clarinee302e12017-10-09 11:16:19 -0700202 setPeriodic(intervalDuration, intervalDuration);
203 }
204
Sumir Kataria6d55d8c2017-12-07 13:37:42 -0800205 /**
206 * Sets the periodic interval for this unit of work.
207 *
208 * @param intervalDuration The interval in milliseconds
209 * @param flexDuration The flex duration in milliseconds
210 */
Jan Clarinee302e12017-10-09 11:16:19 -0700211 public void setPeriodic(long intervalDuration, long flexDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800212 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
213 Log.w(TAG, "Interval duration lesser than minimum allowed value; "
214 + "Changed to " + MIN_PERIODIC_INTERVAL_MILLIS);
215 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
216 }
217 if (flexDuration < MIN_PERIODIC_FLEX_MILLIS) {
218 Log.w(TAG, "Flex duration lesser than minimum allowed value; "
219 + "Changed to " + MIN_PERIODIC_FLEX_MILLIS);
220 flexDuration = MIN_PERIODIC_FLEX_MILLIS;
221 }
222 if (flexDuration > intervalDuration) {
223 Log.w(TAG, "Flex duration greater than interval duration; "
224 + "Changed to " + intervalDuration);
225 flexDuration = intervalDuration;
226 }
Jan Clarinee302e12017-10-09 11:16:19 -0700227 mIntervalDuration = intervalDuration;
228 mFlexDuration = flexDuration;
229 }
230
231 public long getIntervalDuration() {
232 return mIntervalDuration;
233 }
234
235 public void setIntervalDuration(long intervalDuration) {
236 mIntervalDuration = intervalDuration;
237 }
238
239 public long getFlexDuration() {
240 return mFlexDuration;
241 }
242
243 public void setFlexDuration(long flexDuration) {
244 mFlexDuration = flexDuration;
245 }
246
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700247 public void setRunAttemptCount(int runAttemptCount) {
248 this.mRunAttemptCount = runAttemptCount;
249 }
250
251 public int getRunAttemptCount() {
252 return mRunAttemptCount;
253 }
254
255 /**
Jan Clarin0deb5e22017-12-06 11:26:29 -0800256 * For one-off work, this is the time that the work was unblocked by prerequisites.
257 * For periodic work, this is the time that the period started.
258 */
259 public long getPeriodStartTime() {
260 return mPeriodStartTime;
261 }
262
263 public void setPeriodStartTime(long periodStartTime) {
264 mPeriodStartTime = periodStartTime;
265 }
266
267 /**
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800268 * Calculates the UTC time at which this {@link WorkSpec} should be allowed to run.
269 * This method accounts for work that is backed off or periodic.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700270 *
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800271 * If Backoff Policy is set to {@link Constants#BACKOFF_POLICY_EXPONENTIAL}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700272 * increases at an exponential rate with respect to the run attempt count and is capped at
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800273 * {@link Constants#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700274 *
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800275 * If Backoff Policy is set to {@link Constants#BACKOFF_POLICY_LINEAR}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700276 * increases at an linear rate with respect to the run attempt count and is capped at
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800277 * {@link Constants#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700278 *
279 * Based on {@see https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/job/JobSchedulerService.java#1125}
280 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800281 * Note that this runtime is for WorkManager internal use and may not match what the OS
282 * considers to be the next runtime.
Sumir Kataria58e4d6f2017-11-14 14:31:15 -0800283 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800284 * For jobs with constraints, this represents the earliest time at which constraints
285 * should be monitored for this work.
286 *
287 * For jobs without constraints, this represents the earliest time at which this work is
288 * allowed to run.
289 *
290 * @return UTC time at which this {@link WorkSpec} should be allowed to run.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700291 */
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800292 public long calculateNextRunTime() {
293 if (isBackedOff()) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800294 boolean isLinearBackoff = (mBackoffPolicy == Constants.BACKOFF_POLICY_LINEAR);
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800295 long delay = isLinearBackoff ? (mBackoffDelayDuration * mRunAttemptCount)
296 : (long) Math.scalb(mBackoffDelayDuration, mRunAttemptCount - 1);
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800297 return mPeriodStartTime + Math.min(Constants.MAX_BACKOFF_MILLIS, delay);
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800298 } else if (isPeriodic()) {
299 return mPeriodStartTime + mIntervalDuration - mFlexDuration;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700300 } else {
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800301 return mPeriodStartTime + mInitialDelay;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700302 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700303 }
Jan Clarin91bccff2017-10-20 17:51:10 -0700304
305 @Override
306 public boolean equals(Object o) {
307 if (this == o) {
308 return true;
309 }
310 if (o == null || getClass() != o.getClass()) {
311 return false;
312 }
313 WorkSpec other = (WorkSpec) o;
314 return mId.equals(other.mId)
315 && mStatus == other.mStatus
316 && mInitialDelay == other.mInitialDelay
317 && mIntervalDuration == other.mIntervalDuration
318 && mFlexDuration == other.mFlexDuration
319 && mRunAttemptCount == other.mRunAttemptCount
320 && mBackoffPolicy == other.mBackoffPolicy
321 && mBackoffDelayDuration == other.mBackoffDelayDuration
Sumir Kataria9244d372017-11-30 13:39:07 -0800322 && mArguments.equals(other.mArguments)
323 && mOutput.equals(other.mOutput)
Jan Clarin91bccff2017-10-20 17:51:10 -0700324 && (mWorkerClassName != null
325 ? mWorkerClassName.equals(other.mWorkerClassName)
326 : other.mWorkerClassName == null)
Sumir Katariaa48d7ac2017-11-28 16:24:15 -0800327 && (mInputMergerClassName != null
328 ? mInputMergerClassName.equals(other.mInputMergerClassName)
329 : other.mInputMergerClassName == null)
Jan Clarin91bccff2017-10-20 17:51:10 -0700330 && (mConstraints != null
331 ? mConstraints.equals(other.mConstraints)
Sumir Kataria9e637902017-11-27 14:03:47 -0800332 : other.mConstraints == null);
Jan Clarin91bccff2017-10-20 17:51:10 -0700333 }
334
335 @Override
336 public int hashCode() {
337 int result = mId.hashCode();
338 result = 31 * result + mStatus;
339 result = 31 * result + (mWorkerClassName != null ? mWorkerClassName.hashCode() : 0);
Sumir Katariaa48d7ac2017-11-28 16:24:15 -0800340 result = 31 * result
341 + (mInputMergerClassName != null ? mInputMergerClassName.hashCode() : 0);
Sumir Kataria9244d372017-11-30 13:39:07 -0800342 result = 31 * result + mArguments.hashCode();
343 result = 31 * result + mOutput.hashCode();
Jan Clarin91bccff2017-10-20 17:51:10 -0700344 result = 31 * result + (int) (mInitialDelay ^ (mInitialDelay >>> 32));
345 result = 31 * result + (int) (mIntervalDuration ^ (mIntervalDuration >>> 32));
346 result = 31 * result + (int) (mFlexDuration ^ (mFlexDuration >>> 32));
347 result = 31 * result + (mConstraints != null ? mConstraints.hashCode() : 0);
Jan Clarin91bccff2017-10-20 17:51:10 -0700348 result = 31 * result + mRunAttemptCount;
349 result = 31 * result + mBackoffPolicy;
350 result = 31 * result + (int) (mBackoffDelayDuration ^ (mBackoffDelayDuration >>> 32));
351 return result;
352 }
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800353
354 @Override
355 public String toString() {
356 return "{WorkSpec: " + mId + "}";
357 }
Sumir Kataria904ba122017-09-25 13:05:49 -0700358}