content: /*
   * Copyright (C) 2017 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.internal.telephony;
  
  import android.annotation.NonNull;
  import android.os.Handler;
  import android.os.Looper;
  
  import com.android.internal.annotations.VisibleForTesting;
  
  /** The implementation of exponential backoff with jitter applied. */
  public class ExponentialBackoff {
      private int mRetryCounter;
      private long mStartDelayMs;
      private long mMaximumDelayMs;
      private long mCurrentDelayMs;
      private int mMultiplier;
      private final Runnable mRunnable;
      private final Handler mHandler;
  
      /**
       * Implementation of Handler methods, Adapter for testing (can't spy on final methods).
       */
      private HandlerAdapter mHandlerAdapter = new HandlerAdapter() {
          @Override
          public boolean postDelayed(Runnable runnable, long delayMillis) {
              return mHandler.postDelayed(runnable, delayMillis);
          }
  
          @Override
          public void removeCallbacks(Runnable runnable) {
              mHandler.removeCallbacks(runnable);
          }
      };
  
      /**
       * Need to spy final methods for testing.
       */
      public interface HandlerAdapter {
          boolean postDelayed(Runnable runnable, long delayMillis);
          void removeCallbacks(Runnable runnable);
      }
  
      public ExponentialBackoff(
              long initialDelayMs,
              long maximumDelayMs,
              int multiplier,
              @NonNull Looper looper,
              @NonNull Runnable runnable) {
          this(initialDelayMs, maximumDelayMs, multiplier, new Handler(looper), runnable);
      }
  
      public ExponentialBackoff(
              long initialDelayMs,
              long maximumDelayMs,
              int multiplier,
              @NonNull Handler handler,
              @NonNull Runnable runnable) {
          mRetryCounter = 0;
          mStartDelayMs = initialDelayMs;
          mMaximumDelayMs = maximumDelayMs;
          mMultiplier = multiplier;
          mHandler = handler;
          mRunnable = runnable;
      }
  
      /** Starts the backoff, the runnable will be executed after {@link #mStartDelayMs}. */
      public void start() {
          mRetryCounter = 0;
          mCurrentDelayMs = mStartDelayMs;
          mHandlerAdapter.removeCallbacks(mRunnable);
          mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
      }
  
      /** Stops the backoff, all pending messages will be removed from the message queue. */
      public void stop() {
          mRetryCounter = 0;
          mHandlerAdapter.removeCallbacks(mRunnable);
      }
  
      /** Should call when the retry action has failed and we want to retry after a longer delay. */
      public void notifyFailed() {
          mRetryCounter++;
          long temp = Math.min(
                  mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
          mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
          mHandlerAdapter.removeCallbacks(mRunnable);
          mHandlerAdapter.postDelayed(mRunnable, mCurrentDelayMs);
      }
  
      /** Returns the delay for the most recently posted message. */
      public long getCurrentDelay() {
          return mCurrentDelayMs;
      }
  
      @VisibleForTesting
      public void setHandlerAdapter(HandlerAdapter a) {
          mHandlerAdapter  = a;
      }
  }
  