Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5be0f158 authored by Adam He's avatar Adam He
Browse files

Response and Dataset authentication via inline autofill.

Bug: 148815880
Test: manual verification
Change-Id: Ic72f6d828521737db5ce890c0e79cd6f8d16a6c6
parent 677db186
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -43048,6 +43048,7 @@ package android.service.autofill {
    method @NonNull public android.service.autofill.FillResponse build();
    method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
    method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
    method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
    method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
+2 −1
Original line number Diff line number Diff line
@@ -1907,6 +1907,7 @@ public class Activity extends ContextThemeWrapper
            if (!mAutoFillIgnoreFirstResumePause) {
                View focus = getCurrentFocus();
                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
                    // TODO(b/148815880): Bring up keyboard if resumed from inline authentication.
                    // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                    // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                    // window visibility after recreation is INVISIBLE in onResume() and next frame
@@ -8467,7 +8468,7 @@ public class Activity extends ContextThemeWrapper
    /** @hide */
    @Override
    public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
            Intent fillInIntent) {
            Intent fillInIntent, boolean authenticateInline) {
        try {
            startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
                    authenticationId, fillInIntent, 0, 0, null);
+78 −1
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ public final class FillResponse implements Parcelable {
    private final @Nullable SaveInfo mSaveInfo;
    private final @Nullable Bundle mClientState;
    private final @Nullable RemoteViews mPresentation;
    private final @Nullable InlinePresentation mInlinePresentation;
    private final @Nullable RemoteViews mHeader;
    private final @Nullable RemoteViews mFooter;
    private final @Nullable IntentSender mAuthentication;
@@ -95,6 +96,7 @@ public final class FillResponse implements Parcelable {
        mSaveInfo = builder.mSaveInfo;
        mClientState = builder.mClientState;
        mPresentation = builder.mPresentation;
        mInlinePresentation = builder.mInlinePresentation;
        mHeader = builder.mHeader;
        mFooter = builder.mFooter;
        mAuthentication = builder.mAuthentication;
@@ -130,6 +132,11 @@ public final class FillResponse implements Parcelable {
        return mPresentation;
    }

    /** @hide */
    public @Nullable InlinePresentation getInlinePresentation() {
        return mInlinePresentation;
    }

    /** @hide */
    public @Nullable RemoteViews getHeader() {
        return mHeader;
@@ -219,6 +226,7 @@ public final class FillResponse implements Parcelable {
        private SaveInfo mSaveInfo;
        private Bundle mClientState;
        private RemoteViews mPresentation;
        private InlinePresentation mInlinePresentation;
        private RemoteViews mHeader;
        private RemoteViews mFooter;
        private IntentSender mAuthentication;
@@ -317,6 +325,67 @@ public final class FillResponse implements Parcelable {
            return this;
        }

        /**
         * Triggers a custom UI before before autofilling the screen with any data set in this
         * response.
         *
         * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
         * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
         * for examples.
         *
         * <p>This method is similar to
         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts
         * an {@link InlinePresentation} presentation which is required for authenticating through
         * the inline autofill flow.
         *
         * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
         * not work with {@link InlinePresentation}.</p>
         *
         * @param authentication Intent to an activity with your authentication flow.
         * @param presentation The presentation to visualize the response.
         * @param inlinePresentation The inlinePresentation to visualize the response inline.
         * @param ids id of Views that when focused will display the authentication UI.
         *
         * @return This builder.
         *
         * @throws IllegalArgumentException if any of the following occurs:
         * <ul>
         *   <li>{@code ids} is {@code null}</li>
         *   <li>{@code ids} is empty</li>
         *   <li>{@code ids} contains a {@code null} element</li>
         *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
         *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
         *   <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li>
         *   <li>both {@code authentication} and {@code inlinePresentation} are
         *   non-{@code null}</li>
         * </ul>
         *
         * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
         * {@link #setFooter(RemoteViews) footer} are already set for this builder.
         *
         * @see android.app.PendingIntent#getIntentSender()
         */
        @NonNull
        public Builder setAuthentication(@NonNull AutofillId[] ids,
                @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
                @Nullable InlinePresentation inlinePresentation) {
            throwIfDestroyed();
            throwIfDisableAutofillCalled();
            if (mHeader != null || mFooter != null) {
                throw new IllegalStateException("Already called #setHeader() or #setFooter()");
            }

            if (authentication == null ^ (presentation == null && inlinePresentation == null)) {
                throw new IllegalArgumentException("authentication and presentation "
                        + "(dropdown or inline), must be both non-null or null");
            }
            mAuthentication = authentication;
            mPresentation = presentation;
            mInlinePresentation = inlinePresentation;
            mAuthenticationIds = assertValid(ids);
            return this;
        }

        /**
         * Specifies views that should not trigger new
         * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
@@ -644,6 +713,8 @@ public final class FillResponse implements Parcelable {
                        break;
                    }
                }
            } else if (mInlinePresentation != null) {
                mSupportsInlineSuggestions = true;
            }

            mDestroyed = true;
@@ -691,6 +762,9 @@ public final class FillResponse implements Parcelable {
        if (mPresentation != null) {
            builder.append(", hasPresentation");
        }
        if (mInlinePresentation != null) {
            builder.append(", hasInlinePresentation");
        }
        if (mHeader != null) {
            builder.append(", hasHeader");
        }
@@ -740,6 +814,7 @@ public final class FillResponse implements Parcelable {
        parcel.writeParcelableArray(mAuthenticationIds, flags);
        parcel.writeParcelable(mAuthentication, flags);
        parcel.writeParcelable(mPresentation, flags);
        parcel.writeParcelable(mInlinePresentation, flags);
        parcel.writeParcelable(mHeader, flags);
        parcel.writeParcelable(mFooter, flags);
        parcel.writeParcelable(mUserData, flags);
@@ -774,8 +849,10 @@ public final class FillResponse implements Parcelable {
                    AutofillId.class);
            final IntentSender authentication = parcel.readParcelable(null);
            final RemoteViews presentation = parcel.readParcelable(null);
            final InlinePresentation inlinePresentation = parcel.readParcelable(null);
            if (authenticationIds != null) {
                builder.setAuthentication(authenticationIds, authentication, presentation);
                builder.setAuthentication(authenticationIds, authentication, presentation,
                        inlinePresentation);
            }
            final RemoteViews header = parcel.readParcelable(null);
            if (header != null) {
+9 −6
Original line number Diff line number Diff line
@@ -86,9 +86,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;

//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;

//TODO: use java.lang.ref.Cleaner once Android supports Java 9

/**
 * <p>The {@link AutofillManager} class provides ways for apps and custom views to
 * integrate with the Autofill Framework lifecycle.
@@ -547,7 +548,7 @@ public final class AutofillManager {
         * @param fillInIntent The authentication fill-in intent.
         */
        void autofillClientAuthenticate(int authenticationId, IntentSender intent,
                Intent fillInIntent);
                Intent fillInIntent, boolean authenticateInline);

        /**
         * Tells the client this manager has state to be reset.
@@ -2070,7 +2071,7 @@ public final class AutofillManager {
    }

    private void authenticate(int sessionId, int authenticationId, IntentSender intent,
            Intent fillInIntent) {
            Intent fillInIntent, boolean authenticateInline) {
        synchronized (mLock) {
            if (sessionId == mSessionId) {
                final AutofillClient client = getClient();
@@ -2078,7 +2079,8 @@ public final class AutofillManager {
                    // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
                    // before onAuthenticationResult()
                    mOnInvisibleCalled = false;
                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
                            authenticateInline);
                }
            }
        }
@@ -3250,10 +3252,11 @@ public final class AutofillManager {

        @Override
        public void authenticate(int sessionId, int authenticationId, IntentSender intent,
                Intent fillInIntent) {
                Intent fillInIntent, boolean authenticateInline) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
                        authenticateInline));
            }
        }

+1 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ oneway interface IAutoFillManagerClient {
      * Authenticates a fill response or a data set.
      */
    void authenticate(int sessionId, int authenticationId, in IntentSender intent,
            in Intent fillInIntent);
            in Intent fillInIntent, boolean authenticateInline);

    /**
      * Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
Loading