diff options
10 files changed, 154 insertions, 9 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index fb2c9e43497f..969a05c0a988 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3135,6 +3135,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method public boolean isInputMethodPickerShown(); + method @RequiresPermission("android.permission.TEST_INPUT_METHOD") public void setStylusWindowIdleTimeoutForTest(long); field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index a87b1338339a..d23fb363df1c 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -17,6 +17,7 @@ package android.inputmethodservice; import android.annotation.BinderThread; +import android.annotation.DurationMillisLong; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -82,6 +83,7 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_FINISH_STYLUS_HANDWRITING = 130; private static final int DO_UPDATE_TOOL_TYPE = 140; private static final int DO_REMOVE_STYLUS_HANDWRITING_WINDOW = 150; + private static final int DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT = 160; final WeakReference<InputMethodServiceInternal> mTarget; final Context mContext; @@ -151,7 +153,7 @@ class IInputMethodWrapper extends IInputMethod.Stub final InputMethodServiceInternal target = mTarget.get(); switch (msg.what) { case DO_DUMP: { - SomeArgs args = (SomeArgs)msg.obj; + SomeArgs args = (SomeArgs) msg.obj; if (isValid(inputMethod, target, "DO_DUMP")) { final FileDescriptor fd = (FileDescriptor) args.arg1; final PrintWriter fout = (PrintWriter) args.arg2; @@ -201,7 +203,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } return; case DO_CREATE_SESSION: { - SomeArgs args = (SomeArgs)msg.obj; + SomeArgs args = (SomeArgs) msg.obj; if (isValid(inputMethod, target, "DO_CREATE_SESSION")) { inputMethod.createSession(new InputMethodSessionCallbackWrapper( mContext, (InputChannel) args.arg1, @@ -216,7 +218,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } return; case DO_SHOW_SOFT_INPUT: { - final SomeArgs args = (SomeArgs)msg.obj; + final SomeArgs args = (SomeArgs) msg.obj; if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) { inputMethod.showSoftInputWithToken( msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1); @@ -287,6 +289,10 @@ class IInputMethodWrapper extends IInputMethod.Stub } return; } + case DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT: { + inputMethod.setStylusWindowIdleTimeoutForTest((long) msg.obj); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -300,7 +306,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { - + fout.println("Permission Denial: can't dump InputMethodManager from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); @@ -473,6 +479,13 @@ class IInputMethodWrapper extends IInputMethod.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW)); } + @BinderThread + @Override + public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT, timeout)); + } + private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target, String msg) { if (inputMethod != null && target != null && !target.isServiceDestroyed()) { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6cd542af3574..f92f8a328519 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -57,6 +57,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.AnyThread; import android.annotation.CallSuper; import android.annotation.DrawableRes; +import android.annotation.DurationMillisLong; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; @@ -363,6 +364,11 @@ public class InputMethodService extends AbstractInputMethodService { STYLUS_HANDWRITING_IDLE_TIMEOUT_MS * 3; /** + * Stylus idle-timeout after which stylus {@code InkWindow} will be removed. + */ + private static final long STYLUS_WINDOW_IDLE_TIMEOUT_MILLIS = 5 * 60 * 1000; // 5 minutes. + + /** * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view. **/ private RingBuffer<MotionEvent> mPendingEvents; @@ -372,6 +378,8 @@ public class InputMethodService extends AbstractInputMethodService { private Runnable mImeSurfaceRemoverRunnable; private Runnable mFinishHwRunnable; private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS; + private Runnable mStylusWindowIdleTimeoutRunnable; + private long mStylusWindowIdleTimeoutForTest; /** * Returns whether {@link InputMethodService} is responsible for rendering the back button and @@ -1035,7 +1043,6 @@ public class InputMethodService extends AbstractInputMethodService { mInkWindow = new InkWindow(mWindow.getContext()); mInkWindow.setToken(mToken); } - // TODO(b/243571274): set an idle-timeout after which InkWindow is removed. mInkWindow.initOnly(); } @@ -1059,6 +1066,15 @@ public class InputMethodService extends AbstractInputMethodService { /** * {@inheritDoc} + * @hide + */ + @Override + public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { + mStylusWindowIdleTimeoutForTest = timeout; + } + + /** + * {@inheritDoc} */ @MainThread @Override @@ -2485,6 +2501,11 @@ public class InputMethodService extends AbstractInputMethodService { }); } } + + // Create a stylus window idle-timeout after which InkWindow is removed. + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + scheduleStylusWindowIdleTimeout(); + } } /** @@ -2545,7 +2566,6 @@ public class InputMethodService extends AbstractInputMethodService { mHandwritingEventReceiver.dispose(); mHandwritingEventReceiver = null; - // TODO(b/243571274): set an idle-timeout after which InkWindow is removed. mInkWindow.hide(false /* remove */); mPrivOps.resetStylusHandwriting(requestId); @@ -2569,9 +2589,41 @@ public class InputMethodService extends AbstractInputMethodService { } private void removeHandwritingInkWindow() { - mInkWindow.hide(true /* remove */); - mInkWindow.destroy(); - mInkWindow = null; + cancelStylusWindowIdleTimeout(); + mOnPreparedStylusHwCalled = false; + mStylusWindowIdleTimeoutRunnable = null; + if (mInkWindow != null) { + mInkWindow.hide(true /* remove */); + mInkWindow.destroy(); + mInkWindow = null; + } + } + + private void cancelStylusWindowIdleTimeout() { + if (mStylusWindowIdleTimeoutRunnable != null && mHandler != null) { + mHandler.removeCallbacks(mStylusWindowIdleTimeoutRunnable); + } + } + + private void scheduleStylusWindowIdleTimeout() { + if (mHandler == null) { + return; + } + cancelStylusWindowIdleTimeout(); + long timeout = (mStylusWindowIdleTimeoutForTest > 0) + ? mStylusWindowIdleTimeoutForTest : STYLUS_WINDOW_IDLE_TIMEOUT_MILLIS; + mHandler.postDelayed(getStylusWindowIdleTimeoutRunnable(), timeout); + } + + private Runnable getStylusWindowIdleTimeoutRunnable() { + if (mStylusWindowIdleTimeoutRunnable == null) { + mStylusWindowIdleTimeoutRunnable = () -> { + removeHandwritingInkWindow(); + mStylusWindowIdleTimeoutRunnable = null; + }; + } + + return mStylusWindowIdleTimeoutRunnable; } /** diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java index 429b0b80aec3..70fd8cf114d5 100644 --- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java +++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.annotation.AnyThread; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -264,4 +265,14 @@ final class IInputMethodManagerInvoker { throw e.rethrowFromSystemServer(); } } + + @AnyThread + void setStylusWindowIdleTimeoutForTest( + IInputMethodClient client, @DurationMillisLong long timeout) { + try { + mTarget.setStylusWindowIdleTimeoutForTest(client, timeout); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index 978bfc7af60d..4d5a17d92f11 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.DurationMillisLong; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -417,4 +418,12 @@ public interface InputMethod { default void removeStylusHandwritingWindow() { // intentionally empty } + + /** + * Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed. + * @hide + */ + default void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { + // intentionally empty + } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7794b7c90303..c01b4055293b 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -36,8 +36,10 @@ import static com.android.internal.inputmethod.StartInputReason.BOUND_TO_IMMS; import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION; +import android.Manifest; import android.annotation.DisplayContext; import android.annotation.DrawableRes; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; @@ -2576,6 +2578,20 @@ public final class InputMethodManager { } /** + * Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed. + * <p> This API is for tests only.</p> + * @param timeout to set in milliseconds. To reset to default, use a value <= zero. + * @hide + */ + @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) + @TestApi + public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { + synchronized (mH) { + mServiceInvoker.setStylusWindowIdleTimeoutForTest(mClient, timeout); + } + } + + /** * An empty method only to avoid crashes of apps that call this method via reflection and do not * handle {@link NoSuchMethodException} in a graceful manner. * diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl index 9182d1dc56bf..c62fba9fb9b8 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl @@ -87,4 +87,6 @@ oneway interface IInputMethod { void finishStylusHandwriting(); void removeStylusHandwritingWindow(); + + void setStylusWindowIdleTimeoutForTest(long timeout); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 9471faec8ea1..83660566b0d9 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -144,4 +144,10 @@ interface IInputMethodManager { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.INJECT_EVENTS)") void addVirtualStylusIdForTestSession(in IInputMethodClient client); + + /** Set a stylus idle-timeout after which handwriting {@code InkWindow} will be removed. */ + @EnforcePermission("TEST_INPUT_METHOD") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + + "android.Manifest.permission.TEST_INPUT_METHOD)") + void setStylusWindowIdleTimeoutForTest(in IInputMethodClient client, long timeout); } diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index 3e397468aa87..0810e3b1f928 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -286,4 +286,13 @@ final class IInputMethodInvoker { logRemoteException(e); } } + + @AnyThread + void setStylusWindowIdleTimeoutForTest(long timeout) { + try { + mTarget.setStylusWindowIdleTimeoutForTest(timeout); + } catch (RemoteException e) { + logRemoteException(e); + } + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index ed17b9ca3a47..75834ccde245 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -60,6 +60,7 @@ import android.annotation.AnyThread; import android.annotation.BinderThread; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.DurationMillisLong; import android.annotation.EnforcePermission; import android.annotation.IntDef; import android.annotation.NonNull; @@ -4463,6 +4464,31 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } + /** + * Helper method to set a stylus idle-timeout after which handwriting {@code InkWindow} + * will be removed. + * @param timeout to set in milliseconds. To reset to default, use a value <= zero. + */ + @BinderThread + @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD) + @Override + public void setStylusWindowIdleTimeoutForTest( + IInputMethodClient client, @DurationMillisLong long timeout) { + int uid = Binder.getCallingUid(); + synchronized (ImfLock.class) { + if (!canInteractWithImeLocked(uid, client, "setStylusWindowIdleTimeoutForTest")) { + return; + } + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Setting stylus window idle timeout"); + getCurMethodLocked().setStylusWindowIdleTimeoutForTest(timeout); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + @GuardedBy("ImfLock.class") private void removeVirtualStylusIdForTestSessionLocked() { removeStylusDeviceIdLocked(VIRTUAL_STYLUS_ID_FOR_TEST); |