Home | History | Annotate | Download | only in systemalarm
      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 
     49     WorkTimer() {
     50         mTimerMap = new HashMap<>();
     51         mListeners = new HashMap<>();
     52         mLock = new Object();
     53         mExecutorService = Executors.newSingleThreadScheduledExecutor();
     54     }
     55 
     56     @SuppressWarnings("FutureReturnValueIgnored")
     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 
     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
     83     synchronized Map<String, WorkTimerRunnable> getTimerMap() {
     84         return mTimerMap;
     85     }
     86 
     87     @VisibleForTesting
     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 
    101         WorkTimerRunnable(@NonNull WorkTimer workTimer, @NonNull String workSpecId) {
    102             mWorkTimer = workTimer;
    103             mWorkSpecId = workSpecId;
    104         }
    105 
    106         @Override
    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 {
    125         void onTimeLimitExceeded(@NonNull String workSpecId);
    126     }
    127 }
    128