summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nikolas Havrikov <havrikov@google.com> 2022-06-20 14:03:03 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-06-20 14:03:03 +0000
commit8ef7dc586fe08356f27e221ff719ca9e2e0cfc67 (patch)
tree9ef5a10a578975d7fd2bbe64c3febcd6387b09ab
parent12e72f0affd6345b0ad168a5b272a445b5b06977 (diff)
parent6bea050cbfcdfbcf7397d887c891d1ef8abb6fd9 (diff)
Merge "Carve out AutofillSuggestionsController in IMF"
-rw-r--r--services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java236
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java197
3 files changed, 247 insertions, 189 deletions
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
new file mode 100644
index 000000000000..0faae35c1a71
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 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.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
+import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
+
+/**
+ * A controller managing autofill suggestion requests.
+ */
+final class AutofillSuggestionsController {
+ private static final boolean DEBUG = false;
+ private static final String TAG = AutofillSuggestionsController.class.getSimpleName();
+
+ @NonNull private final InputMethodManagerService mService;
+ @NonNull private final ArrayMap<String, InputMethodInfo> mMethodMap;
+ @NonNull private final InputMethodUtils.InputMethodSettings mSettings;
+
+ private static final class CreateInlineSuggestionsRequest {
+ @NonNull final InlineSuggestionsRequestInfo mRequestInfo;
+ @NonNull final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull final String mPackageName;
+
+ CreateInlineSuggestionsRequest(
+ @NonNull InlineSuggestionsRequestInfo requestInfo,
+ @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull String packageName) {
+ mRequestInfo = requestInfo;
+ mCallback = callback;
+ mPackageName = packageName;
+ }
+ }
+
+ /**
+ * If a request to create inline autofill suggestions comes in while the IME is unbound
+ * due to {@link InputMethodManagerService#mPreventImeStartupUnlessTextEditor},
+ * this is where it is stored, so that it may be fulfilled once the IME rebinds.
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private CreateInlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+
+ /**
+ * A callback into the autofill service obtained from the latest call to
+ * {@link #onCreateInlineSuggestionsRequest}, which can be used to invalidate an
+ * autofill session in case the IME process dies.
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+
+ AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
+ mService = service;
+ mMethodMap = mService.mMethodMap;
+ mSettings = mService.mSettings;
+ }
+
+ @GuardedBy("ImfLock.class")
+ void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
+ InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
+ boolean touchExplorationEnabled) {
+ clearPendingInlineSuggestionsRequest();
+ mInlineSuggestionsRequestCallback = callback;
+ final InputMethodInfo imi = mMethodMap.get(mService.getSelectedMethodIdLocked());
+ try {
+ if (userId == mSettings.getCurrentUserId()
+ && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
+ mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
+ requestInfo, callback, imi.getPackageName());
+ if (mService.getCurMethodLocked() != null) {
+ // In the normal case when the IME is connected, we can make the request here.
+ performOnCreateInlineSuggestionsRequest();
+ } else {
+ // Otherwise, the next time the IME connection is established,
+ // InputMethodBindingController.mMainConnection#onServiceConnected() will call
+ // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
+ if (DEBUG) {
+ Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
+ }
+ }
+ } else {
+ callback.onInlineSuggestionsUnsupported();
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
+ void performOnCreateInlineSuggestionsRequest() {
+ if (mPendingInlineSuggestionsRequest == null) {
+ return;
+ }
+ IInputMethodInvoker curMethod = mService.getCurMethodLocked();
+ if (DEBUG) {
+ Slog.d(TAG, "Performing onCreateInlineSuggestionsRequest. mCurMethod = " + curMethod);
+ }
+ if (curMethod != null) {
+ final IInlineSuggestionsRequestCallback callback =
+ new InlineSuggestionsRequestCallbackDecorator(
+ mPendingInlineSuggestionsRequest.mCallback,
+ mPendingInlineSuggestionsRequest.mPackageName,
+ mService.getCurTokenDisplayIdLocked(),
+ mService.getCurTokenLocked(),
+ mService);
+ curMethod.onCreateInlineSuggestionsRequest(
+ mPendingInlineSuggestionsRequest.mRequestInfo, callback);
+ } else {
+ Slog.w(TAG, "No IME connected! Abandoning inline suggestions creation request.");
+ }
+ clearPendingInlineSuggestionsRequest();
+ }
+
+ @GuardedBy("ImfLock.class")
+ private void clearPendingInlineSuggestionsRequest() {
+ mPendingInlineSuggestionsRequest = null;
+ }
+
+ private static boolean isInlineSuggestionsEnabled(InputMethodInfo imi,
+ boolean touchExplorationEnabled) {
+ return imi.isInlineSuggestionsEnabled()
+ && (!touchExplorationEnabled
+ || imi.supportsInlineSuggestionsWithTouchExploration());
+ }
+
+ @GuardedBy("ImfLock.class")
+ void invalidateAutofillSession() {
+ if (mInlineSuggestionsRequestCallback != null) {
+ try {
+ mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Cannot invalidate autofill session.", e);
+ }
+ }
+ }
+
+ /**
+ * The decorator which validates the host package name in the
+ * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
+ */
+ private static final class InlineSuggestionsRequestCallbackDecorator
+ extends IInlineSuggestionsRequestCallback.Stub {
+ @NonNull private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull private final String mImePackageName;
+ private final int mImeDisplayId;
+ @NonNull private final IBinder mImeToken;
+ @NonNull private final InputMethodManagerService mImms;
+
+ InlineSuggestionsRequestCallbackDecorator(
+ @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
+ int displayId, @NonNull IBinder imeToken, @NonNull InputMethodManagerService imms) {
+ mCallback = callback;
+ mImePackageName = imePackageName;
+ mImeDisplayId = displayId;
+ mImeToken = imeToken;
+ mImms = imms;
+ }
+
+ @Override
+ public void onInlineSuggestionsUnsupported() throws RemoteException {
+ mCallback.onInlineSuggestionsUnsupported();
+ }
+
+ @Override
+ public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback)
+ throws RemoteException {
+ if (!mImePackageName.equals(request.getHostPackageName())) {
+ throw new SecurityException(
+ "Host package name in the provide request=[" + request.getHostPackageName()
+ + "] doesn't match the IME package name=[" + mImePackageName
+ + "].");
+ }
+ request.setHostDisplayId(mImeDisplayId);
+ mImms.setCurHostInputToken(mImeToken, request.getHostInputToken());
+ mCallback.onInlineSuggestionsRequest(request, callback);
+ }
+
+ @Override
+ public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ mCallback.onInputMethodStartInput(imeFieldId);
+ }
+
+ @Override
+ public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ mCallback.onInputMethodShowInputRequested(requestResult);
+ }
+
+ @Override
+ public void onInputMethodStartInputView() throws RemoteException {
+ mCallback.onInputMethodStartInputView();
+ }
+
+ @Override
+ public void onInputMethodFinishInputView() throws RemoteException {
+ mCallback.onInputMethodFinishInputView();
+ }
+
+ @Override
+ public void onInputMethodFinishInput() throws RemoteException {
+ mCallback.onInputMethodFinishInput();
+ }
+
+ @Override
+ public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ mCallback.onInlineSuggestionsSessionInvalidated();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 9c2368e25226..b63592c2fedb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -77,7 +77,7 @@ final class InputMethodBindingController {
@GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
@GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
- @GuardedBy("ImfLock.class") private IBinder mCurToken;
+ @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
@GuardedBy("ImfLock.class") private int mCurSeq;
@GuardedBy("ImfLock.class") private boolean mVisibleBound;
private boolean mSupportsStylusHw;
@@ -173,6 +173,7 @@ final class InputMethodBindingController {
* identify it in the future.
*/
@GuardedBy("ImfLock.class")
+ @Nullable
IBinder getCurToken() {
return mCurToken;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 04f0726d8250..7c4803b2b477 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -134,9 +134,7 @@ import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.accessibility.AccessibilityManager;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
@@ -157,7 +155,6 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
-import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
@@ -282,39 +279,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@NonNull
private final Set<String> mNonPreemptibleInputMethods;
- private static final class CreateInlineSuggestionsRequest {
- @NonNull final InlineSuggestionsRequestInfo mRequestInfo;
- @NonNull final IInlineSuggestionsRequestCallback mCallback;
- @NonNull final String mPackageName;
-
- CreateInlineSuggestionsRequest(
- @NonNull InlineSuggestionsRequestInfo requestInfo,
- @NonNull IInlineSuggestionsRequestCallback callback,
- @NonNull String packageName) {
- mRequestInfo = requestInfo;
- mCallback = callback;
- mPackageName = packageName;
- }
- }
-
- /**
- * If a request to create inline autofill suggestions comes in while the IME is unbound
- * due to {@link #mPreventImeStartupUnlessTextEditor}, this is where it is stored, so
- * that it may be fulfilled once the IME rebinds.
- */
- @GuardedBy("ImfLock.class")
- @Nullable
- private CreateInlineSuggestionsRequest mPendingInlineSuggestionsRequest;
-
- /**
- * A callback into the autofill service obtained from the latest call to
- * {@link #onCreateInlineSuggestionsRequestLocked}, which can be used to invalidate an
- * autofill session in case the IME process dies.
- */
- @GuardedBy("ImfLock.class")
- @Nullable
- private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
-
@UserIdInt
private int mLastSwitchUserId;
@@ -338,7 +302,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
private final UserManager mUserManager;
private final UserManagerInternal mUserManagerInternal;
private final InputMethodMenuController mMenuController;
- private final InputMethodBindingController mBindingController;
+ @NonNull private final InputMethodBindingController mBindingController;
+ @NonNull private final AutofillSuggestionsController mAutofillController;
// TODO(b/219056452): Use AccessibilityManagerInternal instead.
private final AccessibilityManager mAccessibilityManager;
@@ -717,7 +682,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
* identify it in the future.
*/
@GuardedBy("ImfLock.class")
- private IBinder getCurTokenLocked() {
+ @Nullable
+ IBinder getCurTokenLocked() {
return mBindingController.getCurToken();
}
@@ -758,7 +724,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
@GuardedBy("ImfLock.class")
@Nullable
- private IInputMethodInvoker getCurMethodLocked() {
+ IInputMethodInvoker getCurMethodLocked() {
return mBindingController.getCurMethod();
}
@@ -1794,6 +1760,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mSettings, context);
mMenuController = new InputMethodMenuController(this);
mBindingController = new InputMethodBindingController(this);
+ mAutofillController = new AutofillSuggestionsController(this);
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
mNonPreemptibleInputMethods = Sets.newHashSet(mRes.getStringArray(
@@ -2179,148 +2146,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
- boolean touchExplorationEnabled) {
- clearPendingInlineSuggestionsRequestLocked();
- mInlineSuggestionsRequestCallback = callback;
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
- try {
- if (userId == mSettings.getCurrentUserId()
- && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
- mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
- requestInfo, callback, imi.getPackageName());
- if (getCurMethodLocked() != null) {
- // In the normal case when the IME is connected, we can make the request here.
- performOnCreateInlineSuggestionsRequestLocked();
- } else {
- // Otherwise, the next time the IME connection is established,
- // InputMethodBindingController.mMainConnection#onServiceConnected() will call
- // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
- if (DEBUG) {
- Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
- }
- }
- } else {
- callback.onInlineSuggestionsUnsupported();
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
- }
- }
-
- @GuardedBy("ImfLock.class")
void performOnCreateInlineSuggestionsRequestLocked() {
- if (mPendingInlineSuggestionsRequest == null) {
- return;
- }
- IInputMethodInvoker curMethod = getCurMethodLocked();
- if (DEBUG) {
- Slog.d(TAG, "Performing onCreateInlineSuggestionsRequest. mCurMethod = " + curMethod);
- }
- if (curMethod != null) {
- final IInlineSuggestionsRequestCallback callback =
- new InlineSuggestionsRequestCallbackDecorator(
- mPendingInlineSuggestionsRequest.mCallback,
- mPendingInlineSuggestionsRequest.mPackageName,
- mCurTokenDisplayId, getCurTokenLocked(), this);
- curMethod.onCreateInlineSuggestionsRequest(
- mPendingInlineSuggestionsRequest.mRequestInfo, callback);
- } else {
- Slog.w(TAG, "No IME connected! Abandoning inline suggestions creation request.");
- }
- clearPendingInlineSuggestionsRequestLocked();
- }
-
- @GuardedBy("ImfLock.class")
- private void clearPendingInlineSuggestionsRequestLocked() {
- mPendingInlineSuggestionsRequest = null;
- }
-
- private static boolean isInlineSuggestionsEnabled(InputMethodInfo imi,
- boolean touchExplorationEnabled) {
- return imi.isInlineSuggestionsEnabled()
- && (!touchExplorationEnabled
- || imi.supportsInlineSuggestionsWithTouchExploration());
- }
-
- /**
- * The decorator which validates the host package name in the
- * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
- */
- private static final class InlineSuggestionsRequestCallbackDecorator
- extends IInlineSuggestionsRequestCallback.Stub {
- @NonNull
- private final IInlineSuggestionsRequestCallback mCallback;
- @NonNull
- private final String mImePackageName;
-
- private final int mImeDisplayId;
-
- @NonNull
- private final IBinder mImeToken;
- @NonNull
- private final InputMethodManagerService mImms;
-
- InlineSuggestionsRequestCallbackDecorator(
- @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
- int displayId, @NonNull IBinder imeToken, @NonNull InputMethodManagerService imms) {
- mCallback = callback;
- mImePackageName = imePackageName;
- mImeDisplayId = displayId;
- mImeToken = imeToken;
- mImms = imms;
- }
-
- @Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
- mCallback.onInlineSuggestionsUnsupported();
- }
-
- @Override
- public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
- IInlineSuggestionsResponseCallback callback)
- throws RemoteException {
- if (!mImePackageName.equals(request.getHostPackageName())) {
- throw new SecurityException(
- "Host package name in the provide request=[" + request.getHostPackageName()
- + "] doesn't match the IME package name=[" + mImePackageName
- + "].");
- }
- request.setHostDisplayId(mImeDisplayId);
- mImms.setCurHostInputToken(mImeToken, request.getHostInputToken());
- mCallback.onInlineSuggestionsRequest(request, callback);
- }
-
- @Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
- mCallback.onInputMethodStartInput(imeFieldId);
- }
-
- @Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
- mCallback.onInputMethodShowInputRequested(requestResult);
- }
-
- @Override
- public void onInputMethodStartInputView() throws RemoteException {
- mCallback.onInputMethodStartInputView();
- }
-
- @Override
- public void onInputMethodFinishInputView() throws RemoteException {
- mCallback.onInputMethodFinishInputView();
- }
-
- @Override
- public void onInputMethodFinishInput() throws RemoteException {
- mCallback.onInputMethodFinishInput();
- }
-
- @Override
- public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
- mCallback.onInlineSuggestionsSessionInvalidated();
- }
+ mAutofillController.performOnCreateInlineSuggestionsRequest();
}
/**
@@ -2794,13 +2621,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
void invalidateAutofillSessionLocked() {
- if (mInlineSuggestionsRequestCallback != null) {
- try {
- mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
- } catch (RemoteException e) {
- Slog.e(TAG, "Cannot invalidate autofill session.", e);
- }
- }
+ mAutofillController.invalidateAutofillSession();
}
@GuardedBy("ImfLock.class")
@@ -5511,7 +5332,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
synchronized (ImfLock.class) {
- onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb,
+ mAutofillController.onCreateInlineSuggestionsRequest(userId, requestInfo, cb,
touchExplorationEnabled);
}
}