OSDN Git Service

Fixed Augmented Autofill workflow so the UI is closed when another field is focused.
authorFelipe Leme <felipeal@google.com>
Thu, 14 Mar 2019 22:50:12 +0000 (15:50 -0700)
committerFelipe Leme <felipeal@google.com>
Fri, 15 Mar 2019 18:14:41 +0000 (11:14 -0700)
Test: atest AugmentedLoginActivityTest#testAugmentedAutoFill_multipleRequests \
            AugmentedLoginActivityTest#testAugmentedAutoFill_rotateDevice
Test: atest CtsAutoFillServiceTestCases  # sanity check

Fixes: 128638902

Change-Id: I09d2a87f3dff72afbc339f2c295343419905f45b

core/java/android/service/autofill/augmented/AugmentedAutofillService.java
core/java/android/view/autofill/AutofillManager.java
core/java/android/view/autofill/Helper.java
services/autofill/java/com/android/server/autofill/Helper.java
services/autofill/java/com/android/server/autofill/Session.java
services/autofill/java/com/android/server/autofill/ViewState.java

index cd54930..19e216a 100644 (file)
@@ -405,7 +405,6 @@ public abstract class AugmentedAutofillService extends Service {
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
                 @NonNull IFillCallback callback) {
             synchronized (mLock) {
-                // TODO(b/123099468): should we close the popupwindow if the focused id changed?
                 mFocusedId = focusedId;
                 mFocusedValue = focusedValue;
                 if (mCallback != null) {
index 13efeaf..604cce5 100644 (file)
@@ -20,6 +20,7 @@ import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUE
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
+import static android.view.autofill.Helper.toList;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
@@ -1872,10 +1873,6 @@ public final class AutofillManager {
         }
     }
 
-    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
-        return set == null ? null : new ArrayList<T>(set);
-    }
-
     /**
      * Notifies that a non-autofillable view was entered because the activity is whitelisted for
      * augmented autofill.
index 48d0dbb..2f12bb2 100644 (file)
@@ -19,7 +19,9 @@ package android.view.autofill;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Set;
 
 /** @hide */
 public final class Helper {
@@ -62,7 +64,8 @@ public final class Helper {
     }
 
     /**
-     * Convers a collaction of {@link AutofillId AutofillIds} to an array.
+     * Converts a collaction of {@link AutofillId AutofillIds} to an array.
+     *
      * @param collection The collection.
      * @return The array.
      */
@@ -75,6 +78,14 @@ public final class Helper {
         return array;
     }
 
+    /**
+     * Converts a Set to a List.
+     */
+    @Nullable
+    public static <T> ArrayList<T> toList(@Nullable Set<T> set) {
+        return set == null ? null : new ArrayList<T>(set);
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
index b715637..3d392c7 100644 (file)
@@ -211,8 +211,8 @@ 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<>();
+    static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) {
+        final ArraySet<AutofillId> ids = new ArraySet<>();
         final int size = structure.getWindowNodeCount();
         for (int i = 0; i < size; i++) {
             final WindowNode node = structure.getWindowNodeAt(i);
@@ -222,7 +222,7 @@ public final class Helper {
     }
 
     private static void addAutofillableIds(@NonNull ViewNode node,
-            @NonNull ArrayList<AutofillId> ids) {
+            @NonNull ArraySet<AutofillId> ids) {
         if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) {
             ids.add(node.getAutofillId());
         }
index 8793341..ea47033 100644 (file)
@@ -26,6 +26,7 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
+import static android.view.autofill.Helper.toList;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.getNumericValue;
@@ -266,6 +267,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
     @GuardedBy("mLock")
     private ArrayList<LogMaker> mAugmentedRequestsLogs;
 
+
+    /**
+     * List of autofill ids of autofillable fields present in the AssistStructure that can be used
+     * to trigger new augmented autofill requests (because the "standard" service was not interested
+     * on autofilling the app.
+     */
+    @GuardedBy("mLock")
+    private ArraySet<AutofillId> mAugmentedAutofillableIds;
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -2327,12 +2337,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                     return;
                 }
 
+                if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(id)) {
+                    // View was already reported when server could not handle a response, but it
+                    // triggered augmented autofill
+
+                    if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable");
+
+                    // Update the view states first...
+                    mCurrentViewId = viewState.id;
+                    viewState.setCurrentValue(value);
+
+                    // ...then trigger the augmented autofill UI
+                    triggerAugmentedAutofillLocked();
+                    return;
+                }
+
                 requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
 
                 // Remove the UI if the ViewState has changed.
                 if (!Objects.equals(mCurrentViewId, viewState.id)) {
                     mUi.hideFillUi(this);
                     mCurrentViewId = viewState.id;
+                    hideAugmentedAutofillLocked(viewState);
                 }
 
                 // If the ViewState is ready to be displayed, onReady() will be called.
@@ -2340,8 +2366,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                 break;
             case ACTION_VIEW_EXITED:
                 if (Objects.equals(mCurrentViewId, viewState.id)) {
-                    if (sVerbose) Slog.d(TAG, "Exiting view " + id);
+                    if (sVerbose) Slog.v(TAG, "Exiting view " + id);
                     mUi.hideFillUi(this);
+                    hideAugmentedAutofillLocked(viewState);
                     mCurrentViewId = null;
                 }
                 break;
@@ -2350,6 +2377,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
         }
     }
 
