blob: cf357f9bed69ebbadfb259a29bb051cbc9410fd0 [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 Kataria564e4302018-02-14 11:22:30 -080017package androidx.work.impl.model;
Sumir Kataria904ba122017-09-25 13:05:49 -070018
Sumir Kataria8b3284f2018-04-13 09:50:18 -070019import static androidx.work.PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS;
20import static androidx.work.PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS;
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -070021import static androidx.work.WorkInfo.State.ENQUEUED;
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -070022import static androidx.work.WorkRequest.MAX_BACKOFF_MILLIS;
23import static androidx.work.WorkRequest.MIN_BACKOFF_MILLIS;
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -080024
Sumir Kataria2cdf00b2019-05-14 16:08:41 -070025import androidx.annotation.IntRange;
Rahul Ravikumare5356662019-03-04 16:22:14 -080026import androidx.annotation.NonNull;
27import androidx.annotation.RestrictTo;
28import androidx.arch.core.util.Function;
29import androidx.room.ColumnInfo;
30import androidx.room.Embedded;
31import androidx.room.Entity;
32import androidx.room.Index;
33import androidx.room.PrimaryKey;
34import androidx.room.Relation;
Sumir Kataria564e4302018-02-14 11:22:30 -080035import androidx.work.BackoffPolicy;
Sumir Kataria564e4302018-02-14 11:22:30 -080036import androidx.work.Constraints;
Sumir Kataria64e6bd82018-03-28 17:14:22 -070037import androidx.work.Data;
Sumir Katariafd60d222018-06-22 16:23:41 -070038import androidx.work.Logger;
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -070039import androidx.work.WorkInfo;
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -070040import androidx.work.WorkRequest;
Sumir Kataria564e4302018-02-14 11:22:30 -080041
Sumir Kataria4401f792018-03-20 12:52:38 -070042import java.util.ArrayList;
Sumir Katariab5728f42018-03-19 12:58:41 -070043import java.util.List;
Sumir Katariafa284c92018-04-23 14:25:53 -070044import java.util.UUID;
Sumir Katariab5728f42018-03-19 12:58:41 -070045
Sumir Kataria904ba122017-09-25 13:05:49 -070046/**
47 * Stores information about a logical unit of work.
Sumir Katariab5728f42018-03-19 12:58:41 -070048 *
49 * @hide
Sumir Kataria904ba122017-09-25 13:05:49 -070050 */
Sumir Katariab5728f42018-03-19 12:58:41 -070051@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -070052@Entity(
53 indices = {@Index(value = {"schedule_requested_at"})}
54)
Sumir Kataria904ba122017-09-25 13:05:49 -070055public class WorkSpec {
Rahul Ravikumar38e7c6a2018-12-13 16:41:34 -080056 private static final String TAG = Logger.tagWithPrefix("WorkSpec");
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -070057 public static final long SCHEDULE_NOT_REQUESTED_YET = -1;
Sumir Kataria904ba122017-09-25 13:05:49 -070058
59 @ColumnInfo(name = "id")
60 @PrimaryKey
61 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070062 public String id;
Sumir Kataria904ba122017-09-25 13:05:49 -070063
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -080064 @ColumnInfo(name = "state")
Sumir Kataria86894df2018-03-06 15:11:16 -080065 @NonNull
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -070066 public WorkInfo.State state = ENQUEUED;
Sumir Kataria904ba122017-09-25 13:05:49 -070067
Jan Clarinee302e12017-10-09 11:16:19 -070068 @ColumnInfo(name = "worker_class_name")
Sumir Kataria86894df2018-03-06 15:11:16 -080069 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070070 public String workerClassName;
Jan Clarinee302e12017-10-09 11:16:19 -070071
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080072 @ColumnInfo(name = "input_merger_class_name")
Sumir Katariab5728f42018-03-19 12:58:41 -070073 public String inputMergerClassName;
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080074
Sumir Kataria64e6bd82018-03-28 17:14:22 -070075 @ColumnInfo(name = "input")
Sumir Kataria9244d372017-11-30 13:39:07 -080076 @NonNull
Sumir Kataria64e6bd82018-03-28 17:14:22 -070077 public Data input = Data.EMPTY;
Sumir Kataria9244d372017-11-30 13:39:07 -080078
79 @ColumnInfo(name = "output")
80 @NonNull
Sumir Kataria64e6bd82018-03-28 17:14:22 -070081 public Data output = Data.EMPTY;
Sumir Kataria9244d372017-11-30 13:39:07 -080082
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -070083 @ColumnInfo(name = "initial_delay")
Sumir Katariab5728f42018-03-19 12:58:41 -070084 public long initialDelay;
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -070085
Jan Clarinee302e12017-10-09 11:16:19 -070086 @ColumnInfo(name = "interval_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -070087 public long intervalDuration;
Jan Clarinee302e12017-10-09 11:16:19 -070088
89 @ColumnInfo(name = "flex_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -070090 public long flexDuration;
Sumir Kataria904ba122017-09-25 13:05:49 -070091
92 @Embedded
Sumir Kataria86894df2018-03-06 15:11:16 -080093 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070094 public Constraints constraints = Constraints.NONE;
Sumir Kataria904ba122017-09-25 13:05:49 -070095
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070096 @ColumnInfo(name = "run_attempt_count")
Sumir Kataria2cdf00b2019-05-14 16:08:41 -070097 @IntRange(from = 0)
Sumir Katariab5728f42018-03-19 12:58:41 -070098 public int runAttemptCount;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070099
Sumir Kataria904ba122017-09-25 13:05:49 -0700100 @ColumnInfo(name = "backoff_policy")
Sumir Kataria86894df2018-03-06 15:11:16 -0800101 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -0700102 public BackoffPolicy backoffPolicy = BackoffPolicy.EXPONENTIAL;
Sumir Kataria904ba122017-09-25 13:05:49 -0700103
104 @ColumnInfo(name = "backoff_delay_duration")
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700105 public long backoffDelayDuration = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
Sumir Kataria904ba122017-09-25 13:05:49 -0700106
Sumir Katariab5728f42018-03-19 12:58:41 -0700107 /**
108 * For one-off work, this is the time that the work was unblocked by prerequisites.
109 * For periodic work, this is the time that the period started.
110 */
Jan Clarin0deb5e22017-12-06 11:26:29 -0800111 @ColumnInfo(name = "period_start_time")
Sumir Katariab5728f42018-03-19 12:58:41 -0700112 public long periodStartTime;
Jan Clarin0deb5e22017-12-06 11:26:29 -0800113
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800114 @ColumnInfo(name = "minimum_retention_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -0700115 public long minimumRetentionDuration;
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800116
Rahul Ravikumarfe793bc2018-07-09 12:43:26 -0700117 /**
118 * This field tells us if this {@link WorkSpec} instance, is actually currently scheduled and
119 * being counted against the {@code SCHEDULER_LIMIT}. This bit is reset for PeriodicWorkRequests
120 * in API < 23, because AlarmManager does not know of PeriodicWorkRequests. So for the next
121 * request to be rescheduled this field has to be reset to {@code SCHEDULE_NOT_REQUESTED_AT}.
122 * For the JobScheduler implementation, we don't reset this field because JobScheduler natively
123 * supports PeriodicWorkRequests.
124 */
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700125 @ColumnInfo(name = "schedule_requested_at")
126 public long scheduleRequestedAt = SCHEDULE_NOT_REQUESTED_YET;
127
Sumir Katariab5728f42018-03-19 12:58:41 -0700128 public WorkSpec(@NonNull String id, @NonNull String workerClassName) {
129 this.id = id;
130 this.workerClassName = workerClassName;
Sumir Kataria904ba122017-09-25 13:05:49 -0700131 }
132
Sumir Katariaa31df0312018-07-27 14:28:01 -0700133 public WorkSpec(@NonNull WorkSpec other) {
134 id = other.id;
135 workerClassName = other.workerClassName;
136 state = other.state;
137 inputMergerClassName = other.inputMergerClassName;
138 input = new Data(other.input);
139 output = new Data(other.output);
140 initialDelay = other.initialDelay;
141 intervalDuration = other.intervalDuration;
142 flexDuration = other.flexDuration;
143 constraints = new Constraints(other.constraints);
144 runAttemptCount = other.runAttemptCount;
145 backoffPolicy = other.backoffPolicy;
146 backoffDelayDuration = other.backoffDelayDuration;
147 periodStartTime = other.periodStartTime;
148 minimumRetentionDuration = other.minimumRetentionDuration;
149 scheduleRequestedAt = other.scheduleRequestedAt;
150 }
151
Sumir Kataria564e4302018-02-14 11:22:30 -0800152 /**
153 * @param backoffDelayDuration The backoff delay duration in milliseconds
154 */
Sumir Kataria904ba122017-09-25 13:05:49 -0700155 public void setBackoffDelayDuration(long backoffDelayDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800156 if (backoffDelayDuration > MAX_BACKOFF_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800157 Logger.get().warning(TAG, "Backoff delay duration exceeds maximum value");
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800158 backoffDelayDuration = MAX_BACKOFF_MILLIS;
159 }
160 if (backoffDelayDuration < MIN_BACKOFF_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800161 Logger.get().warning(TAG, "Backoff delay duration less than minimum value");
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800162 backoffDelayDuration = MIN_BACKOFF_MILLIS;
163 }
Sumir Katariab5728f42018-03-19 12:58:41 -0700164 this.backoffDelayDuration = backoffDelayDuration;
Sumir Kataria904ba122017-09-25 13:05:49 -0700165 }
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -0700166
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700167
Jan Clarinee302e12017-10-09 11:16:19 -0700168 public boolean isPeriodic() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700169 return intervalDuration != 0L;
Jan Clarinee302e12017-10-09 11:16:19 -0700170 }
171
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800172 public boolean isBackedOff() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700173 return state == ENQUEUED && runAttemptCount > 0;
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800174 }
175
Sumir Kataria6d55d8c2017-12-07 13:37:42 -0800176 /**
177 * Sets the periodic interval for this unit of work.
178 *
179 * @param intervalDuration The interval in milliseconds
180 */
Jan Clarinee302e12017-10-09 11:16:19 -0700181 public void setPeriodic(long intervalDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800182 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800183 Logger.get().warning(TAG, String.format(
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700184 "Interval duration lesser than minimum allowed value; Changed to %s",
185 MIN_PERIODIC_INTERVAL_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800186 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
187 }
Jan Clarinee302e12017-10-09 11:16:19 -0700188 setPeriodic(intervalDuration, intervalDuration);
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 * @param flexDuration The flex duration in milliseconds
196 */
Jan Clarinee302e12017-10-09 11:16:19 -0700197 public void setPeriodic(long intervalDuration, long flexDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800198 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800199 Logger.get().warning(TAG, String.format(
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700200 "Interval duration lesser than minimum allowed value; Changed to %s",
201 MIN_PERIODIC_INTERVAL_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800202 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
203 }
204 if (flexDuration < MIN_PERIODIC_FLEX_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800205 Logger.get().warning(TAG,
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700206 String.format("Flex duration lesser than minimum allowed value; Changed to %s",
207 MIN_PERIODIC_FLEX_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800208 flexDuration = MIN_PERIODIC_FLEX_MILLIS;
209 }
210 if (flexDuration > intervalDuration) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800211 Logger.get().warning(TAG,
Sumir Katariafd60d222018-06-22 16:23:41 -0700212 String.format("Flex duration greater than interval duration; Changed to %s",
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700213 intervalDuration));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800214 flexDuration = intervalDuration;
215 }
Sumir Katariab5728f42018-03-19 12:58:41 -0700216 this.intervalDuration = intervalDuration;
217 this.flexDuration = flexDuration;
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800218 }
219
Jan Clarin0deb5e22017-12-06 11:26:29 -0800220 /**
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800221 * Calculates the UTC time at which this {@link WorkSpec} should be allowed to run.
222 * This method accounts for work that is backed off or periodic.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700223 *
Sumir Katariab6430f22018-02-05 13:45:22 -0800224 * If Backoff Policy is set to {@link BackoffPolicy#EXPONENTIAL}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700225 * increases at an exponential rate with respect to the run attempt count and is capped at
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700226 * {@link WorkRequest#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700227 *
Sumir Katariab6430f22018-02-05 13:45:22 -0800228 * If Backoff Policy is set to {@link BackoffPolicy#LINEAR}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700229 * increases at an linear rate with respect to the run attempt count and is capped at
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700230 * {@link WorkRequest#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700231 *
232 * Based on {@see https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/job/JobSchedulerService.java#1125}
233 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800234 * Note that this runtime is for WorkManager internal use and may not match what the OS
235 * considers to be the next runtime.
Sumir Kataria58e4d6f2017-11-14 14:31:15 -0800236 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800237 * For jobs with constraints, this represents the earliest time at which constraints
238 * should be monitored for this work.
239 *
240 * For jobs without constraints, this represents the earliest time at which this work is
241 * allowed to run.
242 *
243 * @return UTC time at which this {@link WorkSpec} should be allowed to run.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700244 */
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800245 public long calculateNextRunTime() {
246 if (isBackedOff()) {
Sumir Katariab5728f42018-03-19 12:58:41 -0700247 boolean isLinearBackoff = (backoffPolicy == BackoffPolicy.LINEAR);
248 long delay = isLinearBackoff ? (backoffDelayDuration * runAttemptCount)
249 : (long) Math.scalb(backoffDelayDuration, runAttemptCount - 1);
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700250 return periodStartTime + Math.min(WorkRequest.MAX_BACKOFF_MILLIS, delay);
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800251 } else if (isPeriodic()) {
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700252 long now = System.currentTimeMillis();
253 long start = periodStartTime == 0 ? (now + initialDelay) : periodStartTime;
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700254 boolean isFlexApplicable = flexDuration != intervalDuration;
255 if (isFlexApplicable) {
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700256 // To correctly emulate flex, we need to set it
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700257 // to now, so the PeriodicWorkRequest has an initial delay of
258 // initialDelay + (interval - flex).
Rahul Ravikumar38beac9b2019-02-13 11:16:38 -0800259
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700260 // The subsequent runs will only add the interval duration and no flex.
261 // This gives us the following behavior:
262 // 1 => now + (interval - flex) + initialDelay = firstRunTime
263 // 2 => firstRunTime + 2 * interval - flex
264 // 3 => firstRunTime + 3 * interval - flex
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700265 long offset = periodStartTime == 0 ? (-1 * flexDuration) : 0;
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700266 return start + intervalDuration + offset;
Rahul Ravikumar67a9b192019-01-28 15:00:20 -0800267 } else {
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700268 // Don't use flexDuration for determining next run time for PeriodicWork
269 // This is because intervalDuration could equal flexDuration.
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700270
271 // The first run of a periodic work request is immediate in JobScheduler, and we
272 // need to emulate this behavior.
273 long offset = periodStartTime == 0 ? 0 : intervalDuration;
274 return start + offset;
Rahul Ravikumar67a9b192019-01-28 15:00:20 -0800275 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700276 } else {
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700277 // We are checking for (periodStartTime == 0) to support our testing use case.
278 // For newly created WorkSpecs periodStartTime will always be 0.
279 long start = (periodStartTime == 0) ? System.currentTimeMillis() : periodStartTime;
280 return start + initialDelay;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700281 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700282 }
Jan Clarin91bccff2017-10-20 17:51:10 -0700283
Rahul Ravikumare92262f2018-02-07 18:48:15 -0800284 /**
285 * @return <code>true</code> if the {@link WorkSpec} has constraints.
286 */
287 public boolean hasConstraints() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700288 return !Constraints.NONE.equals(constraints);
Rahul Ravikumare92262f2018-02-07 18:48:15 -0800289 }
290
Jan Clarin91bccff2017-10-20 17:51:10 -0700291 @Override
292 public boolean equals(Object o) {
Sumir Kataria86894df2018-03-06 15:11:16 -0800293 if (this == o) return true;
294 if (o == null || getClass() != o.getClass()) return false;
295
296 WorkSpec workSpec = (WorkSpec) o;
297
Sumir Katariab5728f42018-03-19 12:58:41 -0700298 if (initialDelay != workSpec.initialDelay) return false;
299 if (intervalDuration != workSpec.intervalDuration) return false;
300 if (flexDuration != workSpec.flexDuration) return false;
301 if (runAttemptCount != workSpec.runAttemptCount) return false;
302 if (backoffDelayDuration != workSpec.backoffDelayDuration) return false;
303 if (periodStartTime != workSpec.periodStartTime) return false;
304 if (minimumRetentionDuration != workSpec.minimumRetentionDuration) return false;
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700305 if (scheduleRequestedAt != workSpec.scheduleRequestedAt) return false;
Sumir Katariab5728f42018-03-19 12:58:41 -0700306 if (!id.equals(workSpec.id)) return false;
307 if (state != workSpec.state) return false;
308 if (!workerClassName.equals(workSpec.workerClassName)) return false;
309 if (inputMergerClassName != null ? !inputMergerClassName.equals(
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700310 workSpec.inputMergerClassName)
311 : workSpec.inputMergerClassName != null) {
Jan Clarin91bccff2017-10-20 17:51:10 -0700312 return false;
313 }
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700314 if (!input.equals(workSpec.input)) return false;
Sumir Katariab5728f42018-03-19 12:58:41 -0700315 if (!output.equals(workSpec.output)) return false;
316 if (!constraints.equals(workSpec.constraints)) return false;
317 return backoffPolicy == workSpec.backoffPolicy;
Jan Clarin91bccff2017-10-20 17:51:10 -0700318 }
319
320 @Override
321 public int hashCode() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700322 int result = id.hashCode();
323 result = 31 * result + state.hashCode();
324 result = 31 * result + workerClassName.hashCode();
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700325 result = 31 * result + (inputMergerClassName != null ? inputMergerClassName.hashCode() : 0);
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700326 result = 31 * result + input.hashCode();
Sumir Katariab5728f42018-03-19 12:58:41 -0700327 result = 31 * result + output.hashCode();
328 result = 31 * result + (int) (initialDelay ^ (initialDelay >>> 32));
329 result = 31 * result + (int) (intervalDuration ^ (intervalDuration >>> 32));
330 result = 31 * result + (int) (flexDuration ^ (flexDuration >>> 32));
331 result = 31 * result + constraints.hashCode();
332 result = 31 * result + runAttemptCount;
333 result = 31 * result + backoffPolicy.hashCode();
334 result = 31 * result + (int) (backoffDelayDuration ^ (backoffDelayDuration >>> 32));
335 result = 31 * result + (int) (periodStartTime ^ (periodStartTime >>> 32));
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700336 result = 31 * result + (int) (minimumRetentionDuration ^ (minimumRetentionDuration >>> 32));
337 result = 31 * result + (int) (scheduleRequestedAt ^ (scheduleRequestedAt >>> 32));
Jan Clarin91bccff2017-10-20 17:51:10 -0700338 return result;
339 }
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800340
Aurimas Liutikas2932b7d2019-06-13 17:56:26 -0700341 @NonNull
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800342 @Override
343 public String toString() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700344 return "{WorkSpec: " + id + "}";
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800345 }
Sumir Katariad2db35d2017-12-14 14:08:48 -0800346
347 /**
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800348 * A POJO containing the ID and state of a WorkSpec.
Sumir Katariad2db35d2017-12-14 14:08:48 -0800349 */
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800350 public static class IdAndState {
Sumir Katariad2db35d2017-12-14 14:08:48 -0800351
352 @ColumnInfo(name = "id")
353 public String id;
354
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800355 @ColumnInfo(name = "state")
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700356 public WorkInfo.State state;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800357
358 @Override
359 public boolean equals(Object o) {
360 if (this == o) return true;
361 if (o == null || getClass() != o.getClass()) return false;
362
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800363 IdAndState that = (IdAndState) o;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800364
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800365 if (state != that.state) return false;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800366 return id.equals(that.id);
367 }
368
369 @Override
370 public int hashCode() {
371 int result = id.hashCode();
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800372 result = 31 * result + state.hashCode();
373 return result;
374 }
375 }
376
377 /**
Sumir Katariac1d15182019-04-04 16:37:07 -0700378 * A POJO containing the ID, state, output, tags, and run attempt count of a WorkSpec.
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800379 */
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700380 public static class WorkInfoPojo {
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800381
382 @ColumnInfo(name = "id")
383 public String id;
384
385 @ColumnInfo(name = "state")
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700386 public WorkInfo.State state;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800387
388 @ColumnInfo(name = "output")
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700389 public Data output;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800390
Sumir Katariac1d15182019-04-04 16:37:07 -0700391 @ColumnInfo(name = "run_attempt_count")
392 public int runAttemptCount;
393
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700394 @Relation(
395 parentColumn = "id",
396 entityColumn = "work_spec_id",
397 entity = WorkTag.class,
398 projection = {"tag"})
399 public List<String> tags;
400
Rahul Ravikumarf9c2cfb2019-07-19 13:27:39 -0700401 // This is actually a 1-1 relationship. However Room 2.1 models the type as a List.
402 // This will change in Room 2.2
403 @Relation(
404 parentColumn = "id",
405 entityColumn = "work_spec_id",
406 entity = WorkProgress.class,
407 projection = {"progress"})
408 public List<Data> progress;
409
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700410 /**
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700411 * Converts this POJO to a {@link WorkInfo}.
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700412 *
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700413 * @return The {@link WorkInfo} represented by this POJO
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700414 */
Rahul Ravikumarf9c2cfb2019-07-19 13:27:39 -0700415 @NonNull
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700416 public WorkInfo toWorkInfo() {
Rahul Ravikumarf9c2cfb2019-07-19 13:27:39 -0700417 Data progress = this.progress != null && !this.progress.isEmpty()
418 ? this.progress.get(0)
419 : Data.EMPTY;
420
421 return new WorkInfo(
422 UUID.fromString(id),
423 state,
424 output,
425 tags,
426 progress,
427 runAttemptCount);
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700428 }
429
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800430 @Override
431 public boolean equals(Object o) {
432 if (this == o) return true;
433 if (o == null || getClass() != o.getClass()) return false;
434
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700435 WorkInfoPojo that = (WorkInfoPojo) o;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800436
Sumir Katariac1d15182019-04-04 16:37:07 -0700437 if (runAttemptCount != that.runAttemptCount) return false;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700438 if (id != null ? !id.equals(that.id) : that.id != null) return false;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800439 if (state != that.state) return false;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700440 if (output != null ? !output.equals(that.output) : that.output != null) return false;
Rahul Ravikumarf9c2cfb2019-07-19 13:27:39 -0700441 if (tags != null ? !tags.equals(that.tags) : that.tags != null) return false;
442 return progress != null ? progress.equals(that.progress) : that.progress == null;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800443 }
444
445 @Override
446 public int hashCode() {
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700447 int result = id != null ? id.hashCode() : 0;
448 result = 31 * result + (state != null ? state.hashCode() : 0);
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800449 result = 31 * result + (output != null ? output.hashCode() : 0);
Sumir Katariac1d15182019-04-04 16:37:07 -0700450 result = 31 * result + runAttemptCount;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700451 result = 31 * result + (tags != null ? tags.hashCode() : 0);
Rahul Ravikumarf9c2cfb2019-07-19 13:27:39 -0700452 result = 31 * result + (progress != null ? progress.hashCode() : 0);
Sumir Katariad2db35d2017-12-14 14:08:48 -0800453 return result;
454 }
455 }
Sumir Kataria4401f792018-03-20 12:52:38 -0700456
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700457 public static final Function<List<WorkInfoPojo>, List<WorkInfo>> WORK_INFO_MAPPER =
458 new Function<List<WorkInfoPojo>, List<WorkInfo>>() {
Sumir Kataria4401f792018-03-20 12:52:38 -0700459 @Override
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700460 public List<WorkInfo> apply(List<WorkInfoPojo> input) {
Sumir Kataria4401f792018-03-20 12:52:38 -0700461 if (input == null) {
462 return null;
463 }
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700464 List<WorkInfo> output = new ArrayList<>(input.size());
465 for (WorkInfoPojo in : input) {
466 output.add(in.toWorkInfo());
Sumir Kataria4401f792018-03-20 12:52:38 -0700467 }
468 return output;
469 }
470 };
Sumir Kataria904ba122017-09-25 13:05:49 -0700471}