Merge "Make IInputMethodManager to oneway (3/N)"

This commit is contained in:
Wilson Wu
2020-12-01 10:39:18 +00:00
committed by Android (Google) Code Review
8 changed files with 232 additions and 61 deletions

View File

@@ -88,8 +88,10 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.Completable;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.inputmethod.ResultCallbacks;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
@@ -666,6 +668,7 @@ public final class InputMethodManager {
final int startInputReason =
nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION
: WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
final Completable.InputBindResult value = Completable.createInputBindResult();
mService.startInputOrWindowGainedFocus(
startInputReason, mClient,
focusedView.getWindowToken(), startInputFlags, softInputMode,
@@ -673,7 +676,9 @@ public final class InputMethodManager {
null,
null,
0 /* missingMethodFlags */,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
ResultCallbacks.of(value));
Completable.getResult(value); // ignore the result
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2039,10 +2044,13 @@ public final class InputMethodManager {
if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
+ ic + " tba=" + tba + " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
res = mService.startInputOrWindowGainedFocus(
final Completable.InputBindResult value = Completable.createInputBindResult();
mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
view.getContext().getApplicationInfo().targetSdkVersion);
view.getContext().getApplicationInfo().targetSdkVersion,
ResultCallbacks.of(value));
res = Completable.getResult(value);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"

View File

@@ -0,0 +1,52 @@
/*
* 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 android.annotation.AnyThread;
import android.annotation.NonNull;
import android.os.RemoteException;
import com.android.internal.view.InputBindResult;
import java.util.function.Supplier;
/**
* Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}.
*/
public final class CallbackUtils {
/**
* Not intended to be instantiated.
*/
private CallbackUtils() {
}
/**
* A utility method using given {@link IInputBindResultResultCallback} to callback the
* {@link InputBindResult}.
*
* @param callback {@link IInputBindResultResultCallback} to be called back.
* @param resultSupplier the supplier from which {@link InputBindResult} is provided.
*/
@AnyThread
public static void onResult(@NonNull IInputBindResultResultCallback callback,
@NonNull Supplier<InputBindResult> resultSupplier) {
try {
callback.onResult(resultSupplier.get());
} catch (RemoteException ignored) { }
}
}

View File

@@ -124,6 +124,16 @@ public final class Completable {
return true;
}
}
/**
* Blocks the calling thread until this object becomes ready to return the value.
*/
@AnyThread
public void await() {
try {
mLatch.await();
} catch (InterruptedException ignored) { }
}
}
/**
@@ -249,6 +259,13 @@ public final class Completable {
return new Completable.SurroundingText();
}
/**
* @return an instance of {@link Completable.InputBindResult}.
*/
public static Completable.InputBindResult createInputBindResult() {
return new Completable.InputBindResult();
}
/**
* Completable object of {@link java.lang.Boolean}.
*/
@@ -277,6 +294,18 @@ public final class Completable {
public static final class InputBindResult
extends Values<com.android.internal.view.InputBindResult> { }
/**
* Await the result by the {@link Completable.Values}.
*
* @return the result once {@link ValueBase#onComplete()}
*/
@AnyThread
@Nullable
public static <T> T getResult(@NonNull Completable.Values<T> value) {
value.await();
return value.getValue();
}
/**
* Await the result by the {@link Completable.Int}, and log it if there is no result after
* given timeout.

View File

@@ -0,0 +1,23 @@
/*
* 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.view.InputBindResult;
oneway interface IInputBindResultResultCallback {
void onResult(in InputBindResult result);
}

View File

@@ -21,6 +21,8 @@ import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.view.InputBindResult;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;
@@ -154,4 +156,31 @@ public final class ResultCallbacks {
}
};
}
/**
* Creates {@link IInputBindResultResultCallback.Stub} that is to set
* {@link Completable.InputBindResult} when receiving the result.
*
* @param value {@link Completable.InputBindResult} to be set when receiving the result.
* @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC
* parameter.
*/
@AnyThread
public static IInputBindResultResultCallback.Stub of(
@NonNull Completable.InputBindResult value) {
final AtomicReference<WeakReference<Completable.InputBindResult>>
atomicRef = new AtomicReference<>(new WeakReference<>(value));
return new IInputBindResultResultCallback.Stub() {
@BinderThread
@Override
public void onResult(InputBindResult result) {
final Completable.InputBindResult value = unwrap(atomicRef);
if (value == null) {
return;
}
value.onComplete(result);
}
};
}
}

View File

