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

Commit 52fef1b2 authored by Simranjit Kohli's avatar Simranjit Kohli Committed by Android (Google) Code Review
Browse files

Merge changes I5f492a5f,I79c850b5,I302584ad into main

* changes:
  [Relayout] Implement Logging
  [Relayout] Part 5: Location based fingerprinting.
  [Relayout] Part 4: Implement core logic
parents ecec0dca a34dbfa9
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -582,4 +582,9 @@ public final class AutofillClientController implements AutofillManager.AutofillC
            Log.e(TAG, "authenticate() failed for intent:" + intent, e);
        }
    }

    @Override
    public boolean isActivityResumed() {
        return mActivity.isResumed();
    }
}
+166 −25
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -774,6 +775,13 @@ public final class AutofillManager {
    // dataset in responses. Used to avoid request pre-fill request again and again.
    private final ArraySet<AutofillId> mAllTrackedViews = new ArraySet<>();

    // Whether we need to re-attempt fill again. Needed for case of relayout.
    private boolean mFillReAttemptNeeded = false;

    private Map<Integer, AutofillId> mFingerprintToViewMap = new ArrayMap<>();

    private AutofillStateFingerprint mAutofillStateFingerprint;

    /** @hide */
    public interface AutofillClient {
        /**
@@ -909,6 +917,11 @@ public final class AutofillManager {
         * @return An ID that is unique in the activity.
         */
        @Nullable AutofillId autofillClientGetNextAutofillId();

        /**
         * @return Whether the activity is resumed or not.
         */
        boolean isActivityResumed();
    }

    /**
@@ -919,6 +932,7 @@ public final class AutofillManager {
        mService = service;
        mOptions = context.getAutofillOptions();
        mIsFillRequested = new AtomicBoolean(false);
        mAutofillStateFingerprint = AutofillStateFingerprint.createInstance();

        mIsFillDialogEnabled = AutofillFeatureFlags.isFillDialogEnabled();
        mFillDialogEnabledHints = AutofillFeatureFlags.getFillDialogEnabledHints();
@@ -1357,6 +1371,20 @@ public final class AutofillManager {
            mOnInvisibleCalled = true;

            if (isExpiredResponse) {
                if (mRelayoutFix && isAuthenticationPending()) {
                    Log.i(TAG, "onInvisibleForAutofill(): Ignoring expiringResponse due to pending"
                            + " authentication");
                    try {
                        mService.notifyNotExpiringResponseDuringAuth(
                                mSessionId, mContext.getUserId());
                    } catch (RemoteException e) {
                        // The failure could be a consequence of something going wrong on the
                        // server side. Do nothing here since it's just logging, but it's
                        // possible follow-up actions may fail.
                    }
                    return;
                }
                Log.i(TAG, "onInvisibleForAutofill(): expiringResponse");
                // Notify service the response has expired.
                updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
                        ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
@@ -1512,6 +1540,20 @@ public final class AutofillManager {
        return mState == STATE_PENDING_AUTHENTICATION;
    }

    /**
     * Called to log notify view entered was ignored due to pending auth
     * @hide
     */
    public void notifyViewEnteredIgnoredDuringAuthCount() {
        try {
            mService.notifyViewEnteredIgnoredDuringAuthCount(mSessionId, mContext.getUserId());
        } catch (RemoteException e) {
            // The failure could be a consequence of something going wrong on the
            // server side. Do nothing here since it's just logging, but it's
            // possible follow-up actions may fail.
        }
    }

    /**
     * Called to check if we should retry fill.
     * Useful for knowing whether to attempt refill after relayout.
@@ -1519,8 +1561,9 @@ public final class AutofillManager {
     * @hide
     */
    public boolean shouldRetryFill() {
        // TODO: Implement in follow-up cl
        return false;
        synchronized (mLock) {
            return isAuthenticationPending() && mFillReAttemptNeeded;
        }
    }

    /**
@@ -1531,8 +1574,13 @@ public final class AutofillManager {
     */
    public boolean attemptRefill() {
        Log.i(TAG, "Attempting refill");
        // TODO: Implement in follow-up cl
        return false;
        // Find active autofillable views. Compute their fingerprints
        List<View> autofillableViews =
                getClient().autofillClientFindAutofillableViewsByTraversal();
        if (sDebug) {
            Log.d(TAG, "Autofillable views count:" + autofillableViews.size());
        }
        return mAutofillStateFingerprint.attemptRefill(autofillableViews, this);
    }

    /**
@@ -2493,7 +2541,13 @@ public final class AutofillManager {

    /** @hide */
    public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
        if (sVerbose) {
            Log.v(TAG, "onAuthenticationResult(): authId= " + authenticationId + ", data=" + data);
        }
        if (!hasAutofillFeature()) {
            if (sVerbose) {
                Log.v(TAG, "onAuthenticationResult(): autofill not enabled");
            }
            return;
        }
        // TODO: the result code is being ignored, so this method is not reliably
@@ -2501,10 +2555,6 @@ public final class AutofillManager {
        // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
        // service set the extra and returned RESULT_CANCELED...

        if (sDebug) {
            Log.d(TAG, "onAuthenticationResult(): id= " + authenticationId + ", data=" + data);
        }

        synchronized (mLock) {
            if (!isActiveLocked()) {
                Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
@@ -2661,6 +2711,7 @@ public final class AutofillManager {
            mSessionId = receiver.getIntResult();
            if (mSessionId != NO_SESSION) {
                mState = STATE_ACTIVE;
                mAutofillStateFingerprint.setSessionId(mSessionId);
            }
            final int extraFlags = receiver.getOptionalExtraIntResult(0);
            if ((extraFlags & RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
@@ -2722,6 +2773,9 @@ public final class AutofillManager {
        if (resetEnteredIds) {
            mEnteredIds = null;
        }
        mFillReAttemptNeeded = false;
        mFingerprintToViewMap.clear();
        mAutofillStateFingerprint = AutofillStateFingerprint.createInstance();
    }

    @GuardedBy("mLock")
@@ -2984,8 +3038,12 @@ public final class AutofillManager {
            Intent fillInIntent, boolean authenticateInline) {
        synchronized (mLock) {
            if (sessionId == mSessionId) {
                if (mRelayoutFixDeprecated) {
                if (mRelayoutFixDeprecated || mRelayoutFix) {
                    mState = STATE_PENDING_AUTHENTICATION;
                    if (sVerbose) {
                        Log.v(TAG, "entering STATE_PENDING_AUTHENTICATION : mRelayoutFix:"
                                + mRelayoutFix);
                    }
                }
                final AutofillClient client = getClient();
                if (client != null) {
@@ -3191,17 +3249,56 @@ public final class AutofillManager {

    @GuardedBy("mLock")
    private void handleFailedIdsLocked(@NonNull ArrayList<AutofillId> failedIds) {
        handleFailedIdsLocked(failedIds, null, false, false);
    }

    @GuardedBy("mLock")
    private void handleFailedIdsLocked(@NonNull ArrayList<AutofillId> failedIds,
            ArrayList<AutofillValue> failedAutofillValues, boolean hideHighlight,
            boolean isRefill) {
        if (!failedIds.isEmpty() && sVerbose) {
            Log.v(TAG, "autofill(): total failed views: " + failedIds);
        }

        if (mRelayoutFix && !failedIds.isEmpty()) {
            // Activity isn't in resumed state, so it's very possible that relayout could've
            // occurred, so wait for it to declare proper failure. It's a temporary failure at the
            // moment. We'll try again later when the activity is resumed.

            // The above doesn't seem to be the correct way. Look for pending auth cases.
            // TODO(b/238252288): Check whether there was any auth done at all
            mFillReAttemptNeeded = true;
            mAutofillStateFingerprint.storeFailedIdsAndValues(
                    failedIds, failedAutofillValues, hideHighlight);
        }
        try {
            mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
            mService.setAutofillFailure(mSessionId, failedIds, isRefill, mContext.getUserId());
        } catch (RemoteException e) {
            // In theory, we could ignore this error since it's not a big deal, but
            // in reality, we rather crash the app anyways, as the failure could be
            // a consequence of something going wrong on the server side...
            throw e.rethrowFromSystemServer();
        }
        if (mRelayoutFix && !failedIds.isEmpty()) {
            if (!getClient().isActivityResumed()) {
                if (sVerbose) {
                    Log.v(TAG, "handleFailedIdsLocked(): failed id's exist, but activity not"
                            + " resumed");
                }
            } else {
                if (isRefill) {
                    Log.i(TAG, "handleFailedIdsLocked(): Attempted refill, but failed");
                } else {
                    // activity has been resumed, try to re-fill
                    // getClient().isActivityResumed() && !failedIds.isEmpty() && !isRefill
                    // TODO(b/238252288): Do better state management, and only trigger the following
                    //  if there was auth previously.
                    Log.i(TAG, "handleFailedIdsLocked(): Attempting refill");
                    attemptRefill();
                    mFillReAttemptNeeded = false;
                }
            }
        }
    }

    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
@@ -3216,13 +3313,46 @@ public final class AutofillManager {
                return;
            }

            final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
                    Helper.toArray(ids));

            autofill(views, ids, values, hideHighlight, false);
        }
    }

    void autofill(View[] views, List<AutofillId> ids, List<AutofillValue> values,
            boolean hideHighlight, boolean isRefill) {
        if (sVerbose) {
            Log.v(TAG, "autofill() ids:" + ids + " isRefill:" + isRefill);
        }
        synchronized (mLock) {
            final AutofillClient client = getClient();
            if (client == null) {
                return;
            }

            if (ids == null) {
                Log.i(TAG, "autofill(): No id's to fill");
                return;
            }

            if (mRelayoutFix && isRefill) {
                try {
                    mService.setAutofillIdsAttemptedForRefill(
                            mSessionId, ids, mContext.getUserId());
                } catch (RemoteException e) {
                    // The failure could be a consequence of something going wrong on the
                    // server side. Do nothing here since it's just logging, but it's
                    // possible follow-up actions may fail.
                }
            }

            final int itemCount = ids.size();
            int numApplied = 0;
            ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
            final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
                    Helper.toArray(ids));

            ArrayList<AutofillId> failedIds = new ArrayList<>();
            ArrayList<AutofillValue> failedAutofillValues = new ArrayList<>();

            if (mLastAutofilledData == null) {
                mLastAutofilledData = new ParcelableMap(itemCount);
@@ -3237,7 +3367,9 @@ public final class AutofillManager {
                    // the service; this is fine, but we need to update the view status in the
                    // server side so it can be triggered again.
                    Log.d(TAG, "autofill(): no View with id " + id);
                    // Possible relayout scenario
                    failedIds.add(id);
                    failedAutofillValues.add(value);
                    continue;
                }
                // Mark the view as to be autofilled with 'value'
@@ -3268,7 +3400,8 @@ public final class AutofillManager {
                }
            }

            handleFailedIdsLocked(failedIds);
            handleFailedIdsLocked(
                    failedIds, failedAutofillValues, hideHighlight, isRefill);

            if (virtualValues != null) {
                for (int i = 0; i < virtualValues.size(); i++) {
@@ -3322,7 +3455,7 @@ public final class AutofillManager {
    private void reportAutofillContentFailure(AutofillId id) {
        try {
            mService.setAutofillFailure(mSessionId, Collections.singletonList(id),
                    mContext.getUserId());
                    false /* isRefill */, mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -3359,10 +3492,12 @@ public final class AutofillManager {
     */
    private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
            boolean saveOnAllViewsInvisible, boolean saveOnFinish,
            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
            @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId,
            boolean shouldGrabViewFingerprints) {
        if (saveTriggerId != null) {
            saveTriggerId.resetSessionId();
        }
        final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
        synchronized (mLock) {
            if (sVerbose) {
                Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId
@@ -3372,6 +3507,7 @@ public final class AutofillManager {
                        + ", fillableIds=" + Arrays.toString(fillableIds)
                        + ", saveTrigerId=" + saveTriggerId
                        + ", mFillableIds=" + mFillableIds
                        + ", shouldGrabViewFingerprints=" + shouldGrabViewFingerprints
                        + ", mEnabled=" + mEnabled
                        + ", mSessionId=" + mSessionId);
            }
@@ -3405,7 +3541,6 @@ public final class AutofillManager {
                    trackedIds = null;
                }

                final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
                if (mFillableIds != null) {
                    allFillableIds.addAll(mFillableIds);
                }
@@ -3424,6 +3559,12 @@ public final class AutofillManager {
                    mTrackedViews = null;
                }
            }
            if (mRelayoutFix && shouldGrabViewFingerprints) {
                // For all the views: tracked and others, calculate fingerprints and store them.
                mAutofillStateFingerprint.setUseRelativePosition(mRelativePositionForRelayout);
                mAutofillStateFingerprint.storeStatePriorToAuthentication(
                        getClient(), allFillableIds);
            }
        }
    }

@@ -3845,7 +3986,7 @@ public final class AutofillManager {

    @GuardedBy("mLock")
    private boolean isPendingAuthenticationLocked() {
        return mRelayoutFixDeprecated && mState == STATE_PENDING_AUTHENTICATION;
        return (mRelayoutFixDeprecated || mRelayoutFix) && mState == STATE_PENDING_AUTHENTICATION;
    }

    @GuardedBy("mLock")
@@ -3858,7 +3999,7 @@ public final class AutofillManager {
        return mState == STATE_FINISHED;
    }

    private void post(Runnable runnable) {
    void post(Runnable runnable) {
        final AutofillClient client = getClient();
        if (client == null) {
            if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
@@ -4700,11 +4841,11 @@ public final class AutofillManager {
        @Override
        public void setTrackedViews(int sessionId, AutofillId[] ids,
                boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
                AutofillId saveTriggerId) {
                AutofillId saveTriggerId, boolean shouldGrabViewFingerprints) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
                        saveOnFinish, fillableIds, saveTriggerId));
                        saveOnFinish, fillableIds, saveTriggerId, shouldGrabViewFingerprints));
            }
        }

+352 −0

File added.

Preview size limit exceeded, changes collapsed.

+4 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ oneway interface IAutoFillManager {
        in IResultReceiver result);
    void updateSession(int sessionId, in AutofillId id, in Rect bounds,
        in AutofillValue value, int action, int flags, int userId);
    void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
    void setAutofillFailure(int sessionId, in List<AutofillId> ids, boolean isRefill, int userId);
    void setViewAutofilled(int sessionId, in AutofillId id, int userId);
    void finishSession(int sessionId, int userId, int commitReason);
    void cancelSession(int sessionId, int userId);
@@ -67,4 +67,7 @@ oneway interface IAutoFillManager {
    void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
    void setAugmentedAutofillWhitelist(in List<String> packages, in List<ComponentName> activities,
        in IResultReceiver result);
    void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
    void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
    void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
}
+2 −1
Original line number Diff line number Diff line
@@ -72,7 +72,8 @@ oneway interface IAutoFillManagerClient {
      */
    void setTrackedViews(int sessionId, in @nullable AutofillId[] savableIds,
            boolean saveOnAllViewsInvisible, boolean saveOnFinish,
            in @nullable AutofillId[] fillableIds, in AutofillId saveTriggerId);
            in @nullable AutofillId[] fillableIds, in AutofillId saveTriggerId,
            in boolean shouldGrabViewFingerprints);

    /**
     * Requests showing the fill UI.
Loading