diff options
6 files changed, 116 insertions, 14 deletions
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index ef0c19c8dc02..12f30f1f05c3 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -164,6 +164,38 @@ public final class InputWindowHandle { this.displayId = displayId; } + public InputWindowHandle(InputWindowHandle other) { + // Do not copy ptr to prevent this copy from sharing the same native object. + ptr = 0; + inputApplicationHandle = new InputApplicationHandle(other.inputApplicationHandle); + token = other.token; + windowToken = other.windowToken; + window = other.window; + name = other.name; + layoutParamsFlags = other.layoutParamsFlags; + layoutParamsType = other.layoutParamsType; + dispatchingTimeoutMillis = other.dispatchingTimeoutMillis; + frameLeft = other.frameLeft; + frameTop = other.frameTop; + frameRight = other.frameRight; + frameBottom = other.frameBottom; + surfaceInset = other.surfaceInset; + scaleFactor = other.scaleFactor; + touchableRegion.set(other.touchableRegion); + inputConfig = other.inputConfig; + touchOcclusionMode = other.touchOcclusionMode; + ownerPid = other.ownerPid; + ownerUid = other.ownerUid; + packageName = other.packageName; + displayId = other.displayId; + touchableRegionSurfaceControl = other.touchableRegionSurfaceControl; + replaceTouchableRegionWithCrop = other.replaceTouchableRegionWithCrop; + if (other.transform != null) { + transform = new Matrix(); + transform.set(other.transform); + } + } + @Override public String toString() { return new StringBuilder(name != null ? name : "") diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index 1a19385e71c5..db17c1056e39 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -27,7 +27,6 @@ import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.WindowManager; - final class HandwritingEventReceiverSurface { public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName(); @@ -39,7 +38,6 @@ final class HandwritingEventReceiverSurface { // TODO(b/217538817): Specify the ordering in WM by usage. private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1; - private final InputApplicationHandle mApplicationHandle; private final InputWindowHandle mWindowHandle; private final InputChannel mClientChannel; private final SurfaceControl mInputSurface; @@ -47,13 +45,11 @@ final class HandwritingEventReceiverSurface { HandwritingEventReceiverSurface(String name, int displayId, @NonNull SurfaceControl sc, @NonNull InputChannel inputChannel) { - mApplicationHandle = new InputApplicationHandle(null, name, - DEFAULT_DISPATCHING_TIMEOUT_MILLIS); - mClientChannel = inputChannel; mInputSurface = sc; - mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId); + mWindowHandle = new InputWindowHandle(new InputApplicationHandle(null, name, + DEFAULT_DISPATCHING_TIMEOUT_MILLIS), displayId); mWindowHandle.name = name; mWindowHandle.token = mClientChannel.getToken(); mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; @@ -61,7 +57,6 @@ final class HandwritingEventReceiverSurface { mWindowHandle.ownerPid = Process.myPid(); mWindowHandle.ownerUid = Process.myUid(); mWindowHandle.scaleFactor = 1.0f; - mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.NOT_TOUCHABLE @@ -69,6 +64,7 @@ final class HandwritingEventReceiverSurface { | InputConfig.INTERCEPTS_STYLUS | InputConfig.TRUSTED_OVERLAY; + // The touchable region of this input surface is not initially configured. final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER); @@ -85,6 +81,10 @@ final class HandwritingEventReceiverSurface { mWindowHandle.ownerUid = imeUid; mWindowHandle.inputConfig &= ~InputConfig.SPY; + // Update the touchable region so that the IME can intercept stylus events + // across the entire display. + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + new SurfaceControl.Transaction() .setInputWindowInfo(mInputSurface, mWindowHandle) .apply(); @@ -108,4 +108,8 @@ final class HandwritingEventReceiverSurface { SurfaceControl getSurface() { return mInputSurface; } + + InputWindowHandle getInputWindowHandle() { + return mWindowHandle; + } } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java index b301d99b00de..a70677222506 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java @@ -22,6 +22,7 @@ import android.annotation.AnyThread; import android.annotation.Nullable; import android.annotation.UiThread; import android.hardware.input.InputManagerInternal; +import android.os.IBinder; import android.os.Looper; import android.util.Slog; import android.view.BatchedInputEventReceiver; @@ -90,7 +91,7 @@ final class HandwritingModeController { * InputEventReceiver that batches events according to the current thread's Choreographer. */ @UiThread - void initializeHandwritingSpy(int displayId) { + void initializeHandwritingSpy(int displayId, IBinder focusedWindowToken) { // When resetting, reuse resources if we are reinitializing on the same display. reset(displayId == mCurrentDisplayId); mCurrentDisplayId = displayId; @@ -110,8 +111,16 @@ final class HandwritingModeController { Slog.e(TAG, "Failed to create input surface"); return; } - mHandwritingSurface = - new HandwritingEventReceiverSurface(name, displayId, surface, channel); + + mHandwritingSurface = new HandwritingEventReceiverSurface( + name, displayId, surface, channel); + + // Configure the handwriting window to receive events over the focused window's bounds. + mWindowManagerInternal.replaceInputSurfaceTouchableRegionWithWindowCrop( + mHandwritingSurface.getSurface(), + mHandwritingSurface.getInputWindowHandle(), + focusedWindowToken); + // Use a dup of the input channel so that event processing can be paused by disposing the // event receiver without causing a fd hangup. mHandwritingEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver( @@ -127,6 +136,10 @@ final class HandwritingModeController { return OptionalInt.of(mCurrentRequestId); } + boolean isStylusGestureOngoing() { + return mRecordingGesture; + } + /** * Starts a {@link HandwritingSession} to transfer to the IME. * @@ -145,6 +158,10 @@ final class HandwritingModeController { Slog.e(TAG, "Cannot start handwriting session: Invalid request id: " + requestId); return null; } + if (!mRecordingGesture) { + Slog.e(TAG, "Cannot start handwriting session: No stylus gesture is being recorded."); + return null; + } Objects.requireNonNull(mHandwritingEventReceiver, "Handwriting session was already transferred to IME."); if (DEBUG) Slog.d(TAG, "Starting handwriting session in display: " + mCurrentDisplayId); @@ -231,7 +248,7 @@ final class HandwritingModeController { mInkWindowInitRunnable = null; } - if (action == MotionEvent.ACTION_UP) { + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mRecordingGesture = false; mHandwritingBuffer.clear(); return; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 77dcbd3e9277..fca9f56b2407 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3372,6 +3372,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Slog.e(TAG, "Stylus handwriting was not initialized."); return; } + if (!mHwController.isStylusGestureOngoing()) { + Slog.e(TAG, "There is no ongoing stylus gesture to start stylus handwriting."); + return; + } if (DEBUG) Slog.v(TAG, "Client requesting Stylus Handwriting to be started"); final IInputMethodInvoker curMethod = getCurMethodLocked(); if (curMethod != null) { @@ -4764,8 +4768,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_RESET_HANDWRITING: { synchronized (ImfLock.class) { if (mBindingController.supportsStylusHandwriting() - && getCurMethodLocked() != null) { - mHwController.initializeHandwritingSpy(mCurTokenDisplayId); + && getCurMethodLocked() != null && mCurFocusedWindow != null) { + mHwController.initializeHandwritingSpy( + mCurTokenDisplayId, mCurFocusedWindow); } else { mHwController.reset(); } @@ -4793,7 +4798,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub session.getHandwritingChannel(), session.getRecordedEvents())) { // When failed to issue IPCs, re-initialize handwriting state. Slog.w(TAG, "Resetting handwriting mode."); - mHwController.initializeHandwritingSpy(mCurTokenDisplayId); + scheduleResetStylusHandwriting(); } } return true; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 0a3c3f049f43..be745966ce30 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -33,6 +33,7 @@ import android.view.IInputFilter; import android.view.IRemoteAnimationFinishedCallback; import android.view.IWindow; import android.view.InputChannel; +import android.view.InputWindowHandle; import android.view.MagnificationSpec; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -853,4 +854,26 @@ public abstract class WindowManagerInternal { * support handwriting (Scribe) by the IME. */ public abstract SurfaceControl getHandwritingSurfaceForDisplay(int displayId); + + /** + * Replaces the touchable region of the provided input surface with the crop of the window with + * the provided token. This method will associate the inputSurface with a copy of + * the given inputWindowHandle, where the copy is configured using + * {@link InputWindowHandle#replaceTouchableRegionWithCrop(SurfaceControl)} with the surface + * of the provided windowToken. + * + * This is a no-op if windowToken is not valid or the window is not found. + * + * This does not change any other properties of the inputSurface. + * + * This method exists to avoid leaking the window's SurfaceControl outside WindowManagerService. + * + * @param inputSurface The surface for which the touchable region should be set. + * @param inputWindowHandle The {@link InputWindowHandle} for the input surface. + * @param windowToken The window whose bounds should be used as the touchable region for the + * inputSurface. + */ + public abstract void replaceInputSurfaceTouchableRegionWithWindowCrop( + @NonNull SurfaceControl inputSurface, @NonNull InputWindowHandle inputWindowHandle, + @NonNull IBinder windowToken); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c50888b5a172..48cf971631fe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8121,6 +8121,27 @@ public class WindowManagerService extends IWindowManager.Stub .build(); } } + + @Override + public void replaceInputSurfaceTouchableRegionWithWindowCrop( + @NonNull SurfaceControl inputSurface, + @NonNull InputWindowHandle inputWindowHandle, + @NonNull IBinder windowToken) { + synchronized (mGlobalLock) { + final WindowState w = mWindowMap.get(windowToken); + if (w == null) { + return; + } + // Make a copy of the InputWindowHandle to avoid leaking the window's + // SurfaceControl. + final InputWindowHandle localHandle = new InputWindowHandle(inputWindowHandle); + localHandle.replaceTouchableRegionWithCrop(w.getSurfaceControl()); + final SurfaceControl.Transaction t = mTransactionFactory.get(); + t.setInputWindowInfo(inputSurface, localHandle); + t.apply(); + t.close(); + } + } } void registerAppFreezeListener(AppFreezeListener listener) { |