• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 android.car.app;
18 
19 import static android.car.app.CarTaskViewController.DBG;
20 
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.util.Slog;
24 
25 /** A wrapper class for {@link Runnable} which retries in an exponential backoff manner. */
26 final class RunnerWithBackoff {
27     private static final String TAG = RunnerWithBackoff.class.getSimpleName();
28     private static final int MAXIMUM_ATTEMPTS = 5;
29     private static final int FIRST_BACKOFF_TIME_MS = 1_000; // 1 second
30     private static final int MAXIMUM_BACKOFF_TIME_MS = 8_000; // 8 seconds
31     private final Handler mHandler = new Handler(Looper.getMainLooper());
32     private final Runnable mAction;
33 
34     private int mBackoffTimeMs;
35     private int mAttempts;
36 
RunnerWithBackoff(Runnable action)37     RunnerWithBackoff(Runnable action) {
38         mAction = action;
39     }
40 
41     private final Runnable mRetryRunnable = new Runnable() {
42         @Override
43         public void run() {
44             if (mAttempts >= MAXIMUM_ATTEMPTS) {
45                 Slog.e(TAG, "Failed to perform action, even after " + mAttempts + " attempts");
46                 return;
47             }
48             if (DBG) {
49                 Slog.d(TAG, "Executing the action. Attempt number " + mAttempts);
50             }
51             mAction.run();
52 
53             mHandler.postDelayed(mRetryRunnable, mBackoffTimeMs);
54             increaseBackoff();
55             mAttempts++;
56         }
57     };
58 
increaseBackoff()59     private void increaseBackoff() {
60         mBackoffTimeMs *= 2;
61         if (mBackoffTimeMs > MAXIMUM_BACKOFF_TIME_MS) {
62             mBackoffTimeMs = MAXIMUM_BACKOFF_TIME_MS;
63         }
64     }
65 
66     /** Starts the retrying. The first try happens synchronously. */
start()67     public void start() {
68         if (DBG) {
69             Slog.d(TAG, "start backoff runner");
70         }
71         // Stop the existing retrying as a safeguard to prevent multiple starts.
72         stopInternal();
73 
74         mBackoffTimeMs = FIRST_BACKOFF_TIME_MS;
75         mAttempts = 0;
76         // Call .run() instead of posting to handler so that first try can happen synchronously.
77         mRetryRunnable.run();
78     }
79 
80     /** Stops the retrying. */
stop()81     public void stop() {
82         if (DBG) {
83             Slog.d(TAG, "stop backoff runner");
84         }
85         stopInternal();
86     }
87 
stopInternal()88     private void stopInternal() {
89         mHandler.removeCallbacks(mRetryRunnable);
90     }
91 }
92