diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java index 6cc652d942ccb..2c2763988d149 100644 --- a/core/java/android/util/imetracing/ImeTracingClientImpl.java +++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java @@ -25,6 +25,9 @@ import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.InputMethodManager; +import com.android.internal.inputmethod.Completable; +import com.android.internal.inputmethod.ResultCallbacks; + import java.io.PrintWriter; /** @@ -32,7 +35,9 @@ import java.io.PrintWriter; */ class ImeTracingClientImpl extends ImeTracing { ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException { - sEnabled = mService.isImeTraceEnabled(); + final Completable.Boolean value = Completable.createBoolean(); + mService.isImeTraceEnabled(ResultCallbacks.of(value)); + sEnabled = Completable.getResult(value); } @Override diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index eaf72dce62fe2..6526b8c4d743a 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1747,8 +1747,14 @@ public final class InputMethodManager { try { Log.d(TAG, "showSoftInput() view=" + view + " flags=" + flags); - return mService.showSoftInput( - mClient, view.getWindowToken(), flags, resultReceiver); + final Completable.Boolean value = Completable.createBoolean(); + mService.showSoftInput( + mClient, + view.getWindowToken(), + flags, + resultReceiver, + ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1775,8 +1781,14 @@ public final class InputMethodManager { Log.w(TAG, "No current root view, ignoring showSoftInputUnchecked()"); return; } + final Completable.Boolean value = Completable.createBoolean(); mService.showSoftInput( - mClient, mCurRootView.getView().getWindowToken(), flags, resultReceiver); + mClient, + mCurRootView.getView().getWindowToken(), + flags, + resultReceiver, + ResultCallbacks.of(value)); + Completable.getResult(value); // ignore the result } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1849,7 +1861,10 @@ public final class InputMethodManager { } try { - return mService.hideSoftInput(mClient, windowToken, flags, resultReceiver); + final Completable.Boolean value = Completable.createBoolean(); + mService.hideSoftInput( + mClient, windowToken, flags, resultReceiver, ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2184,8 +2199,14 @@ public final class InputMethodManager { return; } try { + final Completable.Boolean value = Completable.createBoolean(); mService.hideSoftInput( - mClient, mCurRootView.getView().getWindowToken(), HIDE_NOT_ALWAYS, null); + mClient, + mCurRootView.getView().getWindowToken(), + HIDE_NOT_ALWAYS, + null, + ResultCallbacks.of(value)); + Completable.getResult(value); // ignore the result } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2920,7 +2941,9 @@ public final class InputMethodManager { @TestApi public boolean isInputMethodPickerShown() { try { - return mService.isInputMethodPickerShownForTest(); + final Completable.Boolean value = Completable.createBoolean(); + mService.isInputMethodPickerShownForTest(ResultCallbacks.of(value)); + return Completable.getResult(value); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java index a315e5a4fc940..a77583f87ee43 100644 --- a/core/java/com/android/internal/inputmethod/CallbackUtils.java +++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java @@ -22,6 +22,7 @@ import android.os.RemoteException; import com.android.internal.view.InputBindResult; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; /** @@ -62,4 +63,30 @@ public final class CallbackUtils { callback.onResult(result); } catch (RemoteException ignored) { } } + + /** + * A utility method using given {@link IBooleanResultCallback} to callback the result. + * + * @param callback {@link IInputBindResultResultCallback} to be called back. + * @param resultSupplier the supplier from which the result is provided. + */ + public static void onResult(@NonNull IBooleanResultCallback callback, + @NonNull BooleanSupplier resultSupplier) { + boolean result = false; + Throwable exception = null; + + try { + result = resultSupplier.getAsBoolean(); + } catch (Throwable throwable) { + exception = throwable; + } + + try { + if (exception != null) { + callback.onError(ThrowableHolder.of(exception)); + return; + } + callback.onResult(result); + } catch (RemoteException ignored) { } + } } diff --git a/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl b/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl new file mode 100644 index 0000000000000..6daeb3f27414a --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IBooleanResultCallback.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 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.internal.inputmethod; + +import com.android.internal.inputmethod.ThrowableHolder; + +oneway interface IBooleanResultCallback { + void onResult(boolean result); + void onError(in ThrowableHolder exception); +} \ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java index 1929b6f22f8bc..b07c5f8c9b2a8 100644 --- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -193,4 +193,39 @@ public final class ResultCallbacks { } }; } + + /** + * Creates {@link IBooleanResultCallback.Stub} that is to set {@link Completable.Boolean} when + * receiving the result. + * + * @param value {@link Completable.Boolean} to be set when receiving the result. + * @return {@link IBooleanResultCallback.Stub} that can be passed as a binder IPC parameter. + */ + @AnyThread + public static IBooleanResultCallback.Stub of(@NonNull Completable.Boolean value) { + final AtomicReference> + atomicRef = new AtomicReference<>(new WeakReference<>(value)); + + return new IBooleanResultCallback.Stub() { + @BinderThread + @Override + public void onResult(boolean result) { + final Completable.Boolean value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(result); + } + + @BinderThread + @Override + public void onError(ThrowableHolder throwableHolder) { + final Completable.Boolean value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onError(throwableHolder); + } + }; + } } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index e78ed4e211a79..455e48949690c 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo; import com.android.internal.view.InputBindResult; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; +import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; /** @@ -42,10 +43,10 @@ interface IInputMethodManager { boolean allowsImplicitlySelectedSubtypes); InputMethodSubtype getLastInputMethodSubtype(); - boolean showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, - in ResultReceiver resultReceiver); - boolean hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, - in ResultReceiver resultReceiver); + void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, + in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback); + void hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, + in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback); // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'attribute' is non-null then also does startInput. // @NonNull @@ -64,7 +65,7 @@ interface IInputMethodManager { void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode, int displayId); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); - boolean isInputMethodPickerShownForTest(); + void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback); InputMethodSubtype getCurrentInputMethodSubtype(); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); // This is kept due to @UnsupportedAppUsage. @@ -80,7 +81,7 @@ interface IInputMethodManager { /** Remove the IME surface. Requires passing the currently focused window. */ void removeImeSurfaceFromWindow(in IBinder windowToken); void startProtoDump(in byte[] protoDump, int source, String where); - boolean isImeTraceEnabled(); + void isImeTraceEnabled(in IBooleanResultCallback resultCallback); // Starts an ime trace. void startImeTrace(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 6395094038c76..90edd77bca344 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -159,6 +159,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -3150,41 +3151,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags, - ResultReceiver resultReceiver) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput"); - int uid = Binder.getCallingUid(); - ImeTracing.getInstance().triggerManagerServiceDump( - "InputMethodManagerService#showSoftInput"); - synchronized (mMethodMap) { - if (!calledFromValidUserLocked()) { - return false; - } - final long ident = Binder.clearCallingIdentity(); - try { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - final ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); - } - if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, - cs.selfReportedDisplayId)) { - Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); - return false; - } + public void showSoftInput(IInputMethodClient client, IBinder windowToken, int flags, + ResultReceiver resultReceiver, IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput"); + int uid = Binder.getCallingUid(); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#showSoftInput"); + synchronized (mMethodMap) { + if (!calledFromValidUserLocked()) { + return false; + } + final long ident = Binder.clearCallingIdentity(); + try { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + final ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException( + "unknown client " + client.asBinder()); + } + if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, + cs.selfReportedDisplayId)) { + Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); + return false; + } + } + if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); + return showCurrentInputLocked(windowToken, flags, resultReceiver, + SoftInputShowHideReason.SHOW_SOFT_INPUT); + } finally { + Binder.restoreCallingIdentity(ident); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(windowToken, flags, resultReceiver, - SoftInputShowHideReason.SHOW_SOFT_INPUT); - } finally { - Binder.restoreCallingIdentity(ident); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - } + }); } @BinderThread @@ -3266,44 +3270,49 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags, - ResultReceiver resultReceiver) { - int uid = Binder.getCallingUid(); - ImeTracing.getInstance().triggerManagerServiceDump( - "InputMethodManagerService#hideSoftInput"); - synchronized (mMethodMap) { - if (!calledFromValidUserLocked()) { - return false; - } - final long ident = Binder.clearCallingIdentity(); - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput"); - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - final ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); - } - if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, - cs.selfReportedDisplayId)) { - if (DEBUG) { - Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); - } - return false; - } + public void hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags, + ResultReceiver resultReceiver, IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> { + int uid = Binder.getCallingUid(); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#hideSoftInput"); + synchronized (mMethodMap) { + if (!InputMethodManagerService.this.calledFromValidUserLocked()) { + return false; } + final long ident = Binder.clearCallingIdentity(); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideSoftInput"); + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + final ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException( + "unknown client " + client.asBinder()); + } + if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, + cs.selfReportedDisplayId)) { + if (DEBUG) { + Slog.w(TAG, + "Ignoring hideSoftInput of uid " + uid + ": " + client); + } + return false; + } + } - if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); - return hideCurrentInputLocked(windowToken, flags, resultReceiver, - SoftInputShowHideReason.HIDE_SOFT_INPUT); - } finally { - Binder.restoreCallingIdentity(ident); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); + return InputMethodManagerService.this.hideCurrentInputLocked(windowToken, + flags, resultReceiver, + SoftInputShowHideReason.HIDE_SOFT_INPUT); + } finally { + Binder.restoreCallingIdentity(ident); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } - } + }); } boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @@ -3726,9 +3735,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId)); } - public boolean isInputMethodPickerShownForTest() { + /** + * A test API for CTS to make sure that the input method menu is showing. + * + * @param resultCallback {@code true} while the input method menu is showing UI. + */ + public void isInputMethodPickerShownForTest(IBooleanResultCallback resultCallback) { synchronized(mMethodMap) { - return mMenuController.isisInputMethodPickerShownForTestLocked(); + CallbackUtils.onResult( + resultCallback, mMenuController::isisInputMethodPickerShownForTestLocked); } } @@ -4120,8 +4135,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public boolean isImeTraceEnabled() { - return ImeTracing.getInstance().isEnabled(); + public void isImeTraceEnabled(IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> ImeTracing.getInstance().isEnabled()); } @BinderThread diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 6bdae63461b27..ccb78a416386e 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -73,6 +73,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IBooleanResultCallback; import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; @@ -1501,7 +1502,15 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public boolean showSoftInput( + public void showSoftInput( + IInputMethodClient client, IBinder token, int flags, + ResultReceiver resultReceiver, IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, + () -> showSoftInputInternal(client, token, flags, resultReceiver)); + } + + @BinderThread + private boolean showSoftInputInternal( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver) { final int callingUid = Binder.getCallingUid(); @@ -1548,7 +1557,16 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public boolean hideSoftInput( + public void hideSoftInput( + IInputMethodClient client, IBinder windowToken, int flags, + ResultReceiver resultReceiver, IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, + () -> hideSoftInputInternal(client, windowToken, flags, resultReceiver)); + + } + + @BinderThread + private boolean hideSoftInputInternal( IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver) { final int callingUid = Binder.getCallingUid(); @@ -1768,9 +1786,9 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public boolean isInputMethodPickerShownForTest() { + public void isInputMethodPickerShownForTest(IBooleanResultCallback resultCallback) { reportNotSupported(); - return false; + CallbackUtils.onResult(resultCallback, () -> false); } @BinderThread @@ -1834,8 +1852,8 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public boolean isImeTraceEnabled() { - return false; + public void isImeTraceEnabled(IBooleanResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, () -> false); } @BinderThread