blob: 9f8029cd1e71aeee3e667bd9e12f507dd1d8877d [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
Rahul Ravikumare5356662019-03-04 16:22:14 -080025import androidx.annotation.NonNull;
26import androidx.annotation.RestrictTo;
27import androidx.arch.core.util.Function;
28import androidx.room.ColumnInfo;
29import androidx.room.Embedded;
30import androidx.room.Entity;
31import androidx.room.Index;
32import androidx.room.PrimaryKey;
33import androidx.room.Relation;
Sumir Kataria564e4302018-02-14 11:22:30 -080034import androidx.work.BackoffPolicy;
Sumir Kataria564e4302018-02-14 11:22:30 -080035import androidx.work.Constraints;
Sumir Kataria64e6bd82018-03-28 17:14:22 -070036import androidx.work.Data;
Sumir Katariafd60d222018-06-22 16:23:41 -070037import androidx.work.Logger;
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -070038import androidx.work.WorkInfo;
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -070039import androidx.work.WorkRequest;
Sumir Kataria564e4302018-02-14 11:22:30 -080040
Sumir Kataria4401f792018-03-20 12:52:38 -070041import java.util.ArrayList;
Sumir Katariab5728f42018-03-19 12:58:41 -070042import java.util.List;
Sumir Katariafa284c92018-04-23 14:25:53 -070043import java.util.UUID;
Sumir Katariab5728f42018-03-19 12:58:41 -070044
Sumir Kataria904ba122017-09-25 13:05:49 -070045/**
46 * Stores information about a logical unit of work.
Sumir Katariab5728f42018-03-19 12:58:41 -070047 *
48 * @hide
Sumir Kataria904ba122017-09-25 13:05:49 -070049 */
Sumir Katariab5728f42018-03-19 12:58:41 -070050@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -070051@Entity(
52 indices = {@Index(value = {"schedule_requested_at"})}
53)
Sumir Kataria904ba122017-09-25 13:05:49 -070054public class WorkSpec {
Rahul Ravikumar38e7c6a2018-12-13 16:41:34 -080055 private static final String TAG = Logger.tagWithPrefix("WorkSpec");
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -070056 public static final long SCHEDULE_NOT_REQUESTED_YET = -1;
Sumir Kataria904ba122017-09-25 13:05:49 -070057
58 @ColumnInfo(name = "id")
59 @PrimaryKey
60 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070061 public String id;
Sumir Kataria904ba122017-09-25 13:05:49 -070062
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -080063 @ColumnInfo(name = "state")
Sumir Kataria86894df2018-03-06 15:11:16 -080064 @NonNull
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -070065 public WorkInfo.State state = ENQUEUED;
Sumir Kataria904ba122017-09-25 13:05:49 -070066
Jan Clarinee302e12017-10-09 11:16:19 -070067 @ColumnInfo(name = "worker_class_name")
Sumir Kataria86894df2018-03-06 15:11:16 -080068 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070069 public String workerClassName;
Jan Clarinee302e12017-10-09 11:16:19 -070070
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080071 @ColumnInfo(name = "input_merger_class_name")
Sumir Katariab5728f42018-03-19 12:58:41 -070072 public String inputMergerClassName;
Sumir Katariaa48d7ac2017-11-28 16:24:15 -080073
Sumir Kataria64e6bd82018-03-28 17:14:22 -070074 @ColumnInfo(name = "input")
Sumir Kataria9244d372017-11-30 13:39:07 -080075 @NonNull
Sumir Kataria64e6bd82018-03-28 17:14:22 -070076 public Data input = Data.EMPTY;
Sumir Kataria9244d372017-11-30 13:39:07 -080077
78 @ColumnInfo(name = "output")
79 @NonNull
Sumir Kataria64e6bd82018-03-28 17:14:22 -070080 public Data output = Data.EMPTY;
Sumir Kataria9244d372017-11-30 13:39:07 -080081
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -070082 @ColumnInfo(name = "initial_delay")
Sumir Katariab5728f42018-03-19 12:58:41 -070083 public long initialDelay;
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -070084
Jan Clarinee302e12017-10-09 11:16:19 -070085 @ColumnInfo(name = "interval_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -070086 public long intervalDuration;
Jan Clarinee302e12017-10-09 11:16:19 -070087
88 @ColumnInfo(name = "flex_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -070089 public long flexDuration;
Sumir Kataria904ba122017-09-25 13:05:49 -070090
91 @Embedded
Sumir Kataria86894df2018-03-06 15:11:16 -080092 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -070093 public Constraints constraints = Constraints.NONE;
Sumir Kataria904ba122017-09-25 13:05:49 -070094
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070095 @ColumnInfo(name = "run_attempt_count")
Sumir Katariab5728f42018-03-19 12:58:41 -070096 public int runAttemptCount;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -070097
Sumir Kataria904ba122017-09-25 13:05:49 -070098 @ColumnInfo(name = "backoff_policy")
Sumir Kataria86894df2018-03-06 15:11:16 -080099 @NonNull
Sumir Katariab5728f42018-03-19 12:58:41 -0700100 public BackoffPolicy backoffPolicy = BackoffPolicy.EXPONENTIAL;
Sumir Kataria904ba122017-09-25 13:05:49 -0700101
102 @ColumnInfo(name = "backoff_delay_duration")
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700103 public long backoffDelayDuration = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
Sumir Kataria904ba122017-09-25 13:05:49 -0700104
Sumir Katariab5728f42018-03-19 12:58:41 -0700105 /**
106 * For one-off work, this is the time that the work was unblocked by prerequisites.
107 * For periodic work, this is the time that the period started.
108 */
Jan Clarin0deb5e22017-12-06 11:26:29 -0800109 @ColumnInfo(name = "period_start_time")
Sumir Katariab5728f42018-03-19 12:58:41 -0700110 public long periodStartTime;
Jan Clarin0deb5e22017-12-06 11:26:29 -0800111
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800112 @ColumnInfo(name = "minimum_retention_duration")
Sumir Katariab5728f42018-03-19 12:58:41 -0700113 public long minimumRetentionDuration;
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800114
Rahul Ravikumarfe793bc2018-07-09 12:43:26 -0700115 /**
116 * This field tells us if this {@link WorkSpec} instance, is actually currently scheduled and
117 * being counted against the {@code SCHEDULER_LIMIT}. This bit is reset for PeriodicWorkRequests
118 * in API < 23, because AlarmManager does not know of PeriodicWorkRequests. So for the next
119 * request to be rescheduled this field has to be reset to {@code SCHEDULE_NOT_REQUESTED_AT}.
120 * For the JobScheduler implementation, we don't reset this field because JobScheduler natively
121 * supports PeriodicWorkRequests.
122 */
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700123 @ColumnInfo(name = "schedule_requested_at")
124 public long scheduleRequestedAt = SCHEDULE_NOT_REQUESTED_YET;
125
Sumir Katariab5728f42018-03-19 12:58:41 -0700126 public WorkSpec(@NonNull String id, @NonNull String workerClassName) {
127 this.id = id;
128 this.workerClassName = workerClassName;
Sumir Kataria904ba122017-09-25 13:05:49 -0700129 }
130
Sumir Katariaa31df0312018-07-27 14:28:01 -0700131 public WorkSpec(@NonNull WorkSpec other) {
132 id = other.id;
133 workerClassName = other.workerClassName;
134 state = other.state;
135 inputMergerClassName = other.inputMergerClassName;
136 input = new Data(other.input);
137 output = new Data(other.output);
138 initialDelay = other.initialDelay;
139 intervalDuration = other.intervalDuration;
140 flexDuration = other.flexDuration;
141 constraints = new Constraints(other.constraints);
142 runAttemptCount = other.runAttemptCount;
143 backoffPolicy = other.backoffPolicy;
144 backoffDelayDuration = other.backoffDelayDuration;
145 periodStartTime = other.periodStartTime;
146 minimumRetentionDuration = other.minimumRetentionDuration;
147 scheduleRequestedAt = other.scheduleRequestedAt;
148 }
149
Sumir Kataria564e4302018-02-14 11:22:30 -0800150 /**
151 * @param backoffDelayDuration The backoff delay duration in milliseconds
152 */
Sumir Kataria904ba122017-09-25 13:05:49 -0700153 public void setBackoffDelayDuration(long backoffDelayDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800154 if (backoffDelayDuration > MAX_BACKOFF_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800155 Logger.get().warning(TAG, "Backoff delay duration exceeds maximum value");
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800156 backoffDelayDuration = MAX_BACKOFF_MILLIS;
157 }
158 if (backoffDelayDuration < MIN_BACKOFF_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800159 Logger.get().warning(TAG, "Backoff delay duration less than minimum value");
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800160 backoffDelayDuration = MIN_BACKOFF_MILLIS;
161 }
Sumir Katariab5728f42018-03-19 12:58:41 -0700162 this.backoffDelayDuration = backoffDelayDuration;
Sumir Kataria904ba122017-09-25 13:05:49 -0700163 }
Xyan Bhatnagara1af78b2017-10-04 17:36:36 -0700164
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700165
Jan Clarinee302e12017-10-09 11:16:19 -0700166 public boolean isPeriodic() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700167 return intervalDuration != 0L;
Jan Clarinee302e12017-10-09 11:16:19 -0700168 }
169
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800170 public boolean isBackedOff() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700171 return state == ENQUEUED && runAttemptCount > 0;
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800172 }
173
Sumir Kataria6d55d8c2017-12-07 13:37:42 -0800174 /**
175 * Sets the periodic interval for this unit of work.
176 *
177 * @param intervalDuration The interval in milliseconds
178 */
Jan Clarinee302e12017-10-09 11:16:19 -0700179 public void setPeriodic(long intervalDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800180 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800181 Logger.get().warning(TAG, String.format(
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700182 "Interval duration lesser than minimum allowed value; Changed to %s",
183 MIN_PERIODIC_INTERVAL_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800184 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
185 }
Jan Clarinee302e12017-10-09 11:16:19 -0700186 setPeriodic(intervalDuration, intervalDuration);
187 }
188
Sumir Kataria6d55d8c2017-12-07 13:37:42 -0800189 /**
190 * Sets the periodic interval for this unit of work.
191 *
192 * @param intervalDuration The interval in milliseconds
193 * @param flexDuration The flex duration in milliseconds
194 */
Jan Clarinee302e12017-10-09 11:16:19 -0700195 public void setPeriodic(long intervalDuration, long flexDuration) {
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800196 if (intervalDuration < MIN_PERIODIC_INTERVAL_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800197 Logger.get().warning(TAG, String.format(
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700198 "Interval duration lesser than minimum allowed value; Changed to %s",
199 MIN_PERIODIC_INTERVAL_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800200 intervalDuration = MIN_PERIODIC_INTERVAL_MILLIS;
201 }
202 if (flexDuration < MIN_PERIODIC_FLEX_MILLIS) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800203 Logger.get().warning(TAG,
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700204 String.format("Flex duration lesser than minimum allowed value; Changed to %s",
205 MIN_PERIODIC_FLEX_MILLIS));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800206 flexDuration = MIN_PERIODIC_FLEX_MILLIS;
207 }
208 if (flexDuration > intervalDuration) {
Sumir Katariabd134a02018-11-29 16:02:17 -0800209 Logger.get().warning(TAG,
Sumir Katariafd60d222018-06-22 16:23:41 -0700210 String.format("Flex duration greater than interval duration; Changed to %s",
Rahul Ravikumar697d6a42018-04-16 15:44:09 -0700211 intervalDuration));
Sumir Kataria1cd0e4e2017-12-12 10:53:46 -0800212 flexDuration = intervalDuration;
213 }
Sumir Katariab5728f42018-03-19 12:58:41 -0700214 this.intervalDuration = intervalDuration;
215 this.flexDuration = flexDuration;
Sumir Kataria3d5949e2018-03-06 13:32:20 -0800216 }
217
Jan Clarin0deb5e22017-12-06 11:26:29 -0800218 /**
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800219 * Calculates the UTC time at which this {@link WorkSpec} should be allowed to run.
220 * This method accounts for work that is backed off or periodic.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700221 *
Sumir Katariab6430f22018-02-05 13:45:22 -0800222 * If Backoff Policy is set to {@link BackoffPolicy#EXPONENTIAL}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700223 * increases at an exponential rate with respect to the run attempt count and is capped at
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700224 * {@link WorkRequest#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700225 *
Sumir Katariab6430f22018-02-05 13:45:22 -0800226 * If Backoff Policy is set to {@link BackoffPolicy#LINEAR}, then delay
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700227 * increases at an linear rate with respect to the run attempt count and is capped at
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700228 * {@link WorkRequest#MAX_BACKOFF_MILLIS}.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700229 *
230 * Based on {@see https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/job/JobSchedulerService.java#1125}
231 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800232 * Note that this runtime is for WorkManager internal use and may not match what the OS
233 * considers to be the next runtime.
Sumir Kataria58e4d6f2017-11-14 14:31:15 -0800234 *
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800235 * For jobs with constraints, this represents the earliest time at which constraints
236 * should be monitored for this work.
237 *
238 * For jobs without constraints, this represents the earliest time at which this work is
239 * allowed to run.
240 *
241 * @return UTC time at which this {@link WorkSpec} should be allowed to run.
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700242 */
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800243 public long calculateNextRunTime() {
244 if (isBackedOff()) {
Sumir Katariab5728f42018-03-19 12:58:41 -0700245 boolean isLinearBackoff = (backoffPolicy == BackoffPolicy.LINEAR);
246 long delay = isLinearBackoff ? (backoffDelayDuration * runAttemptCount)
247 : (long) Math.scalb(backoffDelayDuration, runAttemptCount - 1);
Rahul Ravikumar7031a0f2018-04-19 14:24:30 -0700248 return periodStartTime + Math.min(WorkRequest.MAX_BACKOFF_MILLIS, delay);
Xyan Bhatnagar1c78bb22017-12-08 15:48:19 -0800249 } else if (isPeriodic()) {
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700250 long now = System.currentTimeMillis();
251 long start = periodStartTime == 0 ? (now + initialDelay) : periodStartTime;
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700252 boolean isFlexApplicable = flexDuration != intervalDuration;
253 if (isFlexApplicable) {
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700254 // To correctly emulate flex, we need to set it
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700255 // to now, so the PeriodicWorkRequest has an initial delay of
256 // initialDelay + (interval - flex).
Rahul Ravikumar38beac9b2019-02-13 11:16:38 -0800257
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700258 // The subsequent runs will only add the interval duration and no flex.
259 // This gives us the following behavior:
260 // 1 => now + (interval - flex) + initialDelay = firstRunTime
261 // 2 => firstRunTime + 2 * interval - flex
262 // 3 => firstRunTime + 3 * interval - flex
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700263 long offset = periodStartTime == 0 ? (-1 * flexDuration) : 0;
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700264 return start + intervalDuration + offset;
Rahul Ravikumar67a9b192019-01-28 15:00:20 -0800265 } else {
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700266 // Don't use flexDuration for determining next run time for PeriodicWork
267 // This is because intervalDuration could equal flexDuration.
Rahul Ravikumar25dc2192019-04-29 10:27:18 -0700268
269 // The first run of a periodic work request is immediate in JobScheduler, and we
270 // need to emulate this behavior.
271 long offset = periodStartTime == 0 ? 0 : intervalDuration;
272 return start + offset;
Rahul Ravikumar67a9b192019-01-28 15:00:20 -0800273 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700274 } else {
Rahul Ravikumar4d5c5ce2019-04-25 16:09:07 -0700275 // We are checking for (periodStartTime == 0) to support our testing use case.
276 // For newly created WorkSpecs periodStartTime will always be 0.
277 long start = (periodStartTime == 0) ? System.currentTimeMillis() : periodStartTime;
278 return start + initialDelay;
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700279 }
Xyan Bhatnagar367c6492017-10-06 11:20:02 -0700280 }
Jan Clarin91bccff2017-10-20 17:51:10 -0700281
Rahul Ravikumare92262f2018-02-07 18:48:15 -0800282 /**
283 * @return <code>true</code> if the {@link WorkSpec} has constraints.
284 */
285 public boolean hasConstraints() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700286 return !Constraints.NONE.equals(constraints);
Rahul Ravikumare92262f2018-02-07 18:48:15 -0800287 }
288
Jan Clarin91bccff2017-10-20 17:51:10 -0700289 @Override
290 public boolean equals(Object o) {
Sumir Kataria86894df2018-03-06 15:11:16 -0800291 if (this == o) return true;
292 if (o == null || getClass() != o.getClass()) return false;
293
294 WorkSpec workSpec = (WorkSpec) o;
295
Sumir Katariab5728f42018-03-19 12:58:41 -0700296 if (initialDelay != workSpec.initialDelay) return false;
297 if (intervalDuration != workSpec.intervalDuration) return false;
298 if (flexDuration != workSpec.flexDuration) return false;
299 if (runAttemptCount != workSpec.runAttemptCount) return false;
300 if (backoffDelayDuration != workSpec.backoffDelayDuration) return false;
301 if (periodStartTime != workSpec.periodStartTime) return false;
302 if (minimumRetentionDuration != workSpec.minimumRetentionDuration) return false;
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700303 if (scheduleRequestedAt != workSpec.scheduleRequestedAt) return false;
Sumir Katariab5728f42018-03-19 12:58:41 -0700304 if (!id.equals(workSpec.id)) return false;
305 if (state != workSpec.state) return false;
306 if (!workerClassName.equals(workSpec.workerClassName)) return false;
307 if (inputMergerClassName != null ? !inputMergerClassName.equals(
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700308 workSpec.inputMergerClassName)
309 : workSpec.inputMergerClassName != null) {
Jan Clarin91bccff2017-10-20 17:51:10 -0700310 return false;
311 }
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700312 if (!input.equals(workSpec.input)) return false;
Sumir Katariab5728f42018-03-19 12:58:41 -0700313 if (!output.equals(workSpec.output)) return false;
314 if (!constraints.equals(workSpec.constraints)) return false;
315 return backoffPolicy == workSpec.backoffPolicy;
Jan Clarin91bccff2017-10-20 17:51:10 -0700316 }
317
318 @Override
319 public int hashCode() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700320 int result = id.hashCode();
321 result = 31 * result + state.hashCode();
322 result = 31 * result + workerClassName.hashCode();
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700323 result = 31 * result + (inputMergerClassName != null ? inputMergerClassName.hashCode() : 0);
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700324 result = 31 * result + input.hashCode();
Sumir Katariab5728f42018-03-19 12:58:41 -0700325 result = 31 * result + output.hashCode();
326 result = 31 * result + (int) (initialDelay ^ (initialDelay >>> 32));
327 result = 31 * result + (int) (intervalDuration ^ (intervalDuration >>> 32));
328 result = 31 * result + (int) (flexDuration ^ (flexDuration >>> 32));
329 result = 31 * result + constraints.hashCode();
330 result = 31 * result + runAttemptCount;
331 result = 31 * result + backoffPolicy.hashCode();
332 result = 31 * result + (int) (backoffDelayDuration ^ (backoffDelayDuration >>> 32));
333 result = 31 * result + (int) (periodStartTime ^ (periodStartTime >>> 32));
Rahul Ravikumar9f91ee82018-03-20 17:33:38 -0700334 result = 31 * result + (int) (minimumRetentionDuration ^ (minimumRetentionDuration >>> 32));
335 result = 31 * result + (int) (scheduleRequestedAt ^ (scheduleRequestedAt >>> 32));
Jan Clarin91bccff2017-10-20 17:51:10 -0700336 return result;
337 }
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800338
339 @Override
340 public String toString() {
Sumir Katariab5728f42018-03-19 12:58:41 -0700341 return "{WorkSpec: " + id + "}";
Xyan Bhatnagarbbe9f312017-11-16 10:31:14 -0800342 }
Sumir Katariad2db35d2017-12-14 14:08:48 -0800343
344 /**
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800345 * A POJO containing the ID and state of a WorkSpec.
Sumir Katariad2db35d2017-12-14 14:08:48 -0800346 */
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800347 public static class IdAndState {
Sumir Katariad2db35d2017-12-14 14:08:48 -0800348
349 @ColumnInfo(name = "id")
350 public String id;
351
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800352 @ColumnInfo(name = "state")
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700353 public WorkInfo.State state;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800354
355 @Override
356 public boolean equals(Object o) {
357 if (this == o) return true;
358 if (o == null || getClass() != o.getClass()) return false;
359
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800360 IdAndState that = (IdAndState) o;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800361
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800362 if (state != that.state) return false;
Sumir Katariad2db35d2017-12-14 14:08:48 -0800363 return id.equals(that.id);
364 }
365
366 @Override
367 public int hashCode() {
368 int result = id.hashCode();
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800369 result = 31 * result + state.hashCode();
370 return result;
371 }
372 }
373
374 /**
Sumir Katariac1d15182019-04-04 16:37:07 -0700375 * A POJO containing the ID, state, output, tags, and run attempt count of a WorkSpec.
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800376 */
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700377 public static class WorkInfoPojo {
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800378
379 @ColumnInfo(name = "id")
380 public String id;
381
382 @ColumnInfo(name = "state")
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700383 public WorkInfo.State state;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800384
385 @ColumnInfo(name = "output")
Sumir Kataria64e6bd82018-03-28 17:14:22 -0700386 public Data output;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800387
Sumir Katariac1d15182019-04-04 16:37:07 -0700388 @ColumnInfo(name = "run_attempt_count")
389 public int runAttemptCount;
390
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700391 @Relation(
392 parentColumn = "id",
393 entityColumn = "work_spec_id",
394 entity = WorkTag.class,
395 projection = {"tag"})
396 public List<String> tags;
397
398 /**
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700399 * Converts this POJO to a {@link WorkInfo}.
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700400 *
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700401 * @return The {@link WorkInfo} represented by this POJO
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700402 */
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700403 public WorkInfo toWorkInfo() {
Sumir Katariac1d15182019-04-04 16:37:07 -0700404 return new WorkInfo(UUID.fromString(id), state, output, tags, runAttemptCount);
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700405 }
406
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800407 @Override
408 public boolean equals(Object o) {
409 if (this == o) return true;
410 if (o == null || getClass() != o.getClass()) return false;
411
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700412 WorkInfoPojo that = (WorkInfoPojo) o;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800413
Sumir Katariac1d15182019-04-04 16:37:07 -0700414 if (runAttemptCount != that.runAttemptCount) return false;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700415 if (id != null ? !id.equals(that.id) : that.id != null) return false;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800416 if (state != that.state) return false;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700417 if (output != null ? !output.equals(that.output) : that.output != null) return false;
418 return tags != null ? tags.equals(that.tags) : that.tags == null;
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800419 }
420
421 @Override
422 public int hashCode() {
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700423 int result = id != null ? id.hashCode() : 0;
424 result = 31 * result + (state != null ? state.hashCode() : 0);
Sumir Kataria9cf4f3d2018-02-06 09:57:51 -0800425 result = 31 * result + (output != null ? output.hashCode() : 0);
Sumir Katariac1d15182019-04-04 16:37:07 -0700426 result = 31 * result + runAttemptCount;
Sumir Katariacc5ae8c2018-03-12 12:57:46 -0700427 result = 31 * result + (tags != null ? tags.hashCode() : 0);
Sumir Katariad2db35d2017-12-14 14:08:48 -0800428 return result;
429 }
430 }
Sumir Kataria4401f792018-03-20 12:52:38 -0700431
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700432 public static final Function<List<WorkInfoPojo>, List<WorkInfo>> WORK_INFO_MAPPER =
433 new Function<List<WorkInfoPojo>, List<WorkInfo>>() {
Sumir Kataria4401f792018-03-20 12:52:38 -0700434 @Override
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700435 public List<WorkInfo> apply(List<WorkInfoPojo> input) {
Sumir Kataria4401f792018-03-20 12:52:38 -0700436 if (input == null) {
437 return null;
438 }
Rahul Ravikumar7f7ab612018-11-01 16:22:11 -0700439 List<WorkInfo> output = new ArrayList<>(input.size());
440 for (WorkInfoPojo in : input) {
441 output.add(in.toWorkInfo());
Sumir Kataria4401f792018-03-20 12:52:38 -0700442 }
443 return output;
444 }
445 };
Sumir Kataria904ba122017-09-25 13:05:49 -0700446}