@@ -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.IInputBindResultResultCallback;
/**
* Public interface to the global input method manager, used by all client
@@ -48,14 +49,15 @@ interface IInputMethodManager {
// 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
InputBindResult startInputOrWindowGainedFocus(
void startInputOrWindowGainedFocus(
/* @StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken,
/* @StartInputFlags */ int startInputFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
int unverifiedTargetSdkVersion);
int unverifiedTargetSdkVersion,
in IInputBindResultResultCallback inputBindResult);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);

View File

@@ -158,6 +158,8 @@ import android.view.inputmethod.InputMethodSubtype;
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.IInputBindResultResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -208,6 +210,7 @@ import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* This class provides a system service that manages input methods.
@@ -3347,63 +3350,68 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
public void startInputOrWindowGainedFocus(
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
@MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
}
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"IMMS.startInputOrWindowGainedFocus");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#startInputOrWindowGainedFocus");
final int callingUserId = UserHandle.getCallingUserId();
final int userId;
if (attribute != null && attribute.targetInputMethodUser != null
&& attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"Using EditorInfo.targetInputMethodUser requires"
+ " INTERACT_ACROSS_USERS_FULL.");
userId = attribute.targetInputMethodUser.getIdentifier();
if (!mUserManagerInternal.isUserRunning(userId)) {
// There is a chance that we hit here because of race condition. Let's just
// return an error code instead of crashing the caller process, which at least
// has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important
// process.
Slog.e(TAG, "User #" + userId + " is not running.");
return InputBindResult.INVALID_USER;
}
} else {
userId = callingUserId;
}
final InputBindResult result;
synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity();
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
windowToken, startInputFlags, softInputMode, windowFlags, attribute,
inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " editorInfo=" + attribute);
@MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion,
IInputBindResultResultCallback resultCallback) {
CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
}
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"IMMS.startInputOrWindowGainedFocus");
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#startInputOrWindowGainedFocus");
final int callingUserId = UserHandle.getCallingUserId();
final int userId;
if (attribute != null && attribute.targetInputMethodUser != null
&& attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"Using EditorInfo.targetInputMethodUser requires"
+ " INTERACT_ACROSS_USERS_FULL.");
userId = attribute.targetInputMethodUser.getIdentifier();
if (!mUserManagerInternal.isUserRunning(userId)) {
// There is a chance that we hit here because of race condition. Let's just
// return an error code instead of crashing the caller process, which at
// least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
// important process.
Slog.e(TAG, "User #" + userId + " is not running.");
return InputBindResult.INVALID_USER;
}
} else {
userId = callingUserId;
}
final InputBindResult result;
synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity();
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
attribute, inputContext, missingMethods, unverifiedTargetSdkVersion,
userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " editorInfo=" + attribute);
return InputBindResult.NULL;
}
return result;
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
return result;
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
@NonNull

View File

@@ -72,6 +72,8 @@ import android.view.inputmethod.InputMethodSubtype;
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.IInputBindResultResultCallback;
import com.android.internal.inputmethod.IMultiClientInputMethod;
import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.IMultiClientInputMethodSession;
@@ -104,6 +106,7 @@ import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import java.util.function.Supplier;
/**
* Actual implementation of multi-client InputMethodManagerService.
@@ -1588,7 +1591,26 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
public InputBindResult startInputOrWindowGainedFocus(
public void startInputOrWindowGainedFocus(
@StartInputReason int startInputReason,
@Nullable IInputMethodClient client,
@Nullable IBinder windowToken,
@StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode,
int windowFlags,
@Nullable EditorInfo editorInfo,
@Nullable IInputContext inputContext,
@MissingMethodFlags int missingMethods,
int unverifiedTargetSdkVersion,
IInputBindResultResultCallback resultCallback) {
CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () ->
startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
missingMethods, unverifiedTargetSdkVersion));
}
@BinderThread
private InputBindResult startInputOrWindowGainedFocusInternal(
@StartInputReason int startInputReason,
@Nullable IInputMethodClient client,
@Nullable IBinder windowToken,
@@ -1676,8 +1698,7 @@ public final class MultiClientInputMethodManagerService {
clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
inputContext, missingMethods, editorInfo, startInputFlags,
softInputMode, windowHandle);
} catch (RemoteException e) {
}
} catch (RemoteException ignored) { }
break;
}
return InputBindResult.NULL_EDITOR_INFO;
@@ -1708,8 +1729,7 @@ public final class MultiClientInputMethodManagerService {
clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
inputContext, missingMethods, editorInfo, startInputFlags,
softInputMode, windowHandle);
} catch (RemoteException e) {
}
} catch (RemoteException ignored) { }
clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,