WorkTimer.java revision dd3c04c8aa64eb9c8d3da1bbac6024e613d39143
1accafea75309628ec2456574bcaff8c97411187bJan Clarin/*
2accafea75309628ec2456574bcaff8c97411187bJan Clarin * Copyright 2018 The Android Open Source Project
3accafea75309628ec2456574bcaff8c97411187bJan Clarin *
4accafea75309628ec2456574bcaff8c97411187bJan Clarin * Licensed under the Apache License, Version 2.0 (the "License");
5accafea75309628ec2456574bcaff8c97411187bJan Clarin * you may not use this file except in compliance with the License.
6accafea75309628ec2456574bcaff8c97411187bJan Clarin * You may obtain a copy of the License at
7accafea75309628ec2456574bcaff8c97411187bJan Clarin *
8accafea75309628ec2456574bcaff8c97411187bJan Clarin *      http://www.apache.org/licenses/LICENSE-2.0
9accafea75309628ec2456574bcaff8c97411187bJan Clarin *
10accafea75309628ec2456574bcaff8c97411187bJan Clarin * Unless required by applicable law or agreed to in writing, software
11accafea75309628ec2456574bcaff8c97411187bJan Clarin * distributed under the License is distributed on an "AS IS" BASIS,
12accafea75309628ec2456574bcaff8c97411187bJan Clarin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13accafea75309628ec2456574bcaff8c97411187bJan Clarin * See the License for the specific language governing permissions and
14accafea75309628ec2456574bcaff8c97411187bJan Clarin * limitations under the License.
15accafea75309628ec2456574bcaff8c97411187bJan Clarin */
16accafea75309628ec2456574bcaff8c97411187bJan Clarin
17accafea75309628ec2456574bcaff8c97411187bJan Clarinpackage android.arch.background.workmanager.impl.background.systemalarm;
18accafea75309628ec2456574bcaff8c97411187bJan Clarin
19accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.arch.background.workmanager.BaseWork;
20accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.arch.background.workmanager.impl.logger.Logger;
21accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.support.annotation.NonNull;
22accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.support.annotation.RestrictTo;
23dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarimport android.support.annotation.VisibleForTesting;
24accafea75309628ec2456574bcaff8c97411187bJan Clarin
25dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarimport java.util.HashMap;
26accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.Map;
27accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.Executors;
28accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.ScheduledExecutorService;
29accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.TimeUnit;
30accafea75309628ec2456574bcaff8c97411187bJan Clarin
31accafea75309628ec2456574bcaff8c97411187bJan Clarin/**
32dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * Manages timers to enforce a time limit for processing {@link BaseWork}.
33dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * Notifies a {@link TimeLimitExceededListener} when the time limit
34dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * is exceeded.
35accafea75309628ec2456574bcaff8c97411187bJan Clarin *
36accafea75309628ec2456574bcaff8c97411187bJan Clarin * @hide
37accafea75309628ec2456574bcaff8c97411187bJan Clarin */
38accafea75309628ec2456574bcaff8c97411187bJan Clarin@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
39dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarclass WorkTimer {
40dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
41accafea75309628ec2456574bcaff8c97411187bJan Clarin    private static final String TAG = "WorkTimer";
42accafea75309628ec2456574bcaff8c97411187bJan Clarin
43dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    private final ScheduledExecutorService mExecutorService;
44dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    private final Map<String, WorkTimerRunnable> mTimerMap;
45dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    private final Map<String, TimeLimitExceededListener> mListeners;
46dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    private final Object mLock;
47dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
48dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    WorkTimer() {
49dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        mTimerMap = new HashMap<>();
50dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        mListeners = new HashMap<>();
51dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        mLock = new Object();
52dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        mExecutorService = Executors.newSingleThreadScheduledExecutor();
53dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    }
54accafea75309628ec2456574bcaff8c97411187bJan Clarin
55dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    @SuppressWarnings("FutureReturnValueIgnored")
56dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    void startTimer(@NonNull final String workSpecId,
57dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            long processingTimeMillis,
58dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            @NonNull TimeLimitExceededListener listener) {
59accafea75309628ec2456574bcaff8c97411187bJan Clarin
60dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        synchronized (mLock) {
61dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            Logger.debug(TAG, "Starting timer for %s", workSpecId);
62dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            // clear existing timer's first
63dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            stopTimer(workSpecId);
64dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            WorkTimerRunnable runnable = new WorkTimerRunnable(this, workSpecId);
65dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            mTimerMap.put(workSpecId, runnable);
66dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            mListeners.put(workSpecId, listener);
67dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            mExecutorService.schedule(runnable, processingTimeMillis, TimeUnit.MILLISECONDS);
68dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        }
69accafea75309628ec2456574bcaff8c97411187bJan Clarin    }
70accafea75309628ec2456574bcaff8c97411187bJan Clarin
71dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    void stopTimer(@NonNull final String workSpecId) {
72dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        synchronized (mLock) {
73dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            if (mTimerMap.containsKey(workSpecId)) {
74dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                Logger.debug(TAG, "Stopping timer for %s", workSpecId);
75dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                mTimerMap.remove(workSpecId);
76dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                mListeners.remove(workSpecId);
77accafea75309628ec2456574bcaff8c97411187bJan Clarin            }
78dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        }
79dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    }
80accafea75309628ec2456574bcaff8c97411187bJan Clarin
81dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    @VisibleForTesting
82dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    synchronized Map<String, WorkTimerRunnable> getTimerMap() {
83dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        return mTimerMap;
84accafea75309628ec2456574bcaff8c97411187bJan Clarin    }
85accafea75309628ec2456574bcaff8c97411187bJan Clarin
86dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    @VisibleForTesting
87dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    synchronized Map<String, TimeLimitExceededListener> getListeners() {
88dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        return mListeners;
89dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    }
90dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
91dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    /**
92dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar     * The actual runnable scheduled on the scheduled executor.
93dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar     */
94dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar    static class WorkTimerRunnable implements Runnable {
95dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        static final String TAG = "WrkTimerRunnable";
96dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
97dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        private final WorkTimer mWorkTimer;
98dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        private final String mWorkSpecId;
99dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
100dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        WorkTimerRunnable(@NonNull WorkTimer workTimer, @NonNull String workSpecId) {
101dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            mWorkTimer = workTimer;
102dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            mWorkSpecId = workSpecId;
103dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        }
104dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar
105dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        @Override
106dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar        public void run() {
107dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            synchronized (mWorkTimer.mLock) {
108dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                if (mWorkTimer.mTimerMap.containsKey(mWorkSpecId)) {
109dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    mWorkTimer.mTimerMap.remove(mWorkSpecId);
110dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    // notify time limit exceeded.
111dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    TimeLimitExceededListener listener = mWorkTimer.mListeners.remove(mWorkSpecId);
112dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    if (listener != null) {
113dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                        listener.onTimeLimitExceeded(mWorkSpecId);
114dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    }
115dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                } else {
116dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                    Logger.debug(TAG, "Timer with %s is already marked as complete.", mWorkSpecId);
117dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar                }
118dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar            }
119accafea75309628ec2456574bcaff8c97411187bJan Clarin        }
120accafea75309628ec2456574bcaff8c97411187bJan Clarin    }
121accafea75309628ec2456574bcaff8c97411187bJan Clarin
122accafea75309628ec2456574bcaff8c97411187bJan Clarin    interface TimeLimitExceededListener {
123accafea75309628ec2456574bcaff8c97411187bJan Clarin        void onTimeLimitExceeded(@NonNull String workSpecId);
124accafea75309628ec2456574bcaff8c97411187bJan Clarin    }
125accafea75309628ec2456574bcaff8c97411187bJan Clarin}
126