summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/quickstep/util/TISBindHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/quickstep/util/TISBindHelper.java')
-rw-r--r--quickstep/src/com/android/quickstep/util/TISBindHelper.java145
1 files changed, 145 insertions, 0 deletions
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
new file mode 100644
index 0000000000..7b122c6c66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 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.quickstep.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to simplify binding to {@link TouchInteractionService}
+ */
+public class TISBindHelper implements ServiceConnection {
+
+ private static final String TAG = "TISBindHelper";
+
+ private static final long BACKOFF_MILLIS = 1000;
+
+ // Max backoff caps at 5 mins
+ private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
+
+ private final Handler mHandler = new Handler();
+ private final Runnable mConnectionRunnable = this::internalBindToTIS;
+ private final Context mContext;
+ private final Consumer<TISBinder> mConnectionCallback;
+ private final ArrayList<Runnable> mPendingConnectedCallbacks = new ArrayList<>();
+
+ private short mConnectionAttempts;
+ private boolean mTisServiceBound;
+ private boolean mIsConnected;
+
+ public TISBindHelper(Context context, Consumer<TISBinder> connectionCallback) {
+ mContext = context;
+ mConnectionCallback = connectionCallback;
+ internalBindToTIS();
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ if (!(iBinder instanceof TISBinder)) {
+ // Seems like there can be a race condition when user unlocks, which kills the TIS
+ // process and re-starts it. I guess in the meantime service can be connected to
+ // a killed TIS? Either way, unbind and try to re-connect in that case.
+ internalUnbindToTIS();
+ mHandler.postDelayed(mConnectionRunnable, BACKOFF_MILLIS);
+ return;
+ }
+
+ Log.d(TAG, "TIS service connected");
+ mIsConnected = true;
+ mConnectionCallback.accept((TISBinder) iBinder);
+ // Flush the pending callbacks
+ for (Runnable r : mPendingConnectedCallbacks) {
+ r.run();
+ }
+ mPendingConnectedCallbacks.clear();
+ resetServiceBindRetryState();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) { }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ Log.w(TAG, "TIS binding died");
+ internalBindToTIS();
+ }
+
+ /**
+ * Runs the given {@param r} runnable when the service is connected.
+ */
+ public void runOnBindToTouchInteractionService(Runnable r) {
+ if (mIsConnected) {
+ r.run();
+ } else {
+ mPendingConnectedCallbacks.add(r);
+ }
+ }
+
+ /**
+ * Binds to {@link TouchInteractionService}. If the binding fails, attempts to retry via
+ * {@link #mConnectionRunnable}. Unbind via {@link #internalUnbindToTIS()}
+ */
+ private void internalBindToTIS() {
+ mTisServiceBound = mContext.bindService(new Intent(mContext, TouchInteractionService.class),
+ this, 0);
+ if (mTisServiceBound) {
+ 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++;
+ }
+
+ /** See {@link #internalBindToTIS()} */
+ private void internalUnbindToTIS() {
+ if (mTisServiceBound) {
+ mContext.unbindService(this);
+ mTisServiceBound = false;
+ }
+ }
+
+ private void resetServiceBindRetryState() {
+ if (mHandler.hasCallbacks(mConnectionRunnable)) {
+ mHandler.removeCallbacks(mConnectionRunnable);
+ }
+ mConnectionAttempts = 0;
+ }
+
+ /**
+ * Called when the activity is destroyed to clear the binding
+ */
+ public void onDestroy() {
+ internalUnbindToTIS();
+ resetServiceBindRetryState();
+ mIsConnected = false;
+ mPendingConnectedCallbacks.clear();
+ }
+}