diff options
6 files changed, 183 insertions, 7 deletions
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ac984dacdef7..60ccf67249b7 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -3095,6 +3095,10 @@ public final class AutofillManager { client.autofillClientRunOnUiThread(runnable); } + private void setFillDialogTriggerIds(@Nullable List<AutofillId> ids) { + mFillDialogTriggerIds = ids; + } + /** * Checks the id of autofill whether supported the fill dialog. * @@ -3819,6 +3823,13 @@ public final class AutofillManager { new FillCallback(callback, id)); } } + + public void notifyFillDialogTriggerIds(List<AutofillId> ids) { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.setFillDialogTriggerIds(ids)); + } + } } private static final class AugmentedAutofillManagerClient diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 64507aac54cb..2e5967cc32d1 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -148,4 +148,9 @@ oneway interface IAutoFillManagerClient { */ void requestFillFromClient(int id, in InlineSuggestionsRequest request, in IFillCallback callback); + + /** + * Notifies autofill ids that require to show the fill dialog. + */ + void notifyFillDialogTriggerIds(in List<AutofillId> ids); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index aa42e8deb581..1cff3744687e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -284,7 +284,9 @@ final class AutofillManagerServiceImpl } final Session session = mSessions.get(sessionId); if (session != null && uid == session.uid) { - session.setAuthenticationResultLocked(data, authenticationId); + synchronized (session.mLock) { + session.setAuthenticationResultLocked(data, authenticationId); + } } } @@ -374,7 +376,9 @@ final class AutofillManagerServiceImpl + " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly; mMaster.logRequestLocked(historyItem); - newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); + synchronized (newSession.mLock) { + newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags); + } if (forAugmentedAutofillOnly) { // Must embed the flag in the response, at the high-end side of the long. diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index a4bf52a3ed1b..095c1fc80d3e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -17,6 +17,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; +import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; @@ -149,9 +150,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID"; + final Object mLock; + private final AutofillManagerServiceImpl mService; private final Handler mHandler; - private final Object mLock; private final AutoFillUI mUi; private final MetricsLogger mMetricsLogger = new MetricsLogger(); @@ -428,6 +430,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ @GuardedBy("mLock") private boolean mClientSuggestionsEnabled; + + /** Whether the fill dialog UI is disabled. */ + @GuardedBy("mLock") + private boolean mFillDialogDisabled; } /** @@ -860,7 +866,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.getRemoteInlineSuggestionRenderServiceLocked(); if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled) && remoteRenderService != null - && isViewFocusedLocked(flags)) { + && (isViewFocusedLocked(flags) || (isRequestFromActivityStarted(flags)))) { final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer; if (mSessionFlags.mClientSuggestionsEnabled) { final int finalRequestId = requestId; @@ -906,6 +912,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState requestAssistStructureLocked(requestId, flags); } + private boolean isRequestFromActivityStarted(int flags) { + return (flags & FLAG_ACTIVITY_START) != 0; + } + @GuardedBy("mLock") private void requestAssistStructureLocked(int requestId, int flags) { try { @@ -1501,6 +1511,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState this, intentSender, intent)); } + // AutoFillUiCallback + @Override + public void requestShowSoftInput(AutofillId id) { + IAutoFillManagerClient client = getClient(); + if (client != null) { + try { + client.requestShowSoftInput(id); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending input show up notification", e); + } + } + synchronized (Session.this.mLock) { + // stop to show fill dialog + mSessionFlags.mFillDialogDisabled = true; + } + } + private void notifyFillUiHidden(@NonNull AutofillId autofillId) { synchronized (mLock) { try { @@ -2869,6 +2896,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // View is triggering autofill. mCurrentViewId = viewState.id; viewState.update(value, virtualBounds, flags); + if (!isRequestFromActivityStarted(flags)) { + mSessionFlags.mFillDialogDisabled = true; + } requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); break; case ACTION_VALUE_CHANGED: @@ -2958,6 +2988,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (isSameViewEntered) { + setFillDialogDisabledAndStartInput(); return; } @@ -2968,6 +2999,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (Objects.equals(mCurrentViewId, viewState.id)) { if (sVerbose) Slog.v(TAG, "Exiting view " + id); mUi.hideFillUi(this); + mUi.hideFillDialog(this); hideAugmentedAutofillLocked(viewState); // We don't send an empty response to IME so that it doesn't cause UI flicker // on the IME side if it arrives before the input view is finished on the IME. @@ -3148,6 +3180,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + if (requestShowFillDialog(response, filledId, filterText)) { + synchronized (mLock) { + final ViewState currentView = mViewStates.get(mCurrentViewId); + currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN); + mService.logDatasetShown(id, mClientState); + } + return; + } + + setFillDialogDisabled(); + if (response.supportsInlineSuggestions()) { synchronized (mLock) { if (requestShowInlineSuggestionsLocked(response, filterText)) { @@ -3192,6 +3235,81 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + @GuardedBy("mLock") + private void updateFillDialogTriggerIdsLocked() { + final FillResponse response = getLastResponseLocked(null); + + if (response == null) return; + + final AutofillId[] ids = response.getFillDialogTriggerIds(); + notifyClientFillDialogTriggerIds(ids == null ? null : Arrays.asList(ids)); + } + + private void notifyClientFillDialogTriggerIds(List<AutofillId> fieldIds) { + try { + if (sVerbose) { + Slog.v(TAG, "notifyFillDialogTriggerIds(): " + fieldIds); + } + getClient().notifyFillDialogTriggerIds(fieldIds); + } catch (RemoteException e) { + Slog.w(TAG, "Cannot set trigger ids for fill dialog", e); + } + } + + private boolean isFillDialogUiEnabled() { + // TODO read from Settings or somewhere + final boolean isSettingsEnabledFillDialog = true; + synchronized (Session.this.mLock) { + return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled; + } + } + + private void setFillDialogDisabled() { + synchronized (mLock) { + mSessionFlags.mFillDialogDisabled = true; + } + notifyClientFillDialogTriggerIds(null); + } + + private void setFillDialogDisabledAndStartInput() { + if (getUiForShowing().isFillDialogShowing()) { + setFillDialogDisabled(); + final AutofillId id; + synchronized (mLock) { + id = mCurrentViewId; + } + requestShowSoftInput(id); + } + } + + private boolean requestShowFillDialog(FillResponse response, + AutofillId filledId, String filterText) { + if (!isFillDialogUiEnabled()) { + // Unsupported fill dialog UI + return false; + } + + final AutofillId[] ids = response.getFillDialogTriggerIds(); + if (ids == null || !ArrayUtils.contains(ids, filledId)) { + return false; + } + + final Drawable serviceIcon = getServiceIcon(); + + getUiForShowing().showFillDialog(filledId, response, filterText, + mService.getServicePackageName(), mComponentName, serviceIcon, this); + return true; + } + + @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. + private Drawable getServiceIcon() { + synchronized (mLock) { + return mService.getServiceIconLocked(); + } + } + /** * Returns whether we made a request to show inline suggestions. */ @@ -3584,9 +3702,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService != null && (mSessionFlags.mAugmentedAutofillOnly - || !mSessionFlags.mInlineSupportedByService - || mSessionFlags.mExpiredResponse) - && isViewFocusedLocked(flags)) { + || !mSessionFlags.mInlineSupportedByService + || mSessionFlags.mExpiredResponse) + && isViewFocusedLocked(flags) + || isFillDialogUiEnabled()) { if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback( (extras) -> { @@ -3642,6 +3761,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClientState = newClientState != null ? newClientState : newResponse.getClientState(); setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false); + updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); if (mCurrentViewId == null) { diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index adb1e3e43731..4a14f1420cf2 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -82,6 +82,8 @@ final class ViewState { public static final int STATE_INLINE_DISABLED = 0x8000; /** The View is waiting for an inline suggestions request from IME.*/ public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000; + /** Fill dialog were shown for this View. */ + public static final int STATE_FILL_DIALOG_SHOWN = 0x20000; public final AutofillId id; diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 71c3c16a2c06..47fd68c25705 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -90,6 +90,7 @@ public final class AutoFillUI { void startIntentSender(IntentSender intentSender, Intent intent); void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent); void cancelSession(); + void requestShowSoftInput(AutofillId id); } public AutoFillUI(@NonNull Context context) { @@ -155,6 +156,12 @@ public final class AutoFillUI { } /** + * Hides the fill UI. + */ + public void hideFillDialog(@NonNull AutoFillUiCallback callback) { + mHandler.post(() -> hideFillDialogUiThread(callback)); + } + /** * Filters the options in the fill UI. * * @param filterText The filter prefix. @@ -369,6 +376,21 @@ public final class AutoFillUI { } /** + * Shows the UI asking the user to choose for autofill. + */ + public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response, + @Nullable String filterText, @Nullable String servicePackageName, + @NonNull ComponentName componentName, @Nullable Drawable serviceIcon, + @NonNull AutoFillUiCallback callback) { + if (sVerbose) { + Slog.v(TAG, "showFillDialog for " + + componentName.toShortString() + ": " + response); + } + + // TODO show fill dialog + } + + /** * Executes an operation in the pending save UI, if any. */ public void onPendingSaveUi(int operation, @NonNull IBinder token) { @@ -400,6 +422,11 @@ public final class AutoFillUI { return mSaveUi == null ? false : mSaveUi.isShowing(); } + public boolean isFillDialogShowing() { + // TODO + return false; + } + public void dump(PrintWriter pw) { pw.println("Autofill UI"); final String prefix = " "; @@ -442,6 +469,11 @@ public final class AutoFillUI { } @android.annotation.UiThread + private void hideFillDialogUiThread(@Nullable AutoFillUiCallback callback) { + // TODO + } + + @android.annotation.UiThread private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) { if (mSaveUi == null) { // Calling destroySaveUiUiThread() twice is normal - it usually happens when the @@ -475,12 +507,14 @@ public final class AutoFillUI { private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi, @Nullable AutoFillUiCallback callback, boolean notifyClient) { hideFillUiUiThread(callback, notifyClient); + hideFillDialogUiThread(callback); destroySaveUiUiThread(pendingSaveUi, notifyClient); } @android.annotation.UiThread private void hideAllUiThread(@Nullable AutoFillUiCallback callback) { hideFillUiUiThread(callback, true); + hideFillDialogUiThread(callback); final PendingUi pendingSaveUi = hideSaveUiUiThread(callback); if (pendingSaveUi != null && pendingSaveUi.getState() == PendingUi.STATE_FINISHED) { if (sDebug) { |