1 /* 2 * Copyright (C) 2019 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 com.android.permission.util; 18 19 import android.annotation.NonNull; 20 import android.os.Handler; 21 import android.os.SystemClock; 22 23 import com.android.internal.annotations.GuardedBy; 24 25 /** 26 * A throttled runnable that can wrap around a runnable and throttle calls to its run(). 27 * 28 * The throttling logic makes sure that the original runnable will be called only after the 29 * specified interval passes since the last actual call. The first call in a while (after the 30 * specified interval passes since the last actual call) will always result in the original runnable 31 * being called immediately, and then subsequent calls will start to be throttled. It is guaranteed 32 * that any call to this throttled runnable will always result in the original runnable being called 33 * afterwards, within the specified interval. 34 */ 35 public class ThrottledRunnable implements Runnable { 36 37 @NonNull 38 private final Handler mHandler; 39 private final long mIntervalMillis; 40 @NonNull 41 private final Runnable mRunnable; 42 43 @NonNull 44 private final Object mLock = new Object(); 45 46 @GuardedBy("mLock") 47 private long mScheduledUptimeMillis; 48 ThrottledRunnable(@onNull Handler handler, long intervalMillis, @NonNull Runnable runnable)49 public ThrottledRunnable(@NonNull Handler handler, long intervalMillis, 50 @NonNull Runnable runnable) { 51 mHandler = handler; 52 mIntervalMillis = intervalMillis; 53 mRunnable = runnable; 54 } 55 56 @Override run()57 public void run() { 58 synchronized (mLock) { 59 if (mHandler.hasCallbacks(mRunnable)) { 60 // We have a scheduled runnable. 61 return; 62 } 63 long currentUptimeMillis = SystemClock.uptimeMillis(); 64 if (mScheduledUptimeMillis == 0 65 || currentUptimeMillis > mScheduledUptimeMillis + mIntervalMillis) { 66 // First time in a while, schedule immediately. 67 mScheduledUptimeMillis = currentUptimeMillis; 68 } else { 69 // We were scheduled not long ago, so schedule with delay for throttling. 70 mScheduledUptimeMillis = mScheduledUptimeMillis + mIntervalMillis; 71 } 72 mHandler.postAtTime(mRunnable, mScheduledUptimeMillis); 73 } 74 } 75 } 76