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