Merge changes I6b8787a2,Ie4194051 into main
* changes:
Version-gate IME show/hide async nature
Add flag for fixing return of show/hide input
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 2f515fe..3a008aa 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -323,14 +323,14 @@
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -339,14 +339,15 @@
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ boolean async) {
final IInputMethodManager service = getService();
if (service == null) {
return false;
}
try {
return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -407,7 +408,7 @@
@Nullable IRemoteInputConnection remoteInputConnection,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
final IInputMethodManager service = getService();
if (service == null) {
return -1;
@@ -416,7 +417,7 @@
service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
- imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 23d7732..2f649c2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -441,6 +442,36 @@
public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
/**
+ * Use async method for {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)},
+ * {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int, ResultReceiver)},
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} for apps targeting V+.
+ * <p>
+ * Apps can incorrectly rely on {@link InputMethodManager#showSoftInput(View, int)} and
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} method return type
+ * to interpret result of a request rather than relying on {@link ResultReceiver}. The return
+ * type of the method was never documented to have accurate info of visibility but few apps
+ * incorrectly rely on it.
+ * <p>
+ * Starting Android V, we use async calls into system_server which returns {@code true} if
+ * method call was made but return type doesn't guarantee execution.
+ * Apps targeting older versions will fallback to existing behavior of calling synchronous
+ * methods which had undocumented result in return type.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
+
+ /**
+ * Version-gating is guarded by bug-fix flag.
+ */
+ private static final boolean ASYNC_SHOW_HIDE_METHOD_ENABLED =
+ !Flags.compatchangeForZerojankproxy()
+ || CompatChanges.isChangeEnabled(USE_ASYNC_SHOW_HIDE_METHOD);
+
+ /**
* If {@code true}, avoid calling the
* {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
* by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus}
@@ -2246,6 +2277,8 @@
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
@@ -2315,6 +2348,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2383,7 +2418,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2426,7 +2462,8 @@
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
- reason);
+ reason,
+ ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -2459,6 +2496,9 @@
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@link ResultReceiver} in
+ * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2487,6 +2527,8 @@
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
+ * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
+ * this does not return result of the request. For result use {@param resultReceiver} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
@@ -2530,7 +2572,7 @@
return true;
} else {
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
- statsToken, flags, resultReceiver, reason);
+ statsToken, flags, resultReceiver, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
}
@@ -2573,7 +2615,7 @@
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(),
- statsToken, flags, null, reason);
+ statsToken, flags, null, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
}
}
@@ -3350,7 +3392,7 @@
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+ mImeDispatcher, ASYNC_SHOW_HIDE_METHOD_ENABLED);
} else {
res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3653,7 +3695,8 @@
statsToken,
HIDE_NOT_ALWAYS,
null,
- reason);
+ reason,
+ true /*async */);
}
}
@@ -3745,7 +3788,7 @@
IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
0 /* flags */, null /* resultReceiver */,
- SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+ SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, true /* async */);
}
}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e294ee2..bae8aff 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -136,3 +136,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "compatchange_for_zerojankproxy"
+ namespace: "input_method"
+ description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
+ bug: "352594277"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b51678e..efbf887 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -76,10 +76,10 @@
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags, int lastClickToolType,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in ImeTracker.Token statsToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ in @nullable ResultReceiver resultReceiver, int reason, boolean async);
/**
* A test API for CTS to request hiding the current soft input window, with the request origin
@@ -120,7 +120,8 @@
in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, int userId,
- in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 58e3452..e1f26d6 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -116,11 +116,11 @@
boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason);
+ @SoftInputShowHideReason int reason, boolean async);
boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async);
@PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
void hideSoftInputFromServerForTest();
@@ -132,7 +132,8 @@
@Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod);
InputBindResult startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client,
@@ -290,17 +291,17 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
- resultReceiver, reason);
+ resultReceiver, reason, async);
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
- reason);
+ reason, async);
}
@EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -336,11 +337,13 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
mCallback.startInputOrWindowGainedFocusAsync(
startInputReason, client, windowToken, startInputFlags, softInputMode,
windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
- unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+ unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq,
+ useAsyncShowHideMethod);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2ad0d2a..7cfd2cc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3086,7 +3086,7 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, boolean async) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput", mDumper);
@@ -3533,7 +3533,7 @@
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput", mDumper);
synchronized (ImfLock.class) {
@@ -3675,7 +3675,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
// implemented by ZeroJankProxy
}
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index c940a9c..214aa1d 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -77,6 +77,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* A proxy that processes all {@link IInputMethodManager} calls asynchronously.
@@ -175,19 +176,45 @@
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
@MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- offload(() -> mInner.showSoftInput(
- client, windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason));
- return true;
+ @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.showSoftInput(
+ client, windowToken, statsToken, flags, lastClickToolType, resultReceiver,
+ reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.showSoftInput(
+ client,
+ windowToken,
+ statsToken,
+ flags,
+ lastClickToolType,
+ resultReceiver,
+ reason,
+ async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- offload(() -> mInner.hideSoftInput(
- client, windowToken, statsToken, flags, resultReceiver, reason));
- return true;
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
+
+ if (async) {
+ offload(() -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async));
+ return true;
+ } else {
+ final var future = CompletableFuture.supplyAsync(
+ () -> mInner.hideSoftInput(
+ client, windowToken, statsToken, flags, resultReceiver, reason, async),
+ this::offload);
+ return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+ }
}
@Override
@@ -207,7 +234,8 @@
IRemoteInputConnection inputConnection,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId,
- @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+ boolean useAsyncShowHideMethod) {
offload(() -> {
InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags,