Merge "Idle-timeout handwriting delegation" into udc-dev
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index eb4dba6..502855d 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -25,6 +25,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.UiThread;
 import android.hardware.input.InputManager;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.text.TextUtils;
@@ -55,17 +56,20 @@
     public static final String TAG = HandwritingModeController.class.getSimpleName();
     // TODO(b/210039666): flip the flag.
     static final boolean DEBUG = true;
+    // Use getHandwritingBufferSize() and not this value directly.
     private static final int EVENT_BUFFER_SIZE = 100;
     // A longer event buffer used for handwriting delegation
     // TODO(b/210039666): make this device touch sampling rate dependent.
-    private static final int LONG_EVENT_BUFFER = EVENT_BUFFER_SIZE * 20;
+    // Use getHandwritingBufferSize() and not this value directly.
+    private static final int LONG_EVENT_BUFFER_SIZE = EVENT_BUFFER_SIZE * 20;
+    private static final long HANDWRITING_DELEGATION_IDLE_TIMEOUT_MS = 3000;
 
     // This must be the looper for the UiThread.
     private final Looper mLooper;
     private final InputManagerInternal mInputManagerInternal;
     private final WindowManagerInternal mWindowManagerInternal;
 
-    private List<MotionEvent> mHandwritingBuffer;
+    private ArrayList<MotionEvent> mHandwritingBuffer;
     private InputEventReceiver mHandwritingEventReceiver;
     private Runnable mInkWindowInitRunnable;
     private boolean mRecordingGesture;
@@ -73,6 +77,8 @@
     // when set, package names are used for handwriting delegation.
     private @Nullable String mDelegatePackageName;
     private @Nullable String mDelegatorPackageName;
+    private Runnable mDelegationIdleTimeoutRunnable;
+    private Handler mDelegationIdleTimeoutHandler;
 
     private HandwritingEventReceiverSurface mHandwritingSurface;
 
@@ -110,7 +116,7 @@
         mCurrentDisplayId = displayId;
 
         if (mHandwritingBuffer == null) {
-            mHandwritingBuffer = new ArrayList<>(EVENT_BUFFER_SIZE);
+            mHandwritingBuffer = new ArrayList<>(getHandwritingBufferSize());
         }
 
         if (DEBUG) Slog.d(TAG, "Initializing handwriting spy monitor for display: " + displayId);
@@ -159,8 +165,8 @@
             @NonNull String delegatePackageName, @NonNull String delegatorPackageName) {
         mDelegatePackageName = delegatePackageName;
         mDelegatorPackageName = delegatorPackageName;
-        ((ArrayList) mHandwritingBuffer).ensureCapacity(LONG_EVENT_BUFFER);
-        // TODO(b/210039666): cancel delegation after a timeout or next input method client binding.
+        mHandwritingBuffer.ensureCapacity(getHandwritingBufferSize());
+        scheduleHandwritingDelegationTimeout();
     }
 
     @Nullable String getDelegatePackageName() {
@@ -171,6 +177,32 @@
         return mDelegatorPackageName;
     }
 
+    private void scheduleHandwritingDelegationTimeout() {
+        if (mDelegationIdleTimeoutHandler == null) {
+            mDelegationIdleTimeoutHandler = new Handler(mLooper);
+        } else {
+            mDelegationIdleTimeoutHandler.removeCallbacks(mDelegationIdleTimeoutRunnable);
+        }
+        mDelegationIdleTimeoutRunnable =  () -> {
+            Slog.d(TAG, "Stylus handwriting delegation idle timed-out.");
+            clearPendingHandwritingDelegation();
+            if (mHandwritingBuffer != null) {
+                mHandwritingBuffer.forEach(MotionEvent::recycle);
+                mHandwritingBuffer.clear();
+                mHandwritingBuffer.trimToSize();
+                mHandwritingBuffer.ensureCapacity(getHandwritingBufferSize());
+            }
+        };
+        mDelegationIdleTimeoutHandler.postDelayed(
+                mDelegationIdleTimeoutRunnable, HANDWRITING_DELEGATION_IDLE_TIMEOUT_MS);
+    }
+
+    private int getHandwritingBufferSize() {
+        if (mDelegatePackageName != null && mDelegatorPackageName != null) {
+            return LONG_EVENT_BUFFER_SIZE;
+        }
+        return EVENT_BUFFER_SIZE;
+    }
     /**
      * Clear any pending handwriting delegation info.
      */
@@ -178,6 +210,11 @@
         if (DEBUG) {
             Slog.d(TAG, "clearPendingHandwritingDelegation");
         }
+        if (mDelegationIdleTimeoutHandler != null) {
+            mDelegationIdleTimeoutHandler.removeCallbacks(mDelegationIdleTimeoutRunnable);
+            mDelegationIdleTimeoutHandler = null;
+        }
+        mDelegationIdleTimeoutRunnable = null;
         mDelegatorPackageName = null;
         mDelegatePackageName = null;
     }
@@ -322,7 +359,7 @@
             return;
         }
 
-        if (mHandwritingBuffer.size() >= EVENT_BUFFER_SIZE) {
+        if (mHandwritingBuffer.size() >= getHandwritingBufferSize()) {
             if (DEBUG) {
                 Slog.w(TAG, "Current gesture exceeds the buffer capacity."
                         + " The rest of the gesture will not be recorded.");