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 17564e43098c323d1a90be53c190b8fdbdde973505Sumir Katariapackage androidx.work.impl.background.systemalarm; 18accafea75309628ec2456574bcaff8c97411187bJan Clarin 19accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.support.annotation.NonNull; 20accafea75309628ec2456574bcaff8c97411187bJan Clarinimport android.support.annotation.RestrictTo; 21dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarimport android.support.annotation.VisibleForTesting; 22697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumarimport android.util.Log; 23accafea75309628ec2456574bcaff8c97411187bJan Clarin 247031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumarimport androidx.work.WorkRequest; 25b5728f4e1a4b3f4f1fabf033b1363ca6b1cffdefSumir Kataria 26dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarimport java.util.HashMap; 27accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.Map; 28accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.Executors; 29accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.ScheduledExecutorService; 30accafea75309628ec2456574bcaff8c97411187bJan Clarinimport java.util.concurrent.TimeUnit; 31accafea75309628ec2456574bcaff8c97411187bJan Clarin 32accafea75309628ec2456574bcaff8c97411187bJan Clarin/** 337031a0fbe12b8159ab2dc6d9c50be5b3f38477faRahul Ravikumar * Manages timers to enforce a time limit for processing {@link WorkRequest}. 34dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * Notifies a {@link TimeLimitExceededListener} when the time limit 35dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * is exceeded. 36accafea75309628ec2456574bcaff8c97411187bJan Clarin * 37accafea75309628ec2456574bcaff8c97411187bJan Clarin * @hide 38accafea75309628ec2456574bcaff8c97411187bJan Clarin */ 39accafea75309628ec2456574bcaff8c97411187bJan Clarin@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 40dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumarclass WorkTimer { 41dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 42accafea75309628ec2456574bcaff8c97411187bJan Clarin private static final String TAG = "WorkTimer"; 43accafea75309628ec2456574bcaff8c97411187bJan Clarin 44dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final ScheduledExecutorService mExecutorService; 45dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final Map<String, WorkTimerRunnable> mTimerMap; 46dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final Map<String, TimeLimitExceededListener> mListeners; 47dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final Object mLock; 48dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 49dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar WorkTimer() { 50dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mTimerMap = new HashMap<>(); 51dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mListeners = new HashMap<>(); 52dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mLock = new Object(); 53dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mExecutorService = Executors.newSingleThreadScheduledExecutor(); 54dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 55accafea75309628ec2456574bcaff8c97411187bJan Clarin 56dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar @SuppressWarnings("FutureReturnValueIgnored") 57dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar void startTimer(@NonNull final String workSpecId, 58dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar long processingTimeMillis, 59dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar @NonNull TimeLimitExceededListener listener) { 60accafea75309628ec2456574bcaff8c97411187bJan Clarin 61dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar synchronized (mLock) { 62697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar Log.d(TAG, String.format("Starting timer for %s", workSpecId)); 63dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar // clear existing timer's first 64dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar stopTimer(workSpecId); 65dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar WorkTimerRunnable runnable = new WorkTimerRunnable(this, workSpecId); 66dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mTimerMap.put(workSpecId, runnable); 67dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mListeners.put(workSpecId, listener); 68dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mExecutorService.schedule(runnable, processingTimeMillis, TimeUnit.MILLISECONDS); 69dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 70accafea75309628ec2456574bcaff8c97411187bJan Clarin } 71accafea75309628ec2456574bcaff8c97411187bJan Clarin 72dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar void stopTimer(@NonNull final String workSpecId) { 73dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar synchronized (mLock) { 74dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar if (mTimerMap.containsKey(workSpecId)) { 75697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar Log.d(TAG, String.format("Stopping timer for %s", workSpecId)); 76dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mTimerMap.remove(workSpecId); 77dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mListeners.remove(workSpecId); 78accafea75309628ec2456574bcaff8c97411187bJan Clarin } 79dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 80dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 81accafea75309628ec2456574bcaff8c97411187bJan Clarin 82dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar @VisibleForTesting 83dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar synchronized Map<String, WorkTimerRunnable> getTimerMap() { 84dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar return mTimerMap; 85accafea75309628ec2456574bcaff8c97411187bJan Clarin } 86accafea75309628ec2456574bcaff8c97411187bJan Clarin 87dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar @VisibleForTesting 88dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar synchronized Map<String, TimeLimitExceededListener> getListeners() { 89dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar return mListeners; 90dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 91dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 92dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar /** 93dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar * The actual runnable scheduled on the scheduled executor. 94dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar */ 95dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar static class WorkTimerRunnable implements Runnable { 96dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar static final String TAG = "WrkTimerRunnable"; 97dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 98dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final WorkTimer mWorkTimer; 99dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar private final String mWorkSpecId; 100dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 101dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar WorkTimerRunnable(@NonNull WorkTimer workTimer, @NonNull String workSpecId) { 102dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mWorkTimer = workTimer; 103dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mWorkSpecId = workSpecId; 104dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 105dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar 106dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar @Override 107dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar public void run() { 108dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar synchronized (mWorkTimer.mLock) { 109dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar if (mWorkTimer.mTimerMap.containsKey(mWorkSpecId)) { 110dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar mWorkTimer.mTimerMap.remove(mWorkSpecId); 111dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar // notify time limit exceeded. 112dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar TimeLimitExceededListener listener = mWorkTimer.mListeners.remove(mWorkSpecId); 113dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar if (listener != null) { 114dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar listener.onTimeLimitExceeded(mWorkSpecId); 115dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 116dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } else { 117697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar Log.d(TAG, String.format( 118697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar "Timer with %s is already marked as complete.", mWorkSpecId)); 119dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 120dd3c04c8aa64eb9c8d3da1bbac6024e613d39143Rahul Ravikumar } 121accafea75309628ec2456574bcaff8c97411187bJan Clarin } 122accafea75309628ec2456574bcaff8c97411187bJan Clarin } 123accafea75309628ec2456574bcaff8c97411187bJan Clarin 124accafea75309628ec2456574bcaff8c97411187bJan Clarin interface TimeLimitExceededListener { 125accafea75309628ec2456574bcaff8c97411187bJan Clarin void onTimeLimitExceeded(@NonNull String workSpecId); 126accafea75309628ec2456574bcaff8c97411187bJan Clarin } 127accafea75309628ec2456574bcaff8c97411187bJan Clarin} 128