Wait for TIS cleanup after unbind before binding again

Fixes: 375315062
Test: Switch users 10 times, no crash (also check logs)
Flag: EXEMPT bug fix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:21172dbf21a8fc2754a4e6fb7a951e1fc32a58a4)
Merged-In: I7b3ac7114d519be9f46d0c38758bdcc7239d033a
Change-Id: I7b3ac7114d519be9f46d0c38758bdcc7239d033a
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 83ca496..2b71c87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -19,10 +19,11 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
+import android.os.IRemoteCallback;
 import android.view.MotionEvent;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
-// Next ID: 34
+// Next ID: 36
 oneway interface IOverviewProxy {
 
     void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -137,4 +138,10 @@
      * Sent when {@link TaskbarDelegate#appTransitionPending} is called.
      */
     void appTransitionPending(boolean pending) = 34;
+
+    /**
+     * Sent right after OverviewProxy calls unbindService() on the TouchInteractionService.
+     * TouchInteractionService is expected to send the reply once it has finished cleaning up.
+     */
+    void onUnbind(IRemoteCallback reply) = 35;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e3cf411..adf9eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -58,6 +58,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.PatternMatcher;
 import android.os.Process;
@@ -146,7 +147,6 @@
     public static final String TAG_OPS = "OverviewProxyService";
     private static final long BACKOFF_MILLIS = 1000;
     private static final long DEFERRED_CALLBACK_MILLIS = 5000;
-
     // Max backoff caps at 5 mins
     private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
 
@@ -183,6 +183,10 @@
     private int mConnectionBackoffAttempts;
     private boolean mBound;
     private boolean mIsEnabled;
+    // This is set to false when the overview service is requested to be bound until it is notified
+    // that the previous service has been cleaned up in IOverviewProxy#onUnbind(). It is also set to
+    // true after a 1000ms timeout by mDeferredBindAfterTimedOutCleanup.
+    private boolean mIsPrevServiceCleanedUp = true;
 
     private boolean mIsSystemOrVisibleBgUser;
     private int mCurrentBoundedUserId = -1;
@@ -489,6 +493,12 @@
         retryConnectionWithBackoff();
     };
 
+    private final Runnable mDeferredBindAfterTimedOutCleanup = () -> {
+        Log.w(TAG_OPS, "Timed out waiting for previous service to clean up, binding to new one");
+        mIsPrevServiceCleanedUp = true;
+        maybeBindService();
+    };
+
     private final BroadcastReceiver mUserEventReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -859,6 +869,7 @@
                 mShadeViewControllerLazy.get().cancelInputFocusTransfer();
             });
         }
+        mIsPrevServiceCleanedUp = true;
         startConnectionToCurrentUser();
     }
 
@@ -889,6 +900,19 @@
         }
         mHandler.removeCallbacks(mConnectionRunnable);
 
+        maybeBindService();
+    }
+
+    private void maybeBindService() {
+        if (!mIsPrevServiceCleanedUp) {
+            Log.w(TAG_OPS, "Skipping connection to TouchInteractionService until previous"
+                    + " instance is cleaned up.");
+            if (!mHandler.hasCallbacks(mDeferredConnectionCallback)) {
+                mHandler.postDelayed(mDeferredBindAfterTimedOutCleanup, BACKOFF_MILLIS);
+            }
+            return;
+        }
+
         // Avoid creating TouchInteractionService because the System user in HSUM mode does not
         // interact with UI elements
         UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
@@ -907,6 +931,7 @@
             Log.e(TAG_OPS, "Unable to bind because of security error", e);
         }
         if (mBound) {
+            mIsPrevServiceCleanedUp = false;
             // Ensure that connection has been established even if it thinks it is bound
             mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
         } else {
@@ -960,6 +985,24 @@
             // Always unbind the service (ie. if called through onNullBinding or onBindingDied)
             mContext.unbindService(mOverviewServiceConnection);
             mBound = false;
+            if (mOverviewProxy != null) {
+                try {
+                    mOverviewProxy.onUnbind(new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) throws RemoteException {
+                            // Received Launcher reply, try to bind anew.
+                            mIsPrevServiceCleanedUp = true;
+                            if (mHandler.hasCallbacks(mDeferredBindAfterTimedOutCleanup)) {
+                                mHandler.removeCallbacks(mDeferredBindAfterTimedOutCleanup);
+                                maybeBindService();
+                            }
+                        }
+                    });
+                } catch (RemoteException e) {
+                    Log.w(TAG_OPS, "disconnectFromLauncherService failed to notify Launcher");
+                    mIsPrevServiceCleanedUp = true;
+                }
+            }
         }
 
         if (mOverviewProxy != null) {
@@ -1189,6 +1232,7 @@
         pw.print("  mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
         pw.print("  mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
         pw.print("  mNavBarMode="); pw.println(mNavBarMode);
+        pw.print("  mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
         mSysUiState.dump(pw, args);
     }