Merge "Integrate autofill session lifecycle with URL bar changes when on compat mode." into pi-dev

This commit is contained in:
TreeHugger Robot
2018-03-27 05:15:20 +00:00
committed by Android (Google) Code Review
4 changed files with 144 additions and 25 deletions

View File

@@ -273,6 +273,16 @@ public final class AutofillManager {
*/
public static final int STATE_DISABLED_BY_SERVICE = 4;
/**
* Same as {@link #STATE_UNKNOWN}, but used on
* {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
* the URL bar changed on client mode
*
* @hide
*/
public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
/**
* Timeout in ms for calls to the field classification service.
* @hide
@@ -1947,15 +1957,24 @@ public final class AutofillManager {
* Marks the state of the session as finished.
*
* @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
* FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
* {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
* requests for the activity).
* FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
* {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
* changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
* disabled further autofill requests for the activity).
*/
private void setSessionFinished(int newState) {
synchronized (mLock) {
if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
resetSessionLocked(/* resetEnteredIds= */ false);
mState = newState;
if (sVerbose) {
Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
+ getStateAsString(newState));
}
if (newState == STATE_UNKNOWN_COMPAT_MODE) {
resetSessionLocked(/* resetEnteredIds= */ true);
mState = STATE_UNKNOWN;
} else {
resetSessionLocked(/* resetEnteredIds= */ false);
mState = newState;
}
}
}
@@ -2107,19 +2126,26 @@ public final class AutofillManager {
@GuardedBy("mLock")
private String getStateAsStringLocked() {
switch (mState) {
return getStateAsString(mState);
}
@NonNull
private static String getStateAsString(int state) {
switch (state) {
case STATE_UNKNOWN:
return "STATE_UNKNOWN";
return "UNKNOWN";
case STATE_ACTIVE:
return "STATE_ACTIVE";
return "ACTIVE";
case STATE_FINISHED:
return "STATE_FINISHED";
return "FINISHED";
case STATE_SHOWING_SAVE_UI:
return "STATE_SHOWING_SAVE_UI";
return "SHOWING_SAVE_UI";
case STATE_DISABLED_BY_SERVICE:
return "STATE_DISABLED_BY_SERVICE";
return "DISABLED_BY_SERVICE";
case STATE_UNKNOWN_COMPAT_MODE:
return "UNKNOWN_COMPAT_MODE";
default:
return "INVALID:" + mState;
return "INVALID:" + state;
}
}

View File

@@ -180,9 +180,11 @@ public final class Helper {
*
* @param structure Assist structure
* @param urlBarIds list of ids; only the first id found will be sanitized.
*
* @return the node containing the URL bar
*/
@Nullable
public static void sanitizeUrlBar(@NonNull AssistStructure structure,
public static ViewNode sanitizeUrlBar(@NonNull AssistStructure structure,
@NonNull String[] urlBarIds) {
final ViewNode urlBarNode = findViewNode(structure, (node) -> {
return ArrayUtils.contains(urlBarIds, node.getIdEntry());
@@ -191,7 +193,7 @@ public final class Helper {
final String domain = urlBarNode.getText().toString();
if (domain.isEmpty()) {
if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarNode.getIdEntry());
return;
return null;
}
urlBarNode.setWebDomain(domain);
if (sDebug) {
@@ -199,6 +201,7 @@ public final class Helper {
+ urlBarNode.getWebDomain());
}
}
return urlBarNode;
}
private interface ViewNodeFilter {

View File

@@ -146,6 +146,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** Whether the app being autofilled is running in compat mode. */
private final boolean mCompatMode;
/** Node representing the URL bar on compat mode. */
@GuardedBy("mLock")
private ViewNode mUrlBar;
@GuardedBy("mLock")
private boolean mSaveOnAllViewsInvisible;
@GuardedBy("mLock")
private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>();
@@ -280,7 +287,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
}
if (urlBarIds != null) {
Helper.sanitizeUrlBar(structure, urlBarIds);
mUrlBar = Helper.sanitizeUrlBar(structure, urlBarIds);
if (mUrlBar != null) {
final AutofillId urlBarId = mUrlBar.getAutofillId();
if (sDebug) {
Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
+ mUrlBar.getWebDomain());
}
final ViewState viewState = new ViewState(Session.this, urlBarId,
Session.this, ViewState.STATE_URL_BAR);
mViewStates.put(urlBarId, viewState);
}
}
}
structure.sanitizeForParceling(true);
@@ -1878,14 +1895,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
if (sVerbose) {
Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + action + ", flags=" + flags);
Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + actionAsString(action)
+ ", flags=" + flags);
}
ViewState viewState = mViewStates.get(id);
if (viewState == null) {
if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
|| action == ACTION_VIEW_ENTERED) {
if (sVerbose) Slog.v(TAG, "Creating viewState for " + id + " on " + action);
if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
boolean isIgnored = isIgnoredLocked(id);
viewState = new ViewState(this, id, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
@@ -1895,11 +1913,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// detectable, and batch-send them when the session is finished (but that will
// require tracking detectable fields on AutofillManager)
if (isIgnored) {
if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + viewState);
return;
}
} else {
if (sVerbose) Slog.v(TAG, "Ignored action " + action + " for " + id);
if (sVerbose) Slog.v(TAG, "Ignoring specific action when viewState=null");
return;
}
}
@@ -1913,6 +1931,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
requestNewFillResponseLocked(flags);
break;
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
// Must cancel the session if the value of the URL bar changed
final String currentUrl = mUrlBar == null ? null
: mUrlBar.getText().toString().trim();
if (currentUrl == null) {
// Sanity check - shouldn't happen.
wtf(null, "URL bar value changed, but current value is null");
return;
}
if (value == null || ! value.isText()) {
// Sanity check - shouldn't happen.
wtf(null, "URL bar value changed to null or non-text: %s", value);
return;
}
final String newUrl = value.getTextValue().toString();
if (newUrl.equals(currentUrl)) {
if (sDebug) Slog.d(TAG, "Ignoring change on URL bar as it's the same");
return;
}
if (mSaveOnAllViewsInvisible) {
// We cannot cancel the session because it could hinder Save when all views
// are finished, as the URL bar changed callback is usually called before
// the virtual views become invisible.
if (sDebug) {
Slog.d(TAG, "Ignoring change on URL because session will finish when "
+ "views are gone");
}
return;
}
if (sDebug) Slog.d(TAG, "Finishing session because URL bar changed");
forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
return;
}
if (value != null && !value.equals(viewState.getCurrentValue())) {
if (value.isEmpty()
&& viewState.getCurrentValue() != null
@@ -1953,6 +2005,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sVerbose && virtualBounds != null) {
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")");
return;
}
requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
@@ -2068,7 +2126,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (response == null) return;
ArraySet<AutofillId> trackedViews = null;
boolean saveOnAllViewsInvisible = false;
mSaveOnAllViewsInvisible = false;
boolean saveOnFinish = true;
final SaveInfo saveInfo = response.getSaveInfo();
final AutofillId saveTriggerId;
@@ -2081,10 +2139,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mCompatMode) {
flags |= SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE;
}
saveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
mSaveOnAllViewsInvisible = (flags & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
// We only need to track views if we want to save once they become invisible.
if (saveOnAllViewsInvisible) {
if (mSaveOnAllViewsInvisible) {
if (trackedViews == null) {
trackedViews = new ArraySet<>();
}
@@ -2129,7 +2187,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
+ " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish);
}
mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
saveOnFinish, toArray(fillableIds), saveTriggerId);
} catch (RemoteException e) {
Slog.w(TAG, "Cannot set tracked ids", e);
@@ -2421,6 +2479,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("mClientState: "); pw.println(
Helper.bundleToString(mClientState));
pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
pw.print(prefix); pw.print("mUrlBar: ");
if (mUrlBar == null) {
pw.println("N/A");
} else {
pw.print("id="); pw.print(mUrlBar.getAutofillId());
pw.print(" domain="); pw.print(mUrlBar.getWebDomain());
pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText());
}
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
mRemoteFillService.dump(prefix, pw);
}
@@ -2513,6 +2581,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
@GuardedBy("mLock")
void forceRemoveSelfLocked() {
forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN);
}
@GuardedBy("mLock")
void forceRemoveSelfLocked(int clientState) {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
final boolean isPendingSaveUi = isSaveUiPendingLocked();
@@ -2521,7 +2594,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mUi.destroyAll(mPendingSaveUi, this, false);
if (!isPendingSaveUi) {
try {
mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN);
mClient.setSessionFinished(clientState);
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client to finish session", e);
}
@@ -2624,4 +2697,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.wtf(TAG, message);
}
}
private static String actionAsString(int action) {
switch (action) {
case ACTION_START_SESSION:
return "START_SESSION";
case ACTION_VIEW_ENTERED:
return "VIEW_ENTERED";
case ACTION_VIEW_EXITED:
return "VIEW_EXITED";
case ACTION_VALUE_CHANGED:
return "VALUE_CHANGED";
default:
return "UNKNOWN_" + action;
}
}
}

View File

@@ -67,6 +67,8 @@ final class ViewState {
public static final int STATE_IGNORED = 0x080;
/** User manually request autofill in this view, after it was already autofilled. */
public static final int STATE_RESTARTED_SESSION = 0x100;
/** View is the URL bar of a package on compat mode. */
public static final int STATE_URL_BAR = 0x200;
public final AutofillId id;