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

Commit 134cee27 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

No need to deal with windowTokens

we have a link to the client which is enough to find the views.

Also there was some cases where the windowToken was not updated
properly. This is moot now.

Also: Read a array of views from the client to speed up the
client<->AutofillManager communication.

Fixes: 38070352
Test: CtsAutoFillServiceTestCases
      1 Started autofill, saw fill UI
      2 Home button
      3 Kill activity in background
      4 Recents -> back to activity
      5 Saw fill UI restored
Change-Id: I7c2c9411204fa5d65867efae9b7296399121c3a2
parent ff6d6d94
Loading
Loading
Loading
Loading
+48 −43
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;

import android.annotation.CallSuper;
@@ -3146,19 +3147,6 @@ public class Activity extends ContextThemeWrapper
    public void onWindowFocusChanged(boolean hasFocus) {
    }

    /**
     * Called before {@link #onAttachedToWindow}.
     *
     * @hide
     */
    @CallSuper
    public void onBeforeAttachedToWindow() {
        if (mAutoFillResetNeeded) {
            getAutofillManager().onAttachedToWindow(
                    getWindow().getDecorView().getRootView().getWindowToken());
        }
    }

    /**
     * Called when the main window associated with the activity has been
     * attached to the window manager.
@@ -7471,35 +7459,51 @@ public class Activity extends ContextThemeWrapper
    }

    /** @hide */
    @Override
    public boolean getViewVisibility(int viewId) {
        Window window = getWindow();
        if (window == null) {
            Log.i(TAG, "no window");
            return false;
    @NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
        final View[] views = new View[viewIds.length];
        final ArrayList<ViewRootImpl> roots =
                WindowManagerGlobal.getInstance().getRootViews(getActivityToken());

        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
            final View rootView = roots.get(rootNum).getView();

            if (rootView != null) {
                for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
                    if (views[viewNum] == null) {
                        views[viewNum] = rootView.findViewByAccessibilityIdTraversal(
                                viewIds[viewNum]);
                    }
                }
            }
        }

        View decorView = window.peekDecorView();
        if (decorView == null) {
            Log.i(TAG, "no decorView");
            return false;
        return views;
    }

        View view = decorView.findViewByAccessibilityIdTraversal(viewId);
    /** @hide */
    @Override
    @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
        final boolean[] isVisible = new boolean[viewIds.length];
        final View views[] = findViewsByAccessibilityIdTraversal(viewIds);

        for (int i = 0; i < viewIds.length; i++) {
            View view = views[i];
            if (view == null) {
            Log.i(TAG, "cannot find view");
            return false;
                isVisible[i] = false;
                continue;
            }

            isVisible[i] = true;

            // Check if the view is visible by checking all parents
        while (view != null) {
            if (view == decorView) {
            while (true) {
                if (view instanceof DecorView && view.getViewRootImpl() == view.getParent()) {
                    break;
                }

                if (view.getVisibility() != View.VISIBLE) {
                Log.i(TAG, view + " is not visible");
                return false;
                    isVisible[i] = false;
                    break;
                }

                if (view.getParent() instanceof View) {
@@ -7508,8 +7512,9 @@ public class Activity extends ContextThemeWrapper
                    break;
                }
            }
        }

        return true;
        return isVisible;
    }

    /** @hide */
+0 −7
Original line number Diff line number Diff line
@@ -479,13 +479,6 @@ public abstract class Window {
         */
        public void onWindowFocusChanged(boolean hasFocus);

        /**
         * @hide
         */
        default void onBeforeAttachedToWindow() {
            // empty
        }

        /**
         * Called when the window has been attached to the window manager.
         * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
+0 −5
Original line number Diff line number Diff line
@@ -108,11 +108,6 @@ public class WindowCallbackWrapper implements Window.Callback {
        mWrapped.onWindowFocusChanged(hasFocus);
    }

    @Override
    public void onBeforeAttachedToWindow() {
        mWrapped.onBeforeAttachedToWindow();
    }

    @Override
    public void onAttachedToWindow() {
        mWrapped.onAttachedToWindow();
+111 −84
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.content.IntentSender;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
@@ -38,7 +37,6 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManagerGlobal;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -47,6 +45,7 @@ import com.android.internal.logging.nano.MetricsProto;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@@ -188,11 +187,11 @@ public final class AutofillManager {
        boolean autofillCallbackRequestHideFillUi();

        /**
         * Checks if the view is currently attached and visible.
         * Checks if views are currently attached and visible.
         *
         * @return {@code true} iff the view is attached or visible
         * @return And array with {@code true} iff the view is attached or visible
         */
        boolean getViewVisibility(int viewId);
        @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);

        /**
         * Checks is the client is currently visible as understood by autofill.
@@ -200,6 +199,15 @@ public final class AutofillManager {
         * @return {@code true} if the client is currently visible
         */
        boolean isVisibleForAutofill();

        /**
         * Find views by traversing the hierarchies of the client.
         *
         * @param viewIds The accessibility ids of the views to find
         *
         * @return And array containing the views, or {@code null} if not found
         */
        @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
    }

    /**
@@ -258,30 +266,6 @@ public final class AutofillManager {
        }
    }

    /**
     * Set window future popup windows should be attached to.
     *
     * @param windowToken The window the popup windows should be attached to
     *
     * {@hide}
     */
    public void onAttachedToWindow(@NonNull IBinder windowToken) {
        if (!hasAutofillFeature()) {
            return;
        }
        synchronized (mLock) {
            if (mSessionId == NO_SESSION) {
                return;
            }

            try {
                mService.setWindow(mSessionId, windowToken);
            } catch (RemoteException e) {
                Log.e(TAG, "Could not attach window to session " + mSessionId);
            }
        }
    }

    /**
     * Called once the client becomes visible.
     *
@@ -292,7 +276,7 @@ public final class AutofillManager {
    public void onVisibleForAutofill() {
        synchronized (mLock) {
            if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
                mTrackedViews.onVisibleForAutofill();
                mTrackedViews.onVisibleForAutofillLocked();
            }
        }
    }
@@ -406,7 +390,7 @@ public final class AutofillManager {

                if (mSessionId == NO_SESSION) {
                    // Starts new session.
                    startSessionLocked(id, view.getWindowToken(), null, value, flags);
                    startSessionLocked(id, null, value, flags);
                } else {
                    // Update focus on existing session.
                    updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
@@ -484,7 +468,7 @@ public final class AutofillManager {

                if (mSessionId == NO_SESSION) {
                    // Starts new session.
                    startSessionLocked(id, view.getWindowToken(), bounds, null, flags);
                    startSessionLocked(id, bounds, null, flags);
                } else {
                    // Update focus on existing session.
                    updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
@@ -725,15 +709,15 @@ public final class AutofillManager {
        return new AutofillId(parent.getAccessibilityViewId(), childId);
    }

    private void startSessionLocked(@NonNull AutofillId id, @NonNull IBinder windowToken,
            @NonNull Rect bounds, @NonNull AutofillValue value, int flags) {
    private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
            @NonNull AutofillValue value, int flags) {
        if (sVerbose) {
            Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
                    + ", flags=" + flags);
        }

        try {
            mSessionId = mService.startSession(mContext.getActivityToken(), windowToken,
            mSessionId = mService.startSession(mContext.getActivityToken(),
                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                    mCallback != null, flags, mContext.getOpPackageName());
            final AutofillClient client = getClientLocked();
@@ -855,9 +839,9 @@ public final class AutofillManager {
        }
    }

    private void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id, int width,
            int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
        final View anchor = findAchorView(windowToken, id);
    private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
            Rect anchorBounds, IAutofillWindowPresenter presenter) {
        final View anchor = findView(id);
        if (anchor == null) {
            return;
        }
@@ -930,27 +914,27 @@ public final class AutofillManager {
        }
    }

    private void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
            List<AutofillValue> values) {
    private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
        synchronized (mLock) {
            if (sessionId != mSessionId) {
                return;
            }

            final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
            if (root == null) {
            final AutofillClient client = getClientLocked();
            if (client == null) {
                return;
            }

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

            for (int i = 0; i < itemCount; i++) {
                final AutofillId id = ids.get(i);
                final AutofillValue value = values.get(i);
                final int viewId = id.getViewId();
                final View view = root.findViewByAccessibilityIdTraversal(viewId);
                final View view = views[i];
                if (view == null) {
                    Log.w(TAG, "autofill(): no View with id " + viewId);
                    continue;
@@ -1022,8 +1006,11 @@ public final class AutofillManager {
        }
    }

    private void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
        final View anchor = findAchorView(windowToken, id);
    private void requestHideFillUi(int sessionId, AutofillId id) {
        final View anchor = findView(id);
        if (anchor == null) {
            return;
        }

        AutofillCallback callback = null;
        synchronized (mLock) {
@@ -1049,8 +1036,11 @@ public final class AutofillManager {
        }
    }

    private void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
        final View anchor = findAchorView(windowToken, id);
    private void notifyNoFillUi(int sessionId, AutofillId id) {
        final View anchor = findView(id);
        if (anchor == null) {
            return;
        }

        AutofillCallback callback = null;
        synchronized (mLock) {
@@ -1070,18 +1060,38 @@ public final class AutofillManager {
        }
    }

    private View findAchorView(IBinder windowToken, AutofillId id) {
        final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
        if (root == null) {
            Log.w(TAG, "no window with token " + windowToken);
            return null;
    /**
     * Get an array of viewIds from a List of {@link AutofillId}.
     *
     * @param autofillIds The autofill ids to convert
     *
     * @return The array of viewIds.
     */
    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
        final int numIds = autofillIds.size();
        final int[] viewIds = new int[numIds];
        for (int i = 0; i < numIds; i++) {
            viewIds[i] = autofillIds.get(i).getViewId();
        }
        final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
        if (view == null) {
            Log.w(TAG, "no view with id " + id);

        return viewIds;
    }

    /**
     * Find a single view by its id.
     *
     * @param autofillId The autofill id of the view
     *
     * @return The view or {@code null} if view was not found
     */
    private View findView(@NonNull AutofillId autofillId) {
        final AutofillClient client = getClientLocked();

        if (client == null) {
            return null;
        }
        return view;

        return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
    }

    /** @hide */
