Loading services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +54 −17 Original line number Diff line number Diff line Loading @@ -17,17 +17,17 @@ package com.android.server.autofill; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.os.Bundle; import android.os.Handler; import android.view.autofill.AutofillId; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.annotations.GuardedBy; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.util.Collections; import java.util.Optional; import java.util.function.Consumer; Loading @@ -46,8 +46,12 @@ final class AutofillInlineSessionController { @NonNull private final Handler mHandler; @Nullable @GuardedBy("mLock") private AutofillInlineSuggestionsRequestSession mSession; @Nullable @GuardedBy("mLock") private InlineFillUi mInlineFillUi; AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal, int userId, ComponentName componentName, Handler handler, Object lock) { Loading @@ -72,16 +76,16 @@ final class AutofillInlineSessionController { // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { // Send an empty response to IME and destroy the existing session. mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(), new InlineSuggestionsResponse(Collections.EMPTY_LIST)); mSession.onInlineSuggestionsResponseLocked( InlineFillUi.emptyUi(mSession.getAutofillIdLocked())); mSession.destroySessionLocked(); mInlineFillUi = null; } // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the // same field. mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId, mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras); mSession.onCreateInlineSuggestionsRequestLocked(); } /** Loading @@ -101,30 +105,63 @@ final class AutofillInlineSessionController { /** * Requests the IME to hide the current suggestions, if any. Returns true if the message is sent * to the IME. * to the IME. This only hides the UI temporarily. For example if user starts typing/deleting * characters, new filterText will kick in and may revive the suggestion UI. */ @GuardedBy("mLock") boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) { if (mSession != null) { return mSession.onInlineSuggestionsResponseLocked(autofillId, new InlineSuggestionsResponse(Collections.EMPTY_LIST)); return mSession.onInlineSuggestionsResponseLocked(InlineFillUi.emptyUi(autofillId)); } return false; } /** * Permanently delete the current inline fill UI. Notify the IME to hide the suggestions as * well. */ @GuardedBy("mLock") boolean deleteInlineFillUiLocked(@NonNull AutofillId autofillId) { mInlineFillUi = null; return hideInlineSuggestionsUiLocked(autofillId); } /** * Updates the inline fill UI with the filter text. It'll send updated inline suggestions to * the IME. */ @GuardedBy("mLock") boolean filterInlineFillUiLocked(@NonNull AutofillId autofillId, @Nullable String filterText) { if (mInlineFillUi != null && mInlineFillUi.getAutofillId().equals(autofillId)) { mInlineFillUi.setFilterText(filterText); return requestImeToShowInlineSuggestionsLocked(); } return false; } /** * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused * on the {@code autofillId}. * Set the current inline fill UI. It'll request the IME to show the inline suggestions when * the IME becomes visible and is focused on the {@code autofillId}. * * @return false if there is no session, or if the IME callback is not available in the session. * @return false if the suggestions are not sent to IME because there is no session, or if the * IME callback is not available in the session. */ @GuardedBy("mLock") boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) { // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { return mSession.onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse); boolean setInlineFillUiLocked(@NonNull InlineFillUi inlineFillUi) { mInlineFillUi = inlineFillUi; return requestImeToShowInlineSuggestionsLocked(); } /** * Sends the suggestions from the current inline fill UI to the IME. * * @return false if the suggestions are not sent to IME because there is no session, or if the * IME callback is not available in the session. */ @GuardedBy("mLock") private boolean requestImeToShowInlineSuggestionsLocked() { if (mSession != null && mInlineFillUi != null) { return mSession.onInlineSuggestionsResponseLocked(mInlineFillUi); } return false; } Loading services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +27 −26 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.content.ComponentName; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.inputmethod.InlineSuggestionsRequest; Loading @@ -37,7 +36,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.IInlineSuggestionsResponseCallback; import com.android.internal.view.InlineSuggestionsRequestInfo; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.lang.ref.WeakReference; Loading Loading @@ -106,7 +105,7 @@ final class AutofillInlineSuggestionsRequestSession { private boolean mImeInputViewStarted; @GuardedBy("mLock") @Nullable private InlineSuggestionsResponse mInlineSuggestionsResponse; private InlineFillUi mInlineFillUi; @GuardedBy("mLock") private boolean mPreviousResponseIsNotEmpty; Loading Loading @@ -156,18 +155,20 @@ final class AutofillInlineSuggestionsRequestSession { * @return false if the IME callback is not available. */ @GuardedBy("mLock") boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) { boolean onInlineSuggestionsResponseLocked(@NonNull InlineFillUi inlineFillUi) { if (mDestroyed) { return false; } if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId); if (sDebug) { Slog.d(TAG, "onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId()); } if (mImeRequest == null || mResponseCallback == null) { return false; } // TODO(b/151123764): each session should only correspond to one field. mAutofillId = autofillId; mInlineSuggestionsResponse = inlineSuggestionsResponse; mAutofillId = inlineFillUi.getAutofillId(); mInlineFillUi = inlineFillUi; maybeUpdateResponseToImeLocked(); return true; } Loading @@ -191,12 +192,12 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId, new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras), new InlineSuggestionsRequestCallbackImpl(this)); mTimeoutCallback = () -> { Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); Slog.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); handleOnReceiveImeRequest(null, null); }; mHandler.postDelayed(mTimeoutCallback, CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS); Loading @@ -207,7 +208,7 @@ final class AutofillInlineSuggestionsRequestSession { */ @GuardedBy("mLock") private void maybeUpdateResponseToImeLocked() { if (sVerbose) Log.v(TAG, "maybeUpdateResponseToImeLocked called"); if (sVerbose) Slog.v(TAG, "maybeUpdateResponseToImeLocked called"); if (mDestroyed || mResponseCallback == null) { return; } Loading @@ -217,18 +218,19 @@ final class AutofillInlineSuggestionsRequestSession { // Although the inline suggestions should disappear when IME hides which removes them // from the view hierarchy, but we still send an empty response to be extra safe. if (sVerbose) Log.v(TAG, "Send empty inline response"); if (sVerbose) Slog.v(TAG, "Send empty inline response"); updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections.EMPTY_LIST)); mPreviousResponseIsNotEmpty = false; } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId, } else if (mImeInputViewStarted && mInlineFillUi != null && match(mAutofillId, mImeCurrentFieldId)) { // 2. if IME is visible, and response is not null, send the response boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty(); InlineSuggestionsResponse response = mInlineFillUi.getInlineSuggestionsResponse(); boolean isEmptyResponse = response.getInlineSuggestions().isEmpty(); if (isEmptyResponse && !mPreviousResponseIsNotEmpty) { // No-op if both the previous response and current response are empty. return; } updateResponseToImeUncheckLocked(mInlineSuggestionsResponse); updateResponseToImeUncheckLocked(response); mPreviousResponseIsNotEmpty = !isEmptyResponse; } } Loading @@ -241,10 +243,9 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } if (sDebug) Log.d(TAG, "Send inline response: " + response.getInlineSuggestions().size()); if (sDebug) Slog.d(TAG, "Send inline response: " + response.getInlineSuggestions().size()); try { mResponseCallback.onInlineSuggestionsResponse(mAutofillId, InlineSuggestionFactory.copy(response)); mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response); } catch (RemoteException e) { Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME"); } Loading @@ -264,7 +265,7 @@ final class AutofillInlineSuggestionsRequestSession { mImeRequestReceived = true; if (mTimeoutCallback != null) { if (sVerbose) Log.v(TAG, "removing timeout callback"); if (sVerbose) Slog.v(TAG, "removing timeout callback"); mHandler.removeCallbacks(mTimeoutCallback); mTimeoutCallback = null; } Loading Loading @@ -335,7 +336,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override public void onInlineSuggestionsUnsupported() throws RemoteException { if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called."); if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called."); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -348,7 +349,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInlineSuggestionsRequest(InlineSuggestionsRequest request, IInlineSuggestionsResponseCallback callback) { if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request); if (sDebug) Slog.d(TAG, "onInlineSuggestionsRequest() received: " + request); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -359,7 +360,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException { if (sVerbose) Log.v(TAG, "onInputMethodStartInput() received on " + imeFieldId); if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -371,14 +372,14 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException { if (sVerbose) { Log.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult); Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult); } } @BinderThread @Override public void onInputMethodStartInputView() { if (sVerbose) Log.v(TAG, "onInputMethodStartInputView() received"); if (sVerbose) Slog.v(TAG, "onInputMethodStartInputView() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -390,7 +391,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override public void onInputMethodFinishInputView() { if (sVerbose) Log.v(TAG, "onInputMethodFinishInputView() received"); if (sVerbose) Slog.v(TAG, "onInputMethodFinishInputView() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -401,7 +402,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodFinishInput() throws RemoteException { if (sVerbose) Log.v(TAG, "onInputMethodFinishInput() received"); if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +17 −18 Original line number Diff line number Diff line Loading @@ -48,17 +48,15 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; Loading Loading @@ -149,7 +147,7 @@ final class RemoteAugmentedAutofillService int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { long requestTime = SystemClock.elapsedRealtime(); Loading @@ -173,7 +171,8 @@ final class RemoteAugmentedAutofillService mCallbacks.resetLastResponse(); maybeRequestShowInlineSuggestions(sessionId, inlineSuggestionsRequest, inlineSuggestionsData, clientState, focusedId, inlineSuggestionsCallback, clientState, focusedId, focusedValue, inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService); requestAutofill.complete(null); } Loading Loading @@ -239,8 +238,8 @@ final class RemoteAugmentedAutofillService private void maybeRequestShowInlineSuggestions(int sessionId, @Nullable InlineSuggestionsRequest request, @Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState, @NonNull AutofillId focusedId, @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty() Loading @@ -250,10 +249,14 @@ final class RemoteAugmentedAutofillService } mCallbacks.setLastResponse(sessionId); final InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse( request, inlineSuggestionsData, focusedId, new InlineSuggestionFactory.InlineSuggestionUiCallback() { final String filterText = focusedValue != null && focusedValue.isText() ? focusedValue.getTextValue().toString() : null; final InlineFillUi inlineFillUi = InlineFillUi.forAugmentedAutofill( request, inlineSuggestionsData, focusedId, filterText, new InlineFillUi.InlineSuggestionUiCallback() { @Override public void autofill(Dataset dataset) { mCallbacks.logAugmentedAutofillSelected(sessionId, Loading @@ -265,8 +268,8 @@ final class RemoteAugmentedAutofillService && fieldIds.get(0).equals(focusedId); client.autofill(sessionId, fieldIds, dataset.getFieldValues(), hideHighlight); inlineSuggestionsCallback.apply(new InlineSuggestionsResponse( Collections.EMPTY_LIST)); inlineSuggestionsCallback.apply( InlineFillUi.emptyUi(focusedId)); } catch (RemoteException e) { Slog.w(TAG, "Encounter exception autofilling the values"); } Loading @@ -283,11 +286,7 @@ final class RemoteAugmentedAutofillService } }, onErrorCallback, remoteRenderService); if (inlineSuggestionsResponse == null) { Slog.w(TAG, "InlineSuggestionFactory created null response"); return; } if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) { if (inlineSuggestionsCallback.apply(inlineFillUi)) { mCallbacks.logAugmentedAutofillShown(sessionId, clientState); } } Loading services/autofill/java/com/android/server/autofill/Session.java +23 −26 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutofillWindowPresenter; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; Loading @@ -102,7 +101,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; Loading Loading @@ -2662,10 +2661,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } else if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) { requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText); if ((viewState.getState() & ViewState.STATE_INLINE_DISABLED) != 0) { final FillResponse response = viewState.getResponse(); if (response != null) { response.getDatasets().clear(); } mInlineSessionController.deleteInlineFillUiLocked(viewState.id); } else { mInlineSessionController.filterInlineFillUiLocked(mCurrentViewId, filterText); } } else if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { if (!TextUtils.isEmpty(filterText)) { // TODO: we should be able to replace this with controller#filterInlineFillUiLocked // to accomplish filtering for augmented autofill. mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); } } Loading Loading @@ -2816,26 +2825,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } final ViewState currentView = mViewStates.get(focusedId); if ((currentView.getState() & ViewState.STATE_INLINE_DISABLED) != 0) { response.getDatasets().clear(); } InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createInlineSuggestionsResponse( inlineSuggestionsRequest.get(), response, filterText, focusedId, this, () -> { InlineFillUi inlineFillUi = InlineFillUi.forAutofill( inlineSuggestionsRequest.get(), response, focusedId, filterText, /*uiCallback*/this, /*onErrorCallback*/ () -> { synchronized (mLock) { mInlineSessionController.hideInlineSuggestionsUiLocked( focusedId); } }, remoteRenderService); if (inlineSuggestionsResponse == null) { Slog.w(TAG, "InlineSuggestionFactory created null response"); return false; } return mInlineSessionController.onInlineSuggestionsResponseLocked(focusedId, inlineSuggestionsResponse); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); } boolean isDestroyed() { Loading Loading @@ -3119,11 +3117,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = mCurrentViewId; final Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsResponseCallback = final Function<InlineFillUi, Boolean> inlineSuggestionsResponseCallback = response -> { synchronized (mLock) { return mInlineSessionController.onInlineSuggestionsResponseLocked( focusedId, response); return mInlineSessionController.setInlineFillUiLocked(response); } }; final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill = Loading services/autofill/java/com/android/server/autofill/ui/FillUi.java +3 −3 Original line number Diff line number Diff line Loading @@ -130,7 +130,7 @@ final class FillUi { } FillUi(@NonNull Context context, @NonNull FillResponse response, @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, @NonNull AutofillId focusedViewId, @Nullable String filterText, @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) { if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode); Loading Loading
services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +54 −17 Original line number Diff line number Diff line Loading @@ -17,17 +17,17 @@ package com.android.server.autofill; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.os.Bundle; import android.os.Handler; import android.view.autofill.AutofillId; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.annotations.GuardedBy; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.util.Collections; import java.util.Optional; import java.util.function.Consumer; Loading @@ -46,8 +46,12 @@ final class AutofillInlineSessionController { @NonNull private final Handler mHandler; @Nullable @GuardedBy("mLock") private AutofillInlineSuggestionsRequestSession mSession; @Nullable @GuardedBy("mLock") private InlineFillUi mInlineFillUi; AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal, int userId, ComponentName componentName, Handler handler, Object lock) { Loading @@ -72,16 +76,16 @@ final class AutofillInlineSessionController { // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { // Send an empty response to IME and destroy the existing session. mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(), new InlineSuggestionsResponse(Collections.EMPTY_LIST)); mSession.onInlineSuggestionsResponseLocked( InlineFillUi.emptyUi(mSession.getAutofillIdLocked())); mSession.destroySessionLocked(); mInlineFillUi = null; } // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the // same field. mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId, mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras); mSession.onCreateInlineSuggestionsRequestLocked(); } /** Loading @@ -101,30 +105,63 @@ final class AutofillInlineSessionController { /** * Requests the IME to hide the current suggestions, if any. Returns true if the message is sent * to the IME. * to the IME. This only hides the UI temporarily. For example if user starts typing/deleting * characters, new filterText will kick in and may revive the suggestion UI. */ @GuardedBy("mLock") boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) { if (mSession != null) { return mSession.onInlineSuggestionsResponseLocked(autofillId, new InlineSuggestionsResponse(Collections.EMPTY_LIST)); return mSession.onInlineSuggestionsResponseLocked(InlineFillUi.emptyUi(autofillId)); } return false; } /** * Permanently delete the current inline fill UI. Notify the IME to hide the suggestions as * well. */ @GuardedBy("mLock") boolean deleteInlineFillUiLocked(@NonNull AutofillId autofillId) { mInlineFillUi = null; return hideInlineSuggestionsUiLocked(autofillId); } /** * Updates the inline fill UI with the filter text. It'll send updated inline suggestions to * the IME. */ @GuardedBy("mLock") boolean filterInlineFillUiLocked(@NonNull AutofillId autofillId, @Nullable String filterText) { if (mInlineFillUi != null && mInlineFillUi.getAutofillId().equals(autofillId)) { mInlineFillUi.setFilterText(filterText); return requestImeToShowInlineSuggestionsLocked(); } return false; } /** * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused * on the {@code autofillId}. * Set the current inline fill UI. It'll request the IME to show the inline suggestions when * the IME becomes visible and is focused on the {@code autofillId}. * * @return false if there is no session, or if the IME callback is not available in the session. * @return false if the suggestions are not sent to IME because there is no session, or if the * IME callback is not available in the session. */ @GuardedBy("mLock") boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) { // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { return mSession.onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse); boolean setInlineFillUiLocked(@NonNull InlineFillUi inlineFillUi) { mInlineFillUi = inlineFillUi; return requestImeToShowInlineSuggestionsLocked(); } /** * Sends the suggestions from the current inline fill UI to the IME. * * @return false if the suggestions are not sent to IME because there is no session, or if the * IME callback is not available in the session. */ @GuardedBy("mLock") private boolean requestImeToShowInlineSuggestionsLocked() { if (mSession != null && mInlineFillUi != null) { return mSession.onInlineSuggestionsResponseLocked(mInlineFillUi); } return false; } Loading
services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +27 −26 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.content.ComponentName; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.inputmethod.InlineSuggestionsRequest; Loading @@ -37,7 +36,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.IInlineSuggestionsResponseCallback; import com.android.internal.view.InlineSuggestionsRequestInfo; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.lang.ref.WeakReference; Loading Loading @@ -106,7 +105,7 @@ final class AutofillInlineSuggestionsRequestSession { private boolean mImeInputViewStarted; @GuardedBy("mLock") @Nullable private InlineSuggestionsResponse mInlineSuggestionsResponse; private InlineFillUi mInlineFillUi; @GuardedBy("mLock") private boolean mPreviousResponseIsNotEmpty; Loading Loading @@ -156,18 +155,20 @@ final class AutofillInlineSuggestionsRequestSession { * @return false if the IME callback is not available. */ @GuardedBy("mLock") boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) { boolean onInlineSuggestionsResponseLocked(@NonNull InlineFillUi inlineFillUi) { if (mDestroyed) { return false; } if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId); if (sDebug) { Slog.d(TAG, "onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId()); } if (mImeRequest == null || mResponseCallback == null) { return false; } // TODO(b/151123764): each session should only correspond to one field. mAutofillId = autofillId; mInlineSuggestionsResponse = inlineSuggestionsResponse; mAutofillId = inlineFillUi.getAutofillId(); mInlineFillUi = inlineFillUi; maybeUpdateResponseToImeLocked(); return true; } Loading @@ -191,12 +192,12 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId); mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId, new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras), new InlineSuggestionsRequestCallbackImpl(this)); mTimeoutCallback = () -> { Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); Slog.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); handleOnReceiveImeRequest(null, null); }; mHandler.postDelayed(mTimeoutCallback, CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS); Loading @@ -207,7 +208,7 @@ final class AutofillInlineSuggestionsRequestSession { */ @GuardedBy("mLock") private void maybeUpdateResponseToImeLocked() { if (sVerbose) Log.v(TAG, "maybeUpdateResponseToImeLocked called"); if (sVerbose) Slog.v(TAG, "maybeUpdateResponseToImeLocked called"); if (mDestroyed || mResponseCallback == null) { return; } Loading @@ -217,18 +218,19 @@ final class AutofillInlineSuggestionsRequestSession { // Although the inline suggestions should disappear when IME hides which removes them // from the view hierarchy, but we still send an empty response to be extra safe. if (sVerbose) Log.v(TAG, "Send empty inline response"); if (sVerbose) Slog.v(TAG, "Send empty inline response"); updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections.EMPTY_LIST)); mPreviousResponseIsNotEmpty = false; } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId, } else if (mImeInputViewStarted && mInlineFillUi != null && match(mAutofillId, mImeCurrentFieldId)) { // 2. if IME is visible, and response is not null, send the response boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty(); InlineSuggestionsResponse response = mInlineFillUi.getInlineSuggestionsResponse(); boolean isEmptyResponse = response.getInlineSuggestions().isEmpty(); if (isEmptyResponse && !mPreviousResponseIsNotEmpty) { // No-op if both the previous response and current response are empty. return; } updateResponseToImeUncheckLocked(mInlineSuggestionsResponse); updateResponseToImeUncheckLocked(response); mPreviousResponseIsNotEmpty = !isEmptyResponse; } } Loading @@ -241,10 +243,9 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed) { return; } if (sDebug) Log.d(TAG, "Send inline response: " + response.getInlineSuggestions().size()); if (sDebug) Slog.d(TAG, "Send inline response: " + response.getInlineSuggestions().size()); try { mResponseCallback.onInlineSuggestionsResponse(mAutofillId, InlineSuggestionFactory.copy(response)); mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response); } catch (RemoteException e) { Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME"); } Loading @@ -264,7 +265,7 @@ final class AutofillInlineSuggestionsRequestSession { mImeRequestReceived = true; if (mTimeoutCallback != null) { if (sVerbose) Log.v(TAG, "removing timeout callback"); if (sVerbose) Slog.v(TAG, "removing timeout callback"); mHandler.removeCallbacks(mTimeoutCallback); mTimeoutCallback = null; } Loading Loading @@ -335,7 +336,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override public void onInlineSuggestionsUnsupported() throws RemoteException { if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called."); if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called."); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -348,7 +349,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInlineSuggestionsRequest(InlineSuggestionsRequest request, IInlineSuggestionsResponseCallback callback) { if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request); if (sDebug) Slog.d(TAG, "onInlineSuggestionsRequest() received: " + request); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -359,7 +360,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException { if (sVerbose) Log.v(TAG, "onInputMethodStartInput() received on " + imeFieldId); if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -371,14 +372,14 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException { if (sVerbose) { Log.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult); Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult); } } @BinderThread @Override public void onInputMethodStartInputView() { if (sVerbose) Log.v(TAG, "onInputMethodStartInputView() received"); if (sVerbose) Slog.v(TAG, "onInputMethodStartInputView() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -390,7 +391,7 @@ final class AutofillInlineSuggestionsRequestSession { @BinderThread @Override public void onInputMethodFinishInputView() { if (sVerbose) Log.v(TAG, "onInputMethodFinishInputView() received"); if (sVerbose) Slog.v(TAG, "onInputMethodFinishInputView() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading @@ -401,7 +402,7 @@ final class AutofillInlineSuggestionsRequestSession { @Override public void onInputMethodFinishInput() throws RemoteException { if (sVerbose) Log.v(TAG, "onInputMethodFinishInput() received"); if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received"); final AutofillInlineSuggestionsRequestSession session = mSession.get(); if (session != null) { session.mHandler.sendMessage(obtainMessage( Loading
services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +17 −18 Original line number Diff line number Diff line Loading @@ -48,17 +48,15 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; Loading Loading @@ -149,7 +147,7 @@ final class RemoteAugmentedAutofillService int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { long requestTime = SystemClock.elapsedRealtime(); Loading @@ -173,7 +171,8 @@ final class RemoteAugmentedAutofillService mCallbacks.resetLastResponse(); maybeRequestShowInlineSuggestions(sessionId, inlineSuggestionsRequest, inlineSuggestionsData, clientState, focusedId, inlineSuggestionsCallback, clientState, focusedId, focusedValue, inlineSuggestionsCallback, client, onErrorCallback, remoteRenderService); requestAutofill.complete(null); } Loading Loading @@ -239,8 +238,8 @@ final class RemoteAugmentedAutofillService private void maybeRequestShowInlineSuggestions(int sessionId, @Nullable InlineSuggestionsRequest request, @Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState, @NonNull AutofillId focusedId, @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty() Loading @@ -250,10 +249,14 @@ final class RemoteAugmentedAutofillService } mCallbacks.setLastResponse(sessionId); final InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse( request, inlineSuggestionsData, focusedId, new InlineSuggestionFactory.InlineSuggestionUiCallback() { final String filterText = focusedValue != null && focusedValue.isText() ? focusedValue.getTextValue().toString() : null; final InlineFillUi inlineFillUi = InlineFillUi.forAugmentedAutofill( request, inlineSuggestionsData, focusedId, filterText, new InlineFillUi.InlineSuggestionUiCallback() { @Override public void autofill(Dataset dataset) { mCallbacks.logAugmentedAutofillSelected(sessionId, Loading @@ -265,8 +268,8 @@ final class RemoteAugmentedAutofillService && fieldIds.get(0).equals(focusedId); client.autofill(sessionId, fieldIds, dataset.getFieldValues(), hideHighlight); inlineSuggestionsCallback.apply(new InlineSuggestionsResponse( Collections.EMPTY_LIST)); inlineSuggestionsCallback.apply( InlineFillUi.emptyUi(focusedId)); } catch (RemoteException e) { Slog.w(TAG, "Encounter exception autofilling the values"); } Loading @@ -283,11 +286,7 @@ final class RemoteAugmentedAutofillService } }, onErrorCallback, remoteRenderService); if (inlineSuggestionsResponse == null) { Slog.w(TAG, "InlineSuggestionFactory created null response"); return; } if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) { if (inlineSuggestionsCallback.apply(inlineFillUi)) { mCallbacks.logAugmentedAutofillShown(sessionId, clientState); } } Loading
services/autofill/java/com/android/server/autofill/Session.java +23 −26 Original line number Diff line number Diff line Loading @@ -94,7 +94,6 @@ import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutofillWindowPresenter; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; Loading @@ -102,7 +101,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.InlineSuggestionFactory; import com.android.server.autofill.ui.InlineFillUi; import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; Loading Loading @@ -2662,10 +2661,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } else if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) { requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText); if ((viewState.getState() & ViewState.STATE_INLINE_DISABLED) != 0) { final FillResponse response = viewState.getResponse(); if (response != null) { response.getDatasets().clear(); } mInlineSessionController.deleteInlineFillUiLocked(viewState.id); } else { mInlineSessionController.filterInlineFillUiLocked(mCurrentViewId, filterText); } } else if (viewState.id.equals(this.mCurrentViewId) && (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { if (!TextUtils.isEmpty(filterText)) { // TODO: we should be able to replace this with controller#filterInlineFillUiLocked // to accomplish filtering for augmented autofill. mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); } } Loading Loading @@ -2816,26 +2825,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } final ViewState currentView = mViewStates.get(focusedId); if ((currentView.getState() & ViewState.STATE_INLINE_DISABLED) != 0) { response.getDatasets().clear(); } InlineSuggestionsResponse inlineSuggestionsResponse = InlineSuggestionFactory.createInlineSuggestionsResponse( inlineSuggestionsRequest.get(), response, filterText, focusedId, this, () -> { InlineFillUi inlineFillUi = InlineFillUi.forAutofill( inlineSuggestionsRequest.get(), response, focusedId, filterText, /*uiCallback*/this, /*onErrorCallback*/ () -> { synchronized (mLock) { mInlineSessionController.hideInlineSuggestionsUiLocked( focusedId); } }, remoteRenderService); if (inlineSuggestionsResponse == null) { Slog.w(TAG, "InlineSuggestionFactory created null response"); return false; } return mInlineSessionController.onInlineSuggestionsResponseLocked(focusedId, inlineSuggestionsResponse); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); } boolean isDestroyed() { Loading Loading @@ -3119,11 +3117,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = mCurrentViewId; final Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsResponseCallback = final Function<InlineFillUi, Boolean> inlineSuggestionsResponseCallback = response -> { synchronized (mLock) { return mInlineSessionController.onInlineSuggestionsResponseLocked( focusedId, response); return mInlineSessionController.setInlineFillUiLocked(response); } }; final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill = Loading
services/autofill/java/com/android/server/autofill/ui/FillUi.java +3 −3 Original line number Diff line number Diff line Loading @@ -130,7 +130,7 @@ final class FillUi { } FillUi(@NonNull Context context, @NonNull FillResponse response, @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, @NonNull AutofillId focusedViewId, @Nullable String filterText, @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) { if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode); Loading