Loading core/java/android/view/autofill/AutofillManager.java +10 −5 Original line number Diff line number Diff line Loading @@ -2211,12 +2211,17 @@ public final class AutofillManager { * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE} * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill * service disabled further autofill requests for the activity). * @param autofillableIds list of ids that could trigger autofill, use to not handle a new * session when they're entered. */ private void setSessionFinished(int newState) { private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) { synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to " + getStateAsString(newState)); + getStateAsString(newState) + "; autofillableIds=" + autofillableIds); } if (autofillableIds != null) { mEnteredIds = new ArraySet<>(autofillableIds); } if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) { resetSessionLocked(/* resetEnteredIds= */ true); Loading Loading @@ -2328,7 +2333,7 @@ public final class AutofillManager { if (sessionFinishedState != 0) { // Callback call was "hijacked" to also update the session state. setSessionFinished(sessionFinishedState); setSessionFinished(sessionFinishedState, /* autofillableIds= */ null); } } Loading Loading @@ -3114,10 +3119,10 @@ public final class AutofillManager { } @Override public void setSessionFinished(int newState) { public void setSessionFinished(int newState, List<AutofillId> autofillableIds) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.post(() -> afm.setSessionFinished(newState)); afm.post(() -> afm.setSessionFinished(newState, autofillableIds)); } } Loading core/java/android/view/autofill/IAutoFillManagerClient.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -98,8 +98,10 @@ oneway interface IAutoFillManagerClient { * * @param newState STATE_FINISHED (because the autofill service returned a null * FillResponse) or STATE_UNKNOWN (because the session was removed). * @param autofillableIds list of ids that could trigger autofill, use to not handle a new * session when they're entered. */ void setSessionFinished(int newState); void setSessionFinished(int newState, in List<AutofillId> autofillableIds); /** * Gets a reference to the binder object that can be used by the Augmented Autofill service. Loading services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +2 −1 Original line number Diff line number Diff line Loading @@ -299,7 +299,8 @@ final class AutofillManagerServiceImpl final IAutoFillManagerClient client = IAutoFillManagerClient.Stub .asInterface(appCallbackToken); try { client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE); client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, /* autofillableIds= */ null); } catch (RemoteException e) { Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e); } Loading services/autofill/java/com/android/server/autofill/Helper.java +28 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; import android.content.ComponentName; import android.metrics.LogMaker; import android.service.autofill.Dataset; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; Loading Loading @@ -205,6 +207,32 @@ public final class Helper { } } /** * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}. */ @NonNull static ArrayList<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) { final ArrayList<AutofillId> ids = new ArrayList<>(); final int size = structure.getWindowNodeCount(); for (int i = 0; i < size; i++) { final WindowNode node = structure.getWindowNodeAt(i); addAutofillableIds(node.getRootViewNode(), ids); } return ids; } private static void addAutofillableIds(@NonNull ViewNode node, @NonNull ArrayList<AutofillId> ids) { if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { ids.add(node.getAutofillId()); } final int size = node.getChildCount(); for (int i = 0; i < size; i++) { final ViewNode child = node.getChildAt(i); addAutofillableIds(child, ids); } } private interface ViewNodeFilter { boolean matches(ViewNode node); } Loading services/autofill/java/com/android/server/autofill/Session.java +30 −13 Original line number Diff line number Diff line Loading @@ -699,14 +699,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } processNullResponseLocked(requestFlags); processNullResponseLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestFlags); processNullResponseLocked(requestId, requestFlags); return; } } Loading Loading @@ -739,7 +739,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && response.getAuthentication() == null) || disableDuration > 0) { // Response is "empty" from an UI point of view, need to notify client. notifyUnavailableToClient(sessionFinishedState); notifyUnavailableToClient(sessionFinishedState, /* autofillableIds= */ null); } if (requestLog != null) { Loading Loading @@ -802,7 +802,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } } notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED); notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null); if (showMessage) { getUiForShowing().showError(message, this); } Loading Loading @@ -1131,7 +1132,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); processNullResponseLocked(0); processNullResponseLocked(requestId, 0); } } Loading Loading @@ -2428,14 +2429,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } private void notifyUnavailableToClient(int sessionFinishedState) { private void notifyUnavailableToClient(int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { if (mHasCallback) { mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState); } else if (sessionFinishedState != 0) { mClient.setSessionFinished(sessionFinishedState); mClient.setSessionFinished(sessionFinishedState, autofillableIds); } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); Loading Loading @@ -2551,10 +2553,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") private void processNullResponseLocked(int flags) { private void processNullResponseLocked(int requestId, int flags) { if ((flags & FLAG_MANUAL_REQUEST) != 0) { getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this); } final FillContext context = getFillContextByRequestIdLocked(requestId); final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); autofillableIds = Helper.getAutofillableIds(structure); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; } mService.resetLastResponse(); // The default autofill service cannot fullfill the request, let's check if the augmented Loading @@ -2563,18 +2577,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mAugmentedAutofillDestroyer == null) { if (sVerbose) { Slog.v(TAG, "canceling session " + id + " when server returned null and there is no" + " AugmentedAutofill for user"); + " AugmentedAutofill for user. AutofillableIds: " + autofillableIds); } // Nothing to be done, but need to notify client. notifyUnavailableToClient(AutofillManager.STATE_FINISHED); notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds); removeSelf(); } else { // TODO(b/123099468, b/119638958): must set internal state so when user focus other // fields it does not generate a new call to the standard autofill service (right now // it does). Must also add CTS tests to exercise this scenario. // it does). In other words, must also pass the autofillableIds - we'll be handled in // a separate change (with new CTS tests to exercise this scenario). if (sVerbose) { Slog.v(TAG, "keeping session " + id + " when server returned null but " + "there is an AugmentedAutofill for user"); + "there is an AugmentedAutofill for user. AutofillableIds: " + autofillableIds); } } } Loading Loading @@ -3134,7 +3151,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mUi.destroyAll(mPendingSaveUi, this, false); if (!isPendingSaveUi) { try { mClient.setSessionFinished(clientState); mClient.setSessionFinished(clientState, /* autofillableIds= */ null); } catch (RemoteException e) { Slog.e(TAG, "Error notifying client to finish session", e); } Loading Loading
core/java/android/view/autofill/AutofillManager.java +10 −5 Original line number Diff line number Diff line Loading @@ -2211,12 +2211,17 @@ public final class AutofillManager { * when the service failed to fullfil the request, or {@link #STATE_DISABLED_BY_SERVICE} * (because the autofill service or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill * service disabled further autofill requests for the activity). * @param autofillableIds list of ids that could trigger autofill, use to not handle a new * session when they're entered. */ private void setSessionFinished(int newState) { private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) { synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to " + getStateAsString(newState)); + getStateAsString(newState) + "; autofillableIds=" + autofillableIds); } if (autofillableIds != null) { mEnteredIds = new ArraySet<>(autofillableIds); } if (newState == STATE_UNKNOWN_COMPAT_MODE || newState == STATE_UNKNOWN_FAILED) { resetSessionLocked(/* resetEnteredIds= */ true); Loading Loading @@ -2328,7 +2333,7 @@ public final class AutofillManager { if (sessionFinishedState != 0) { // Callback call was "hijacked" to also update the session state. setSessionFinished(sessionFinishedState); setSessionFinished(sessionFinishedState, /* autofillableIds= */ null); } } Loading Loading @@ -3114,10 +3119,10 @@ public final class AutofillManager { } @Override public void setSessionFinished(int newState) { public void setSessionFinished(int newState, List<AutofillId> autofillableIds) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.post(() -> afm.setSessionFinished(newState)); afm.post(() -> afm.setSessionFinished(newState, autofillableIds)); } } Loading
core/java/android/view/autofill/IAutoFillManagerClient.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -98,8 +98,10 @@ oneway interface IAutoFillManagerClient { * * @param newState STATE_FINISHED (because the autofill service returned a null * FillResponse) or STATE_UNKNOWN (because the session was removed). * @param autofillableIds list of ids that could trigger autofill, use to not handle a new * session when they're entered. */ void setSessionFinished(int newState); void setSessionFinished(int newState, in List<AutofillId> autofillableIds); /** * Gets a reference to the binder object that can be used by the Augmented Autofill service. Loading
services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +2 −1 Original line number Diff line number Diff line Loading @@ -299,7 +299,8 @@ final class AutofillManagerServiceImpl final IAutoFillManagerClient client = IAutoFillManagerClient.Stub .asInterface(appCallbackToken); try { client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE); client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, /* autofillableIds= */ null); } catch (RemoteException e) { Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e); } Loading
services/autofill/java/com/android/server/autofill/Helper.java +28 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; import android.content.ComponentName; import android.metrics.LogMaker; import android.service.autofill.Dataset; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; Loading Loading @@ -205,6 +207,32 @@ public final class Helper { } } /** * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}. */ @NonNull static ArrayList<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) { final ArrayList<AutofillId> ids = new ArrayList<>(); final int size = structure.getWindowNodeCount(); for (int i = 0; i < size; i++) { final WindowNode node = structure.getWindowNodeAt(i); addAutofillableIds(node.getRootViewNode(), ids); } return ids; } private static void addAutofillableIds(@NonNull ViewNode node, @NonNull ArrayList<AutofillId> ids) { if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { ids.add(node.getAutofillId()); } final int size = node.getChildCount(); for (int i = 0; i < size; i++) { final ViewNode child = node.getChildAt(i); addAutofillableIds(child, ids); } } private interface ViewNodeFilter { boolean matches(ViewNode node); } Loading
services/autofill/java/com/android/server/autofill/Session.java +30 −13 Original line number Diff line number Diff line Loading @@ -699,14 +699,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } processNullResponseLocked(requestFlags); processNullResponseLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestFlags); processNullResponseLocked(requestId, requestFlags); return; } } Loading Loading @@ -739,7 +739,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && response.getAuthentication() == null) || disableDuration > 0) { // Response is "empty" from an UI point of view, need to notify client. notifyUnavailableToClient(sessionFinishedState); notifyUnavailableToClient(sessionFinishedState, /* autofillableIds= */ null); } if (requestLog != null) { Loading Loading @@ -802,7 +802,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } } notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED); notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null); if (showMessage) { getUiForShowing().showError(message, this); } Loading Loading @@ -1131,7 +1132,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); processNullResponseLocked(0); processNullResponseLocked(requestId, 0); } } Loading Loading @@ -2428,14 +2429,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } private void notifyUnavailableToClient(int sessionFinishedState) { private void notifyUnavailableToClient(int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { if (mHasCallback) { mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState); } else if (sessionFinishedState != 0) { mClient.setSessionFinished(sessionFinishedState); mClient.setSessionFinished(sessionFinishedState, autofillableIds); } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); Loading Loading @@ -2551,10 +2553,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") private void processNullResponseLocked(int flags) { private void processNullResponseLocked(int requestId, int flags) { if ((flags & FLAG_MANUAL_REQUEST) != 0) { getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this); } final FillContext context = getFillContextByRequestIdLocked(requestId); final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); autofillableIds = Helper.getAutofillableIds(structure); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; } mService.resetLastResponse(); // The default autofill service cannot fullfill the request, let's check if the augmented Loading @@ -2563,18 +2577,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mAugmentedAutofillDestroyer == null) { if (sVerbose) { Slog.v(TAG, "canceling session " + id + " when server returned null and there is no" + " AugmentedAutofill for user"); + " AugmentedAutofill for user. AutofillableIds: " + autofillableIds); } // Nothing to be done, but need to notify client. notifyUnavailableToClient(AutofillManager.STATE_FINISHED); notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds); removeSelf(); } else { // TODO(b/123099468, b/119638958): must set internal state so when user focus other // fields it does not generate a new call to the standard autofill service (right now // it does). Must also add CTS tests to exercise this scenario. // it does). In other words, must also pass the autofillableIds - we'll be handled in // a separate change (with new CTS tests to exercise this scenario). if (sVerbose) { Slog.v(TAG, "keeping session " + id + " when server returned null but " + "there is an AugmentedAutofill for user"); + "there is an AugmentedAutofill for user. AutofillableIds: " + autofillableIds); } } } Loading Loading @@ -3134,7 +3151,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mUi.destroyAll(mPendingSaveUi, this, false); if (!isPendingSaveUi) { try { mClient.setSessionFinished(clientState); mClient.setSessionFinished(clientState, /* autofillableIds= */ null); } catch (RemoteException e) { Slog.e(TAG, "Error notifying client to finish session", e); } Loading