@@ -1160,22 +1170,26 @@ public final class AutofillManager {
         *
         * @param trackedIds The views to be tracked
         */
        TrackedViews(@NonNull List<AutofillId> trackedIds) {
        TrackedViews(List<AutofillId> trackedIds) {
            mVisibleTrackedIds = null;
            mInvisibleTrackedIds = null;

            AutofillClient client = getClientLocked();
            if (trackedIds != null) {
                int numIds = trackedIds.size();
                for (int i = 0; i < numIds; i++) {
                    AutofillId id = trackedIds.get(i);
            if (trackedIds != null && client != null) {
                final boolean[] isVisible;

                    boolean isVisible = true;
                    if (client != null && client.isVisibleForAutofill()) {
                        isVisible = client.getViewVisibility(id.getViewId());
                if (client.isVisibleForAutofill()) {
                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
                } else {
                    // All false
                    isVisible = new boolean[trackedIds.size()];
                }

                    if (isVisible) {
                final int numIds = trackedIds.size();
                for (int i = 0; i < numIds; i++) {
                    final AutofillId id = trackedIds.get(i);

                    if (isVisible[i]) {
                        mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
                    } else {
                        mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
@@ -1233,16 +1247,23 @@ public final class AutofillManager {
         *
         * @see AutofillClient#isVisibleForAutofill()
         */
        void onVisibleForAutofill() {
            // The visibility of the views might have changed while the client was not started,
        void onVisibleForAutofillLocked() {
            // The visibility of the views might have changed while the client was not be visible,
            // hence update the visibility state for all views.
            AutofillClient client = getClientLocked();
            ArraySet<AutofillId> updatedVisibleTrackedIds = null;
            ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
            if (client != null) {
                if (mInvisibleTrackedIds != null) {
                    for (AutofillId id : mInvisibleTrackedIds) {
                        if (client.getViewVisibility(id.getViewId())) {
                    final ArrayList<AutofillId> orderedInvisibleIds =
                            new ArrayList<>(mInvisibleTrackedIds);
                    final boolean[] isVisible = client.getViewVisibility(
                            getViewIds(orderedInvisibleIds));

                    final int numInvisibleTrackedIds = orderedInvisibleIds.size();
                    for (int i = 0; i < numInvisibleTrackedIds; i++) {
                        final AutofillId id = orderedInvisibleIds.get(i);
                        if (isVisible[i]) {
                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);

                            if (sDebug) {
@@ -1255,8 +1276,16 @@ public final class AutofillManager {
                }

                if (mVisibleTrackedIds != null) {
                    for (AutofillId id : mVisibleTrackedIds) {
                        if (client.getViewVisibility(id.getViewId())) {
                    final ArrayList<AutofillId> orderedVisibleIds =
                            new ArrayList<>(mVisibleTrackedIds);
                    final boolean[] isVisible = client.getViewVisibility(
                            getViewIds(orderedVisibleIds));

                    final int numVisibleTrackedIds = orderedVisibleIds.size();
                    for (int i = 0; i < numVisibleTrackedIds; i++) {
                        final AutofillId id = orderedVisibleIds.get(i);

                        if (isVisible[i]) {
                            updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
                        } else {
                            updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
@@ -1355,12 +1384,11 @@ public final class AutofillManager {
        }

        @Override
        public void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
                List<AutofillValue> values) {
        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.mContext.getMainThreadHandler().post(
                        () -> afm.autofill(sessionId, windowToken, ids, values));
                        () -> afm.autofill(sessionId, ids, values));
            }
        }

@@ -1374,31 +1402,30 @@ public final class AutofillManager {
        }

        @Override
        public void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id,
                int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
                Rect anchorBounds, IAutofillWindowPresenter presenter) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.mContext.getMainThreadHandler().post(
                        () -> afm.requestShowFillUi(sessionId, windowToken, id, width, height,
                                anchorBounds, presenter));
                        () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
                                presenter));
            }
        }

        @Override
        public void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
        public void requestHideFillUi(int sessionId, AutofillId id) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.mContext.getMainThreadHandler().post(
                        () -> afm.requestHideFillUi(sessionId, windowToken, id));
                        () -> afm.requestHideFillUi(sessionId, id));
            }
        }

        @Override
        public void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
        public void notifyNoFillUi(int sessionId, AutofillId id) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.mContext.getMainThreadHandler().post(
                        () -> afm.notifyNoFillUi(sessionId, windowToken, id));
                afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id));
            }
        }

+3 −4
Original line number Diff line number Diff line
@@ -32,12 +32,11 @@ import android.view.autofill.IAutoFillManagerClient;
interface IAutoFillManager {
    // Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
    int addClient(in IAutoFillManagerClient client, int userId);
    int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback,
            in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
            boolean hasCallback, int flags, String packageName);
    int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
            in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
            String packageName);
    FillEventHistory getFillEventHistory();
    boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
    void setWindow(int sessionId, in IBinder windowToken);
    void updateSession(int sessionId, in AutofillId id, in Rect bounds,
            in AutofillValue value, int action, int flags, int userId);
    void finishSession(int sessionId, int userId);
Loading