+    @GuardedBy("mLock")
+    private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) {
+        if ((viewState.getState()
+                & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+            viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
+            cancelAugmentedAutofillLocked();
+        }
+    }
+
     /**
      * Checks whether a view should be ignored.
      */
@@ -2430,14 +2466,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
     }
 
     private void notifyUnavailableToClient(int sessionFinishedState,
-            @Nullable ArrayList<AutofillId> autofillableIds) {
+            @Nullable ArraySet<AutofillId> autofillableIds) {
         synchronized (mLock) {
             if (mCurrentViewId == null) return;
             try {
                 if (mHasCallback) {
                     mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState);
                 } else if (sessionFinishedState != 0) {
-                    mClient.setSessionFinished(sessionFinishedState, autofillableIds);
+                    mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds));
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
@@ -2560,7 +2596,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
 
         final FillContext context = getFillContextByRequestIdLocked(requestId);
 
-        final ArrayList<AutofillId> autofillableIds;
+        final ArraySet<AutofillId> autofillableIds;
         if (context != null) {
             final AssistStructure structure = context.getStructure();
             autofillableIds = Helper.getAutofillableIds(structure);
@@ -2583,16 +2619,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
             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). 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. AutofillableIds: "
                         + autofillableIds);
             }
+            mAugmentedAutofillableIds = autofillableIds;
         }
     }
 
@@ -2660,7 +2692,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
             return null;
         }
 
-        final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
+        final ViewState viewState = mViewStates.get(mCurrentViewId);
+        viewState.setState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
+        final AutofillValue currentValue = viewState.getCurrentValue();
 
         // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
         // further AFM -> AFMS calls.
@@ -2682,6 +2716,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
     }
 
     @GuardedBy("mLock")
+    private void cancelAugmentedAutofillLocked() {
+        final RemoteAugmentedAutofillService remoteService = mService
+                .getRemoteAugmentedAutofillServiceLocked();
+        if (remoteService == null) {
+            Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user");
+            return;
+        }
+        if (sVerbose) Slog.v(TAG, "cancelAugmentedAutofillLocked() on " + mCurrentViewId);
+        remoteService.onDestroyAutofillWindowsRequest();
+    }
+
+    @GuardedBy("mLock")
     private void processResponseLocked(@NonNull FillResponse newResponse,
             @Nullable Bundle newClientState, int flags) {
         // Make sure we are hiding the UI which will be shown
@@ -2967,6 +3013,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
             pw.println(mAugmentedRequestsLogs.size());
         }
 
+        if (mAugmentedAutofillableIds != null) {
+            pw.print(prefix); pw.print("mAugmentedAutofillableIds: ");
+            pw.println(mAugmentedAutofillableIds);
+        }
         mRemoteFillService.dump(prefix, pw);
     }
 
index 2cc6d20..33a2e50 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.server.autofill;
 
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+
 import static com.android.server.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
@@ -47,8 +48,6 @@ final class ViewState {
 
     private static final String TAG = "ViewState";
 
-    // NOTE: state constants must be public because of flagstoString().
-    public static final int STATE_UNKNOWN = 0x000;
     /** Initial state. */
     public static final int STATE_INITIAL = 0x001;
     /** View id is present in a dataset returned by the service. */
@@ -73,6 +72,8 @@ final class ViewState {
     public static final int STATE_AUTOFILL_FAILED = 0x400;
     /** View has been autofilled at least once. */
     public static final int STATE_AUTOFILLED_ONCE = 0x800;
+    /** View triggered the latest augmented autofill request. */
+    public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000;
 
     public final AutofillId id;