1 /* 2 * Copyright (C) 2022 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.systemui.util.service; 18 19 import static com.android.systemui.util.service.dagger.ObservableServiceModule.BASE_RECONNECT_DELAY_MS; 20 import static com.android.systemui.util.service.dagger.ObservableServiceModule.MAX_RECONNECT_ATTEMPTS; 21 import static com.android.systemui.util.service.dagger.ObservableServiceModule.MIN_CONNECTION_DURATION_MS; 22 import static com.android.systemui.util.service.dagger.ObservableServiceModule.OBSERVER; 23 import static com.android.systemui.util.service.dagger.ObservableServiceModule.SERVICE_CONNECTION; 24 25 import android.util.Log; 26 27 import com.android.systemui.util.concurrency.DelayableExecutor; 28 import com.android.systemui.util.time.SystemClock; 29 30 import javax.inject.Inject; 31 import javax.inject.Named; 32 33 /** 34 * The {@link PersistentConnectionManager} is responsible for maintaining a connection to a 35 * {@link ObservableServiceConnection}. 36 * @param <T> The transformed connection type handled by the service. 37 */ 38 public class PersistentConnectionManager<T> { 39 private static final String TAG = "PersistentConnManager"; 40 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 41 42 private final SystemClock mSystemClock; 43 private final DelayableExecutor mMainExecutor; 44 private final int mBaseReconnectDelayMs; 45 private final int mMaxReconnectAttempts; 46 private final int mMinConnectionDuration; 47 private final Observer mObserver; 48 49 private int mReconnectAttempts = 0; 50 private Runnable mCurrentReconnectCancelable; 51 52 private final ObservableServiceConnection<T> mConnection; 53 54 private final Runnable mConnectRunnable = new Runnable() { 55 @Override 56 public void run() { 57 mCurrentReconnectCancelable = null; 58 mConnection.bind(); 59 } 60 }; 61 62 private final Observer.Callback mObserverCallback = () -> initiateConnectionAttempt(); 63 64 private final ObservableServiceConnection.Callback mConnectionCallback = 65 new ObservableServiceConnection.Callback() { 66 private long mStartTime; 67 68 @Override 69 public void onConnected(ObservableServiceConnection connection, Object proxy) { 70 mStartTime = mSystemClock.currentTimeMillis(); 71 } 72 73 @Override 74 public void onDisconnected(ObservableServiceConnection connection, int reason) { 75 // Do not attempt to reconnect if we were manually unbound 76 if (reason == ObservableServiceConnection.DISCONNECT_REASON_UNBIND) { 77 return; 78 } 79 80 if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) { 81 initiateConnectionAttempt(); 82 } else { 83 scheduleConnectionAttempt(); 84 } 85 } 86 }; 87 88 @Inject PersistentConnectionManager( SystemClock clock, DelayableExecutor mainExecutor, @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection, @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts, @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs, @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs, @Named(OBSERVER) Observer observer)89 public PersistentConnectionManager( 90 SystemClock clock, 91 DelayableExecutor mainExecutor, 92 @Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection, 93 @Named(MAX_RECONNECT_ATTEMPTS) int maxReconnectAttempts, 94 @Named(BASE_RECONNECT_DELAY_MS) int baseReconnectDelayMs, 95 @Named(MIN_CONNECTION_DURATION_MS) int minConnectionDurationMs, 96 @Named(OBSERVER) Observer observer) { 97 mSystemClock = clock; 98 mMainExecutor = mainExecutor; 99 mConnection = serviceConnection; 100 mObserver = observer; 101 102 mMaxReconnectAttempts = maxReconnectAttempts; 103 mBaseReconnectDelayMs = baseReconnectDelayMs; 104 mMinConnectionDuration = minConnectionDurationMs; 105 } 106 107 /** 108 * Begins the {@link PersistentConnectionManager} by connecting to the associated service. 109 */ start()110 public void start() { 111 mConnection.addCallback(mConnectionCallback); 112 mObserver.addCallback(mObserverCallback); 113 initiateConnectionAttempt(); 114 } 115 116 /** 117 * Brings down the {@link PersistentConnectionManager}, disconnecting from the service. 118 */ stop()119 public void stop() { 120 mConnection.removeCallback(mConnectionCallback); 121 mObserver.removeCallback(mObserverCallback); 122 mConnection.unbind(); 123 } 124 initiateConnectionAttempt()125 private void initiateConnectionAttempt() { 126 // Reset attempts 127 mReconnectAttempts = 0; 128 129 // The first attempt is always a direct invocation rather than delayed. 130 mConnection.bind(); 131 } 132 scheduleConnectionAttempt()133 private void scheduleConnectionAttempt() { 134 // always clear cancelable if present. 135 if (mCurrentReconnectCancelable != null) { 136 mCurrentReconnectCancelable.run(); 137 mCurrentReconnectCancelable = null; 138 } 139 140 if (mReconnectAttempts >= mMaxReconnectAttempts) { 141 if (DEBUG) { 142 Log.d(TAG, "exceeded max connection attempts."); 143 } 144 return; 145 } 146 147 final long reconnectDelayMs = 148 (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts); 149 150 if (DEBUG) { 151 Log.d(TAG, 152 "scheduling connection attempt in " + reconnectDelayMs + "milliseconds"); 153 } 154 155 mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable, 156 reconnectDelayMs); 157 158 mReconnectAttempts++; 159 } 160 } 161