Merge "Re-try binding to TouchInteractionService if service is killed" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 475f061..ecd38b4 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -37,7 +37,9 @@
 import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.Handler;
 import android.os.IBinder;
+import android.util.Log;
 import android.view.View;
 import android.window.SplashScreen;
 
@@ -89,6 +91,11 @@
 public abstract class BaseQuickstepLauncher extends Launcher
         implements NavigationModeChangeListener {
 
+    private static final long BACKOFF_MILLIS = 1000;
+
+    // Max backoff caps at 5 mins
+    private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
+
     private DepthController mDepthController = new DepthController(this);
     private QuickstepTransitionManager mAppTransitionManager;
 
@@ -108,12 +115,24 @@
         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
             mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
             mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
+            Log.d(TAG, "TIS service connected");
+            resetServiceBindRetryState();
         }
 
         @Override
         public void onServiceDisconnected(ComponentName componentName) { }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            Log.w(TAG, "TIS binding died");
+            internalBindToTIS();
+        }
     };
+
+    private final Runnable mConnectionRunnable = this::internalBindToTIS;
+    private short mConnectionAttempts;
     private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
+    private final Handler mHandler = new Handler();
 
     // Will be updated when dragging from taskbar.
     private @Nullable DragOptions mNextWorkspaceDragOptions = null;
@@ -132,11 +151,11 @@
 
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
 
-
         unbindService(mTisBinderConnection);
         if (mTaskbarManager != null) {
             mTaskbarManager.clearLauncher(this);
         }
+        resetServiceBindRetryState();
         super.onDestroy();
     }
 
@@ -264,8 +283,33 @@
         mAppTransitionManager = new QuickstepTransitionManager(this);
         mAppTransitionManager.registerRemoteAnimations();
 
-        bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
+        internalBindToTIS();
+    }
 
+    /**
+     * Binds {@link #mTisBinderConnection} to {@link TouchInteractionService}. If the binding fails,
+     * attempts to retry via {@link #mConnectionRunnable}
+     */
+    private void internalBindToTIS() {
+        boolean bound = bindService(new Intent(this, TouchInteractionService.class),
+                        mTisBinderConnection, 0);
+        if (bound) {
+            resetServiceBindRetryState();
+            return;
+        }
+
+        Log.w(TAG, "Retrying TIS Binder connection attempt: " + mConnectionAttempts);
+        final long timeoutMs = (long) Math.min(
+                Math.scalb(BACKOFF_MILLIS, mConnectionAttempts), MAX_BACKOFF_MILLIS);
+        mHandler.postDelayed(mConnectionRunnable, timeoutMs);
+        mConnectionAttempts++;
+    }
+
+    private void resetServiceBindRetryState() {
+        if (mHandler.hasCallbacks(mConnectionRunnable)) {
+            mHandler.removeCallbacks(mConnectionRunnable);
+        }
+        mConnectionAttempts = 0;
     }
 
     public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {