diff options
| author | 2022-06-20 14:03:03 +0000 | |
|---|---|---|
| committer | 2022-06-20 14:03:03 +0000 | |
| commit | 8ef7dc586fe08356f27e221ff719ca9e2e0cfc67 (patch) | |
| tree | 9ef5a10a578975d7fd2bbe64c3febcd6387b09ab | |
| parent | 12e72f0affd6345b0ad168a5b272a445b5b06977 (diff) | |
| parent | 6bea050cbfcdfbcf7397d887c891d1ef8abb6fd9 (diff) | |
Merge "Carve out AutofillSuggestionsController in IMF"
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); } } |