144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar/*
244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * Copyright 2018 The Android Open Source Project
344235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar *
444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * Licensed under the Apache License, Version 2.0 (the "License");
544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * you may not use this file except in compliance with the License.
644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * You may obtain a copy of the License at
744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar *
844235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar *      http://www.apache.org/licenses/LICENSE-2.0
944235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar *
1044235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * Unless required by applicable law or agreed to in writing, software
1144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * distributed under the License is distributed on an "AS IS" BASIS,
1244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1344235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * See the License for the specific language governing permissions and
1444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * limitations under the License.
1544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar */
1644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
17564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariapackage androidx.work.impl;
1844235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
1944235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumarimport android.arch.lifecycle.LiveData;
20d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Katariaimport android.os.Looper;
2144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumarimport android.support.annotation.NonNull;
2244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumarimport android.support.annotation.Nullable;
2344235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumarimport android.support.annotation.RestrictTo;
24d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Katariaimport android.support.annotation.WorkerThread;
2544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumarimport android.text.TextUtils;
26697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumarimport android.util.Log;
2744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
2887e8dc0458781cd41a5ee990be811790ac7f4e88Sumir Katariaimport androidx.work.ArrayCreatingInputMerger;
29564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.ExistingWorkPolicy;
307031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumarimport androidx.work.OneTimeWorkRequest;
3162e0be94ea7e54ca9227564b14c1c6736ae770f7Sumir Katariaimport androidx.work.SynchronousWorkContinuation;
32564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.WorkContinuation;
338b3284fa4a62568df91f706b0b2334284794008fSumir Katariaimport androidx.work.WorkRequest;
34564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.WorkStatus;
35564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariaimport androidx.work.impl.utils.EnqueueRunnable;
3655a030183e5b974f3ebf04ef5cd9ab83f3557dadSumir Katariaimport androidx.work.impl.workers.CombineContinuationsWorker;
37564e43098c323d1a90be53c190b8fdbdde973505Sumir Kataria
38707219fe57da2c3b270035c109ab865f01287ae9Rahul Ravikumarimport java.util.ArrayList;
39707219fe57da2c3b270035c109ab865f01287ae9Rahul Ravikumarimport java.util.Collections;
402ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumarimport java.util.HashSet;
41707219fe57da2c3b270035c109ab865f01287ae9Rahul Ravikumarimport java.util.List;
422ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumarimport java.util.Set;
43707219fe57da2c3b270035c109ab865f01287ae9Rahul Ravikumar
4444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar/**
4544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * A concrete implementation of {@link WorkContinuation}.
4644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar *
4744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar * @hide
4844235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar */
4944235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
504484814aab16eec8d004c9e560ed5e25e0272d4dSumir Katariapublic class WorkContinuationImpl extends WorkContinuation
5162e0be94ea7e54ca9227564b14c1c6736ae770f7Sumir Kataria        implements SynchronousWorkContinuation {
5244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
5387d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    private static final String TAG = "WorkContinuationImpl";
5444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
5587d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    private final WorkManagerImpl mWorkManagerImpl;
563665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria    private final String mName;
57b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria    private final ExistingWorkPolicy mExistingWorkPolicy;
587031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar    private final List<? extends WorkRequest> mWork;
5959cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    private final List<String> mIds;
6087d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    private final List<String> mAllIds;
6159cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    private final List<WorkContinuationImpl> mParents;
6287d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    private boolean mEnqueued;
6344235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
6487d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    @NonNull
6587d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    public WorkManagerImpl getWorkManagerImpl() {
6687d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mWorkManagerImpl;
6787d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
6887d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar
6987d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    @Nullable
703665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria    public String getName() {
713665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria        return mName;
7287d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
7304f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria
74b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria    public ExistingWorkPolicy getExistingWorkPolicy() {
7587d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mExistingWorkPolicy;
7687d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
7787d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar
7887d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    @NonNull
797031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar    public List<? extends WorkRequest> getWork() {
8087d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mWork;
8187d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
8204f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria
8387d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    @NonNull
8459cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    public List<String> getIds() {
8587d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mIds;
8687d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
8744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
8887d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    public List<String> getAllIds() {
8987d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mAllIds;
9087d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
9144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
9287d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    public boolean isEnqueued() {
9387d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        return mEnqueued;
9487d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
9544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
9687d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    /**
9787d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar     * Marks the {@link WorkContinuationImpl} as enqueued.
9887d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar     */
9987d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    public void markEnqueued() {
10087d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar        mEnqueued = true;
10187d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
10244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
10359cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    public List<WorkContinuationImpl> getParents() {
10459cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        return mParents;
10587d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar    }
10644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
10759cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    WorkContinuationImpl(
10859cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar            @NonNull WorkManagerImpl workManagerImpl,
1097031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar            @NonNull List<? extends WorkRequest> work) {
110d7c332544c4dc6e06b4a876fd10c25b0469e81bcSumir Kataria        this(
111d7c332544c4dc6e06b4a876fd10c25b0469e81bcSumir Kataria                workManagerImpl,
112d7c332544c4dc6e06b4a876fd10c25b0469e81bcSumir Kataria                null,
113b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria                ExistingWorkPolicy.KEEP,
114d7c332544c4dc6e06b4a876fd10c25b0469e81bcSumir Kataria                work,
115d7c332544c4dc6e06b4a876fd10c25b0469e81bcSumir Kataria                null);
11604f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria    }
11704f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria
1181bffc919a167a732f24cf4216664374087628e15Sumir Kataria    WorkContinuationImpl(
1191bffc919a167a732f24cf4216664374087628e15Sumir Kataria            @NonNull WorkManagerImpl workManagerImpl,
1203665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria            String name,
121b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            ExistingWorkPolicy existingWorkPolicy,
1227031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar            @NonNull List<? extends WorkRequest> work) {
1233665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria        this(workManagerImpl, name, existingWorkPolicy, work, null);
12444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    }
12544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
12659cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
1273665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria            String name,
128b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria            ExistingWorkPolicy existingWorkPolicy,
1297031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar            @NonNull List<? extends WorkRequest> work,
13059cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar            @Nullable List<WorkContinuationImpl> parents) {
13144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        mWorkManagerImpl = workManagerImpl;
1323665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria        mName = name;
13304f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria        mExistingWorkPolicy = existingWorkPolicy;
13444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        mWork = work;
13559cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        mParents = parents;
13651b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria        mIds = new ArrayList<>(mWork.size());
13759cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        mAllIds = new ArrayList<>();
13859cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        if (parents != null) {
13959cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar            for (WorkContinuationImpl parent : parents) {
14059cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar                mAllIds.addAll(parent.mAllIds);
14159cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar            }
14244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        }
14351b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria        for (int i = 0; i < work.size(); i++) {
144fa284c943bd003ff03f1934370d70bd4a5e034c3Sumir Kataria            String id = work.get(i).getStringId();
14551b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria            mIds.add(id);
14651b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria            mAllIds.add(id);
14744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        }
14844235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    }
14944235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
15044235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    @Override
1517031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar    public WorkContinuation then(List<OneTimeWorkRequest> work) {
15244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        // TODO (rahulrav@) We need to decide if we want to allow chaining of continuations after
15344235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        // an initial call to enqueue()
154b776ef68aac1def7eeda4a1f14b7364009302982Sumir Kataria        return new WorkContinuationImpl(mWorkManagerImpl,
1553665b829af9706f7f85c53e4ef5a0825228f4d7aSumir Kataria                mName,
156b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria                ExistingWorkPolicy.KEEP,
15704f39f8ef8b7a77858763da5154a07ed423519bdSumir Kataria                work,
15859cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar                Collections.singletonList(this));
15944235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    }
16044235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
16144235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    @Override
1629cf4f3d3aa83bc64db574955805c67ac9ea84008Sumir Kataria    public LiveData<List<WorkStatus>> getStatuses() {
16319ae799361a8911480519c1c2b2e0e042e3a6239Sumir Kataria        return mWorkManagerImpl.getStatusesById(mAllIds);
16444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    }
16544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar
16644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    @Override
1675a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar    public List<WorkStatus> getStatusesSync() {
1685a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
1695a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar            throw new IllegalStateException("Cannot getStatusesSync on main thread!");
1705a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar        }
1715a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar        return mWorkManagerImpl.getStatusesByIdSync(mAllIds);
1725a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar    }
1735a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar
1745a4ced8020d8a3ee4b9100b97559db3d1fb5132bRahul Ravikumar    @Override
17544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    public void enqueue() {
17644235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        // Only enqueue if not already enqueued.
17744235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        if (!mEnqueued) {
178f56b9ee9361d113ec89eb4a44a153938b987d734Jan Clarin            // The runnable walks the hierarchy of the continuations
17987d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar            // and marks them enqueued using the markEnqueued() method, parent first.
18087d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar            mWorkManagerImpl.getTaskExecutor()
18187d37e5587b277ba523d69df71e17a4039274cdaRahul Ravikumar                    .executeOnBackgroundThread(new EnqueueRunnable(this));
18244235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        } else {
183697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar            Log.w(TAG, String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
18444235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar        }
18544235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar    }
18659cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar
18759cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    @Override
188d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria    @WorkerThread
18962e0be94ea7e54ca9227564b14c1c6736ae770f7Sumir Kataria    public void enqueueSync() {
190d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
19162e0be94ea7e54ca9227564b14c1c6736ae770f7Sumir Kataria            throw new IllegalStateException("Cannot enqueueSync on main thread!");
192d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria        }
193d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria
194d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria        if (!mEnqueued) {
195d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria            // The runnable walks the hierarchy of the continuations
196d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria            // and marks them enqueued using the markEnqueued() method, parent first.
197d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria            new EnqueueRunnable(this).run();
198d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria        } else {
199697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar            Log.w(TAG, String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
200d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria        }
201d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria    }
202d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria
203d7470effc40d8e9f46e7e29d1ccbbf7e8ff706f8Sumir Kataria    @Override
20462e0be94ea7e54ca9227564b14c1c6736ae770f7Sumir Kataria    public SynchronousWorkContinuation synchronous() {
2054484814aab16eec8d004c9e560ed5e25e0272d4dSumir Kataria        return this;
2064484814aab16eec8d004c9e560ed5e25e0272d4dSumir Kataria    }
2074484814aab16eec8d004c9e560ed5e25e0272d4dSumir Kataria
2084484814aab16eec8d004c9e560ed5e25e0272d4dSumir Kataria    @Override
20955a030183e5b974f3ebf04ef5cd9ab83f3557dadSumir Kataria    protected WorkContinuation combineInternal(
2107031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar            @Nullable OneTimeWorkRequest work,
21151b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria            @NonNull List<WorkContinuation> continuations) {
212930c8fb091c540642a0002e89eebb35c9eb88205Rahul Ravikumar
213930c8fb091c540642a0002e89eebb35c9eb88205Rahul Ravikumar        if (work == null) {
21455a030183e5b974f3ebf04ef5cd9ab83f3557dadSumir Kataria            work = new OneTimeWorkRequest.Builder(CombineContinuationsWorker.class)
21562a7e773945d980084dfc5d00c724de2e27dc22dSumir Kataria                    .setInputMerger(ArrayCreatingInputMerger.class)
21687e8dc0458781cd41a5ee990be811790ac7f4e88Sumir Kataria                    .build();
217930c8fb091c540642a0002e89eebb35c9eb88205Rahul Ravikumar        }
218930c8fb091c540642a0002e89eebb35c9eb88205Rahul Ravikumar
21951b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria        List<WorkContinuationImpl> parents = new ArrayList<>(continuations.size());
22059cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        for (WorkContinuation continuation : continuations) {
22159cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar            parents.add((WorkContinuationImpl) continuation);
22259cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        }
22359cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar
22459cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar        return new WorkContinuationImpl(mWorkManagerImpl,
22559cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar                null,
226b6430f2f232996be5971e9a57819aa2377daf920Sumir Kataria                ExistingWorkPolicy.KEEP,
22751b5cd2bc8a7fb7979e2bcd731fc8fd0d195f860Sumir Kataria                Collections.singletonList(work),
22859cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar                parents);
22959cbb5756e54d32fb51e72fe1ed94934c93f7303Rahul Ravikumar    }
2302ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2312ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    /**
2322ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @return {@code true} If there are cycles in the {@link WorkContinuationImpl}.
2332ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2342ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @hide
2352ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     */
2362ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
2372ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    public boolean hasCycles() {
2382ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        return hasCycles(this, new HashSet<String>());
2392ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    }
2402ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2412ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    /**
2422ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @param continuation The {@link WorkContinuationImpl} instance.
2432ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @param visited      The {@link Set} of {@link androidx.work.impl.model.WorkSpec} ids
2442ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     *                     marked as visited.
2452ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @return {@code true} if the {@link WorkContinuationImpl} has a cycle.
2462ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @hide
2472ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     */
2482ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
2492ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    private static boolean hasCycles(
2502ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            @NonNull WorkContinuationImpl continuation,
2512ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            @NonNull Set<String> visited) {
2522ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2532ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // mark the ids of this workContinuation as visited
2542ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // before we check if the parents have cycles.
2552ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        visited.addAll(continuation.getIds());
2562ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2572ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        Set<String> prerequisiteIds = prerequisitesFor(continuation);
2582ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        for (String id : visited) {
2592ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            if (prerequisiteIds.contains(id)) {
2602ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                // This prerequisite has already been visited before.
2612ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                // There is a cycle.
2622ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                return true;
2632ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            }
2642ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        }
2652ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2662ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        List<WorkContinuationImpl> parents = continuation.getParents();
2672ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        if (parents != null && !parents.isEmpty()) {
2682ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            for (WorkContinuationImpl parent : parents) {
2692ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                // if any of the parent has a cycle, then bail out
2702ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                if (hasCycles(parent, visited)) {
2712ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                    return true;
2722ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                }
2732ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            }
2742ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        }
2752ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2762ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // Un-mark the ids of the workContinuation as visited for the next parent.
2772ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // This is because we don't want to change the state of visited ids for subsequent parents
2782ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // This is being done to avoid allocations. Ideally we would check for a
2792ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        // hasCycles(parent, new HashSet<>(visited)) instead.
2802ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        visited.removeAll(continuation.getIds());
2812ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        return false;
2822ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    }
2832ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar
2842ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    /**
2852ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @return the {@link Set} of pre-requisites for a given {@link WorkContinuationImpl}.
2862ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     *
2872ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     * @hide
2882ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar     */
2892ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
2902ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    public static Set<String> prerequisitesFor(WorkContinuationImpl continuation) {
2912ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        Set<String> preRequisites = new HashSet<>();
2922ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        List<WorkContinuationImpl> parents = continuation.getParents();
2932ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        if (parents != null && !parents.isEmpty()) {
2942ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            for (WorkContinuationImpl parent : parents) {
2952ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar                preRequisites.addAll(parent.getIds());
2962ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar            }
2972ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        }
2982ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar        return preRequisites;
2992ca6b2dec030049de439acaa719e321252af4413Rahul Ravikumar    }
30044235b3ed93a4783f6bb1f3979779ee039289bfcRahul Ravikumar}
301