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

Commit 785777bb authored by Felipe Leme's avatar Felipe Leme
Browse files

Inject session id on all AutofillIds kept in the service.

Android Q introduce the FLAG_DELAY_SAVE, which allows the autofill service to delay the SAVE UI by
keeping the session open. But this approach introduces a new problem, as fields from different
activities could have the same AutofillId.

Test: atest MultiScreenLoginTest#testSaveBothFieldsCustomDescription_sameIds
Test: atest AutofillIdTest
Test: atest CtsAutoFillServiceTestCases # sanity check

Fixes: 113593220

Change-Id: Ibd187f5c58c150f820972fcab6d88217294ffe2d
parent 7b307ea7
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -699,6 +699,7 @@ public class AssistStructure implements Parcelable {
        static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS =             0x100;
        static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS =             0x200;
        static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH =          0x400;
        static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID =      0x800;

        int mFlags;
        int mAutofillFlags;
@@ -754,6 +755,9 @@ public class AssistStructure implements Parcelable {
                    } else {
                        mAutofillId = new AutofillId(autofillViewId);
                    }
                    if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
                        mAutofillId.setSessionId(in.readInt());
                    }
                }
                if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
                    mAutofillType = in.readInt();
@@ -899,6 +903,9 @@ public class AssistStructure implements Parcelable {
                if (mAutofillId.isVirtualInt()) {
                    autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID;
                }
                if (mAutofillId.hasSession()) {
                    autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID;
                }
            }
            if (mAutofillValue != null) {
                autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE;
@@ -965,7 +972,9 @@ public class AssistStructure implements Parcelable {
                    if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) {
                        out.writeInt(mAutofillId.getVirtualChildIntId());
                    }
                    // TODO(b/113593220): write session id as well
                    if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) {
                        out.writeInt(mAutofillId.getSessionId());
                    }
                }
                if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) {
                    out.writeInt(mAutofillType);
+22 −3
Original line number Diff line number Diff line
@@ -35,10 +35,10 @@ public final class AutofillId implements Parcelable {
    private static final int FLAG_HAS_SESSION = 0x4;

    private final int mViewId;
    private final int mFlags;
    private int mFlags;
    private final int mVirtualIntId;
    private final long mVirtualLongId;
    private final int mSessionId;
    private int mSessionId;

    /** @hide */
    @TestApi
@@ -72,6 +72,12 @@ public final class AutofillId implements Parcelable {
        mSessionId = sessionId;
    }

    /** @hide */
    public static AutofillId withoutSession(@NonNull AutofillId id) {
        final int flags = id.mFlags & ~FLAG_HAS_SESSION;
        return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION);
    }

    /** @hide */
    public int getViewId() {
        return mViewId;
@@ -136,7 +142,8 @@ public final class AutofillId implements Parcelable {
        return !isVirtualInt() && !isVirtualLong();
    }

    private boolean hasSession() {
    /** @hide */
    public boolean hasSession() {
        return (mFlags & FLAG_HAS_SESSION) != 0;
    }

@@ -145,6 +152,18 @@ public final class AutofillId implements Parcelable {
        return mSessionId;
    }

    /** @hide */
    public void setSessionId(int sessionId) {
        mFlags |= FLAG_HAS_SESSION;
        mSessionId = sessionId;
    }

    /** @hide */
    public void resetSessionId() {
        mFlags &= ~FLAG_HAS_SESSION;
        mSessionId = NO_SESSION;
    }

    /////////////////////////////////
    //  Object "contract" methods. //
    /////////////////////////////////
+11 −0
Original line number Diff line number Diff line
@@ -1139,6 +1139,7 @@ public final class AutofillManager {
        if (mEnteredIds == null) {
            mEnteredIds = new ArraySet<>(1);
        }
        id.resetSessionId();
        mEnteredIds.add(id);
    }

@@ -2177,6 +2178,9 @@ public final class AutofillManager {
    private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
            boolean saveOnAllViewsInvisible, boolean saveOnFinish,
            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
        if (saveTriggerId != null) {
            saveTriggerId.resetSessionId();
        }
        synchronized (mLock) {
            if (sVerbose) {
                Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
@@ -2202,6 +2206,7 @@ public final class AutofillManager {
                        mFillableIds = new ArraySet<>(fillableIds.length);
                    }
                    for (AutofillId id : fillableIds) {
                        id.resetSessionId();
                        mFillableIds.add(id);
                    }
                }
@@ -2264,6 +2269,11 @@ public final class AutofillManager {
     *  session when they're entered.
     */
    private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) {
        if (autofillableIds != null) {
            for (int i = 0; i < autofillableIds.size(); i++) {
                autofillableIds.get(i).resetSessionId();
            }
        }
        synchronized (mLock) {
            if (sVerbose) {
                Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
@@ -2879,6 +2889,7 @@ public final class AutofillManager {
                final int numIds = trackedIds.length;
                for (int i = 0; i < numIds; i++) {
                    final AutofillId id = trackedIds[i];
                    id.resetSessionId();

                    if (isVisible[i]) {
                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
+180 −88
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view.autofill;

import static android.view.autofill.AutofillId.NO_SESSION;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

@@ -34,20 +36,10 @@ public class AutofillIdTest {
    @Test
    public void testNonVirtual() {
        final AutofillId id = new AutofillId(42);
        assertThat(id.getViewId()).isEqualTo(42);
        assertThat(id.isNonVirtual()).isTrue();
        assertThat(id.isVirtualInt()).isFalse();
        assertThat(id.isVirtualLong()).isFalse();
        assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
        assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
        assertNonVirtual(id, 42, NO_SESSION);

        final AutofillId clone = cloneThroughParcel(id);
        assertThat(clone.getViewId()).isEqualTo(42);
        assertThat(clone.isNonVirtual()).isTrue();
        assertThat(clone.isVirtualInt()).isFalse();
        assertThat(clone.isVirtualLong()).isFalse();
        assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID);
        assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID);
        assertNonVirtual(clone, 42, NO_SESSION);
    }

    @Test
@@ -125,84 +117,174 @@ public class AutofillIdTest {
    }

    @Test
    public void testEqualsHashCode() {
        final AutofillId realId = new AutofillId(42);
        final AutofillId realIdSame = new AutofillId(42);
        assertThat(realId).isEqualTo(realIdSame);
        assertThat(realIdSame).isEqualTo(realId);
        assertEqualsIgnoreSession(realId, realIdSame);
        assertEqualsIgnoreSession(realIdSame, realId);
        assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode());

        final AutofillId realIdDifferent = new AutofillId(108);
        assertThat(realId).isNotEqualTo(realIdDifferent);
        assertThat(realIdDifferent).isNotEqualTo(realId);
        assertNotEqualsIgnoreSession(realId, realIdDifferent);
        assertNotEqualsIgnoreSession(realIdDifferent, realId);
        assertThat(realId.hashCode()).isNotEqualTo(realIdDifferent.hashCode());

        final AutofillId virtualId = new AutofillId(42, 1);
        final AutofillId virtualIdSame = new AutofillId(42, 1);
        assertThat(virtualId).isEqualTo(virtualIdSame);
        assertThat(virtualIdSame).isEqualTo(virtualId);
        assertEqualsIgnoreSession(virtualId, virtualIdSame);
        assertEqualsIgnoreSession(virtualIdSame, virtualId);
        assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode());
        assertThat(virtualId).isNotEqualTo(realId);
        assertThat(realId).isNotEqualTo(virtualId);
        assertNotEqualsIgnoreSession(realId, virtualId);
        assertNotEqualsIgnoreSession(virtualId, realId);

        final AutofillId virtualIdDifferentChild = new AutofillId(42, 2);
        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId);
        assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild);
        assertNotEqualsIgnoreSession(virtualIdDifferentChild, virtualId);
        assertNotEqualsIgnoreSession(virtualId, virtualIdDifferentChild);
        assertThat(virtualIdDifferentChild).isNotEqualTo(realId);
        assertThat(realId).isNotEqualTo(virtualIdDifferentChild);
        assertNotEqualsIgnoreSession(virtualIdDifferentChild, realId);
        assertNotEqualsIgnoreSession(realId, virtualIdDifferentChild);

        final AutofillId virtualIdDifferentParent = new AutofillId(108, 1);
        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId);
        assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent);
        assertNotEqualsIgnoreSession(virtualIdDifferentParent, virtualId);
        assertNotEqualsIgnoreSession(virtualId, virtualIdDifferentParent);
        assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild);
        assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent);
        assertNotEqualsIgnoreSession(virtualIdDifferentParent, virtualIdDifferentChild);
        assertNotEqualsIgnoreSession(virtualIdDifferentChild, virtualIdDifferentParent);

        final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108);
        assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId);
        assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession);
        if (false) { // TODO: doesn't work because one object uses int virtual ids, other uses long
            assertEqualsIgnoreSession(virtualIdDifferentSession, virtualId);
            assertEqualsIgnoreSession(virtualId, virtualIdDifferentSession);
        }
        assertThat(virtualIdDifferentSession).isNotEqualTo(realId);
        assertThat(realId).isNotEqualTo(virtualIdDifferentSession);
        assertNotEqualsIgnoreSession(virtualIdDifferentSession, realId);
        assertNotEqualsIgnoreSession(realId, virtualIdDifferentSession);

        final AutofillId sameVirtualIdDifferentSession =
                new AutofillId(new AutofillId(42), 1L, 108);
        assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession);
        assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession);
        assertEqualsIgnoreSession(sameVirtualIdDifferentSession, virtualIdDifferentSession);
        assertEqualsIgnoreSession(virtualIdDifferentSession, sameVirtualIdDifferentSession);
        assertThat(sameVirtualIdDifferentSession.hashCode())
                .isEqualTo(virtualIdDifferentSession.hashCode());
    }

    @Test
    public void testEqualsIgnoreSession() {
        final AutofillId id1 = new AutofillId(new AutofillId(42), 1L, 108);
        final AutofillId id2 = new AutofillId(new AutofillId(42), 1L, 666);
        assertThat(id1).isNotEqualTo(id2);
        assertThat(id2).isNotEqualTo(id1);
        assertEqualsIgnoreSession(id1, id2);
        assertEqualsIgnoreSession(id2, id1);
    public void testFactoryMethod_withoutSession() {
        final AutofillId id = new AutofillId(42);
        id.setSessionId(108);
        assertNonVirtual(id, 42, 108);
        final AutofillId idWithoutSession = AutofillId.withoutSession(id);
        assertNonVirtual(idWithoutSession, 42, NO_SESSION);
    }

    @Test
    public void testSetResetSession() {
        final AutofillId id = new AutofillId(42);
        assertNonVirtual(id, 42, NO_SESSION);
        id.setSessionId(108);
        assertNonVirtual(id, 42, 108);

        final AutofillId clone1 = cloneThroughParcel(id);
        assertNonVirtual(clone1, 42, 108);

        id.resetSessionId();
        assertThat(id.getSessionId()).isEqualTo(NO_SESSION);
        final AutofillId clone2 = cloneThroughParcel(id);
        assertNonVirtual(clone2, 42, NO_SESSION);
    }

    @Test
    public void testEqualsHashCode_nonVirtual_same() {
        final AutofillId id = new AutofillId(42);
        final AutofillId sameId = new AutofillId(42);

        assertThat(id).isEqualTo(sameId);
        assertThat(sameId).isEqualTo(id);
        assertEqualsIgnoreSession(id, sameId);
        assertEqualsIgnoreSession(sameId, id);
        assertThat(id.hashCode()).isEqualTo(sameId.hashCode());
    }

    @Test
    public void testEqualsHashCode_nonVirtual_other() {
        final AutofillId id = new AutofillId(42);
        final AutofillId otherId = new AutofillId(108);

        assertThat(id).isNotEqualTo(otherId);
        assertThat(otherId).isNotEqualTo(id);
        assertNotEqualsIgnoreSession(id, otherId);
        assertNotEqualsIgnoreSession(otherId, id);
        assertThat(id.hashCode()).isNotEqualTo(otherId.hashCode());
    }

    @Test
    public void testEqualsHashCode_virtual_same() {
        final AutofillId id = new AutofillId(42);
        final AutofillId virtual = new AutofillId(42, 1);
        final AutofillId sameVirtual = new AutofillId(42, 1);

        assertThat(virtual).isEqualTo(sameVirtual);
        assertThat(sameVirtual).isEqualTo(virtual);
        assertEqualsIgnoreSession(virtual, sameVirtual);
        assertEqualsIgnoreSession(sameVirtual, virtual);
        assertThat(virtual.hashCode()).isEqualTo(sameVirtual.hashCode());
        assertThat(virtual).isNotEqualTo(id);
        assertThat(id).isNotEqualTo(virtual);
        assertNotEqualsIgnoreSession(id, virtual);
        assertNotEqualsIgnoreSession(virtual, id);
    }

    @Test
    public void testEqualsHashCode_virtual_otherChild() {
        final AutofillId id = new AutofillId(42);
        final AutofillId virtual = new AutofillId(42, 1);
        final AutofillId virtualOtherChild = new AutofillId(42, 2);

        assertThat(virtualOtherChild).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
        assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
        assertThat(virtualOtherChild).isNotEqualTo(id);
        assertThat(id).isNotEqualTo(virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, id);
        assertNotEqualsIgnoreSession(id, virtualOtherChild);
    }

    @Test
    public void testEqualsHashCode_virtual_otherParent() {
        final AutofillId virtual = new AutofillId(42, 1);
        final AutofillId virtualOtherParent = new AutofillId(108, 1);
        final AutofillId virtualOtherChild = new AutofillId(42, 2);

        assertThat(virtualOtherParent).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherParent);
        assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
        assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
        assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
        assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
        assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
    }

    @Test
    public void testEqualsHashCode_virtual_otherSession() {
        final AutofillId virtual = new AutofillId(42, 1);
        final AutofillId virtualOtherSession = new AutofillId(42, 1);
        virtualOtherSession.setSessionId(666);

        assertThat(virtualOtherSession).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherSession);
        assertEqualsIgnoreSession(virtualOtherSession, virtual);
        assertEqualsIgnoreSession(virtual, virtualOtherSession);
    }

    @Test
    public void testEqualsHashCode_virtual_longId_same() {
        final AutofillId hostId = new AutofillId(42);
        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
        final AutofillId sameVirtual = new AutofillId(hostId, 1L, 108);

        assertThat(sameVirtual).isEqualTo(virtual);
        assertThat(virtual).isEqualTo(sameVirtual);
        assertEqualsIgnoreSession(sameVirtual, virtual);
        assertEqualsIgnoreSession(virtual, sameVirtual);
        assertThat(sameVirtual).isNotEqualTo(hostId);
        assertThat(hostId).isNotEqualTo(sameVirtual);
        assertNotEqualsIgnoreSession(sameVirtual, hostId);
        assertNotEqualsIgnoreSession(hostId, sameVirtual);
    }

    @Test
    public void testEqualsHashCode_virtual_longId_otherChild() {
        final AutofillId hostId = new AutofillId(42);
        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
        final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);

        assertThat(virtualOtherChild).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, virtual);
        assertNotEqualsIgnoreSession(virtual, virtualOtherChild);
        assertThat(virtualOtherChild).isNotEqualTo(hostId);
        assertThat(hostId).isNotEqualTo(virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, hostId);
        assertNotEqualsIgnoreSession(hostId, virtualOtherChild);
    }

    @Test
    public void testEqualsHashCode_virtual_longId_otherParent() {
        final AutofillId hostId = new AutofillId(42);
        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
        final AutofillId virtualOtherParent = new AutofillId(new AutofillId(666), 1L, 108);
        final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108);

        assertThat(virtualOtherParent).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherParent);
        assertNotEqualsIgnoreSession(virtualOtherParent, virtual);
        assertNotEqualsIgnoreSession(virtual, virtualOtherParent);
        assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild);
        assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent);
        assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild);
        assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent);
    }

    @Test
    public void testEqualsHashCode_virtual_longId_otherSession() {
        final AutofillId hostId = new AutofillId(42);
        final AutofillId virtual = new AutofillId(hostId, 1L, 108);
        final AutofillId virtualOtherSession = new AutofillId(hostId, 1L, 666);

        assertThat(virtualOtherSession).isNotEqualTo(virtual);
        assertThat(virtual).isNotEqualTo(virtualOtherSession);
        assertEqualsIgnoreSession(virtualOtherSession, virtual);
        assertEqualsIgnoreSession(virtual, virtualOtherSession);
    }

    private AutofillId cloneThroughParcel(AutofillId id) {
@@ -223,17 +305,27 @@ public class AutofillIdTest {
        }
    }

    public static void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
    private void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
        assertWithMessage("id1 is null").that(id1).isNotNull();
        assertWithMessage("id2 is null").that(id2).isNotNull();
        assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
                .isTrue();
    }

    public static void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
    private void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) {
        assertWithMessage("id1 is null").that(id1).isNotNull();
        assertWithMessage("id2 is null").that(id2).isNotNull();
        assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2))
                .isFalse();
    }

    private void assertNonVirtual(AutofillId id, int expectedId, int expectSessionId) {
        assertThat(id.getViewId()).isEqualTo(expectedId);
        assertThat(id.isNonVirtual()).isTrue();
        assertThat(id.isVirtualInt()).isFalse();
        assertThat(id.isVirtualLong()).isFalse();
        assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID);
        assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID);
        assertThat(id.getSessionId()).isEqualTo(expectSessionId);
    }
}
+7 −6
Original line number Diff line number Diff line
@@ -211,25 +211,26 @@ public final class Helper {
     * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}.
     */
    @NonNull
    static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
        final ArraySet<AutofillId> ids = new ArraySet<>();
    static ArrayList<AutofillId> getAutofillIds(@NonNull AssistStructure structure,
            boolean autofillableOnly) {
        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);
            addAutofillableIds(node.getRootViewNode(), ids, autofillableOnly);
        }
        return ids;
    }

    private static void addAutofillableIds(@NonNull ViewNode node,
            @NonNull ArraySet<AutofillId> ids) {
        if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
            @NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) {
        if (!autofillableOnly || 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);
            addAutofillableIds(child, ids, autofillableOnly);
        }
    }

Loading