Merge "Trigger Augmented Autofill when the standard service used FillResponse.disableAutofill()"

This commit is contained in:
TreeHugger Robot
2019-03-20 23:59:30 +00:00
committed by Android (Google) Code Review
5 changed files with 181 additions and 51 deletions

View File

@@ -228,6 +228,9 @@ public final class AutofillManager {
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
/** @hide */
public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
? AutofillManager.FLAG_ADD_CLIENT_DEBUG
@@ -307,8 +310,8 @@ public final class AutofillManager {
/**
* 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
* {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
* because the URL bar changed on client mode
*
* @hide
*/
@@ -316,8 +319,8 @@ public final class AutofillManager {
/**
* Same as {@link #STATE_UNKNOWN}, but used on
* {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
* the service failed to fullfil a request.
* {@link AutofillManagerClient#setSessionFinished(int, List)} when the session was finished
* because the service failed to fullfil a request.
*
* @hide
*/
@@ -436,7 +439,7 @@ public final class AutofillManager {
* There is currently no session running.
* {@hide}
*/
public static final int NO_SESSION = Integer.MIN_VALUE;
public static final int NO_SESSION = Integer.MAX_VALUE;
private final IAutoFillManager mService;
@@ -513,6 +516,10 @@ public final class AutofillManager {
@Nullable
private final AutofillOptions mOptions;
/** When set, session is only used for augmented autofill requests. */
@GuardedBy("mLock")
private boolean mForAugmentedAutofillOnly;
/** @hide */
public interface AutofillClient {
/**
@@ -940,9 +947,8 @@ public final class AutofillManager {
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
if (sVerbose) {
Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
}
if (sVerbose) Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
if (mCallback != null) {
callback = mCallback;
}
@@ -1025,6 +1031,12 @@ public final class AutofillManager {
private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
boolean isVisible, boolean virtual) {
synchronized (mLock) {
if (mForAugmentedAutofillOnly) {
if (sVerbose) {
Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
}
return;
}
if (mEnabled && isActiveLocked()) {
final AutofillId id = virtual ? getAutofillId(view, virtualId)
: view.getAutofillId();
@@ -1168,6 +1180,10 @@ public final class AutofillManager {
AutofillValue value = null;
synchronized (mLock) {
if (mForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
return;
}
// If the session is gone some fields might still be highlighted, hence we have to
// remove the isAutofilled property even if no sessions are active.
if (mLastAutofilledData == null) {
@@ -1221,6 +1237,10 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
if (mForAugmentedAutofillOnly) {
if (sVerbose) Log.v(TAG, "notifyValueChanged(): ignoring on augmented only mode");
return;
}
if (!mEnabled || !isActiveLocked()) {
if (sVerbose) {
Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
@@ -1676,14 +1696,20 @@ public final class AutofillManager {
if (client == null) return; // NOTE: getClient() already logged it..
final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
final ComponentName componentName = client.autofillClientGetComponentName();
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, client.autofillClientGetComponentName(),
mCallback != null, flags, componentName,
isCompatibilityModeEnabledLocked(), receiver);
mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
final int extraFlags = receiver.getOptionalExtraIntResult(0);
if ((extraFlags & FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) != 0) {
if (sDebug) Log.d(TAG, "startSession(" + componentName + "): for augmented only");
mForAugmentedAutofillOnly = true;
}
client.autofillClientResetableStateAvailable();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2400,6 +2426,9 @@ public final class AutofillManager {
pw.print(pfx); pw.print("entered ids for augmented autofill: ");
pw.println(mEnteredForAugmentedAutofillIds);
}
if (mForAugmentedAutofillOnly) {
pw.print(pfx); pw.println("For Augmented Autofill Only");
}
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
if (mOptions != null) {

View File

@@ -96,6 +96,19 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
return mBundle == null ? null : mBundle.getParcelable(EXTRA);
}
/**
* Gets the optional result from an operation that returns an extra {@code int} (besides the
* result code).
*
* @return value set in the bundle, or {@code defaultValue} when not set.
*/
public int getOptionalExtraIntResult(int defaultValue) throws TimeoutException {
waitResult();
if (mBundle == null || !mBundle.containsKey(EXTRA)) return defaultValue;
return mBundle.getInt(EXTRA);
}
@Override
public void send(int resultCode, Bundle resultData) {
mResult = resultCode;
@@ -136,6 +149,18 @@ public final class SyncResultReceiver extends IResultReceiver.Stub {
return bundle;
}
/**
* Creates a bundle for an {@code int} value so it can be retrieved by
* {@link #getParcelableResult()} - typically used to return an extra {@code int} (as the 1st
* is returned as the result code).
*/
@NonNull
public static Bundle bundleFor(int value) {
final Bundle bundle = new Bundle();
bundle.putInt(EXTRA, value);
return bundle;
}
/** @hide */
public static final class TimeoutException extends RemoteException {
private TimeoutException(String msg) {

View File

@@ -683,6 +683,14 @@ public final class AutofillManagerService
send(receiver, value ? 1 : 0);
}
private void send(@NonNull IResultReceiver receiver, int value1, int value2) {
try {
receiver.send(value1, SyncResultReceiver.bundleFor(value2));
} catch (RemoteException e) {
Slog.w(TAG, "Error async reporting result to client: " + e);
}
}
@Nullable
@VisibleForTesting
static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
@@ -1003,14 +1011,20 @@ public final class AutofillManagerService
// TODO(b/113281366): add a callback method on AM to be notified when a task is finished
// so we can clean up sessions kept alive
final int taskId = mAm.getTaskIdForActivity(activityToken, false);
final int sessionId;
final long result;
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
sessionId = service.startSessionLocked(activityToken, taskId, getCallingUid(),
result = service.startSessionLocked(activityToken, taskId, getCallingUid(),
appCallback, autofillId, bounds, value, hasCallback, componentName,
compatMode, mAllowInstantService, flags);
}
send(receiver, sessionId);
final int sessionId = (int) result;
final int resultFlags = (int) (result >> 32);
if (resultFlags != 0) {
send(receiver, sessionId, resultFlags);
} else {
send(receiver, sessionId);
}
}
@Override

View File

@@ -18,6 +18,7 @@ package com.android.server.autofill;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY;
import static android.view.autofill.AutofillManager.NO_SESSION;
import static com.android.server.autofill.Helper.sDebug;
@@ -53,6 +54,7 @@ import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.UserData;
@@ -222,7 +224,7 @@ final class AutofillManagerServiceImpl
session.removeSelfLocked();
}
}
sendStateToClients(false);
sendStateToClients(/* resetClient= */ false);
}
updateRemoteAugmentedAutofillService();
return enabledChanged;
@@ -279,8 +281,15 @@ final class AutofillManagerServiceImpl
}
}
/**
* Starts a new session.
*
* @return {@code long} whose right-most 32 bits represent the session id (which is always
* non-negative), and the left-most contains extra flags (currently either {@code 0} or
* {@link FillRequest#FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY}).
*/
@GuardedBy("mLock")
int startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
long startSessionLocked(@NonNull IBinder activityToken, int taskId, int uid,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@NonNull ComponentName componentName, boolean compatMode,
@@ -290,33 +299,48 @@ final class AutofillManagerServiceImpl
}
final String shortComponentName = componentName.toShortString();
boolean forAugmentedAutofillOnly = false;
if (isAutofillDisabledLocked(componentName)) {
if (sDebug) {
Slog.d(TAG, "startSession(" + shortComponentName
+ "): ignored because disabled by service");
}
// Service disabled autofill; that means no session, unless the activity is whitelisted
// for augmented autofill
if (isWhitelistedForAugmentedAutofillLocked(componentName)) {
if (sDebug) {
Slog.d(TAG, "startSession(" + shortComponentName + "): disabled by service but "
+ "whitelisted for augmented autofill");
}
forAugmentedAutofillOnly = true;
final IAutoFillManagerClient client = IAutoFillManagerClient.Stub
.asInterface(appCallbackToken);
try {
client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE,
/* autofillableIds= */ null);
} catch (RemoteException e) {
Slog.w(TAG, "Could not notify " + shortComponentName + " that it's disabled: " + e);
}
} else {
if (sDebug) {
Slog.d(TAG, "startSession(" + shortComponentName + "): ignored because "
+ "disabled by service and not whitelisted for augmented autofill");
}
final IAutoFillManagerClient client = IAutoFillManagerClient.Stub
.asInterface(appCallbackToken);
try {
client.setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE,
/* autofillableIds= */ null);
} catch (RemoteException e) {
Slog.w(TAG,
"Could not notify " + shortComponentName + " that it's disabled: " + e);
}
return NO_SESSION;
return NO_SESSION;
}
}
if (sVerbose) Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags);
if (sVerbose) {
Slog.v(TAG, "startSession(): token=" + activityToken + ", flags=" + flags
+ ", forAugmentedAutofillOnly=" + forAugmentedAutofillOnly);
}
// Occasionally clean up abandoned sessions
pruneAbandonedSessionsLocked();
final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
appCallbackToken, hasCallback, componentName, compatMode,
bindInstantServiceAllowed, flags);
bindInstantServiceAllowed, forAugmentedAutofillOnly, flags);
if (newSession == null) {
return NO_SESSION;
}
@@ -325,12 +349,20 @@ final class AutofillManagerServiceImpl
"id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName
+ " s=" + mInfo.getServiceInfo().packageName
+ " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds
+ " hc=" + hasCallback + " f=" + flags;
+ " hc=" + hasCallback + " f=" + flags + " aa=" + forAugmentedAutofillOnly;
mMaster.logRequestLocked(historyItem);
newSession.updateLocked(autofillId, virtualBounds, value, ACTION_START_SESSION, flags);
return newSession.id;
if (forAugmentedAutofillOnly) {
// Must embed the flag in the response, at the high-end side of the long.
// (session is always positive, so we don't have to worry about the signal bit)
final long extraFlags = ((long) FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY) << 32;
final long result = extraFlags | newSession.id;
return result;
} else {
return newSession.id;
}
}
/**
@@ -436,7 +468,7 @@ final class AutofillManagerServiceImpl
private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int taskId, int uid,
@NonNull IBinder appCallbackToken, boolean hasCallback,
@NonNull ComponentName componentName, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags) {
// use random ids so that one app cannot know that another app creates sessions
int sessionId;
int tries = 0;
@@ -447,15 +479,17 @@ final class AutofillManagerServiceImpl
return null;
}
sessionId = sRandom.nextInt();
} while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
sessionId = Math.abs(sRandom.nextInt());
} while (sessionId == 0 || sessionId == NO_SESSION
|| mSessions.indexOfKey(sessionId) >= 0);
assertCallerLocked(componentName, compatMode);
final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
componentName, compatMode, bindInstantServiceAllowed, flags);
componentName, compatMode, bindInstantServiceAllowed, forAugmentedAutofillOnly,
flags);
mSessions.put(newSession.id, newSession);
return newSession;
@@ -635,7 +669,7 @@ final class AutofillManagerServiceImpl
remoteFillServices.valueAt(i).destroy();
}
sendStateToClients(true);
sendStateToClients(/* resetclient=*/ true);
if (mClients != null) {
mClients.kill();
mClients = null;

View File

@@ -138,7 +138,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static AtomicInteger sIdCounter = new AtomicInteger();
/** ID of the session */
/**
* ID of the session.
*
* <p>It's always a positive number, to make it easier to embed it in a long.
*/
public final int id;
/** uid the session is for */
@@ -276,6 +280,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private ArraySet<AutofillId> mAugmentedAutofillableIds;
/**
* When {@code true}, the session was created only to handle Augmented Autofill requests (i.e.,
* the session would not have existed otherwsie).
*/
@GuardedBy("mLock")
private boolean mForAugmentedAutofillOnly;
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -538,11 +549,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
@GuardedBy("mLock")
private void requestNewFillResponseLocked(int flags) {
if ((flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
if (mForAugmentedAutofillOnly || (flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
// TODO(b/122858578): log metrics
if (sVerbose) {
Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead");
Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
+ "(mForAugmentedAutofillOnly=" + mForAugmentedAutofillOnly
+ ", flags=" + flags + ")");
}
triggerAugmentedAutofillLocked();
return;
@@ -564,8 +576,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mRequestLogs.put(requestId, log);
if (sVerbose) {
Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId="
+ requestId + ", flags=" + flags);
Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
+ ", flags=" + flags);
}
// If the focus changes very quickly before the first request is returned each focus change
@@ -598,7 +610,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
@NonNull LocalLog wtfHistory, @NonNull ComponentName serviceComponentName,
@NonNull ComponentName componentName, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags) {
if (sessionId < 0) {
wtf(null, "Non-positive sessionId: %s", sessionId);
}
id = sessionId;
mFlags = flags;
this.taskId = taskId;
@@ -616,6 +631,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mWtfHistory = wtfHistory;
mComponentName = componentName;
mCompatMode = compatMode;
mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
setClientLocked(client);
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
@@ -727,14 +743,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final long disableDuration = response.getDisableDuration();
if (disableDuration > 0) {
final int flags = response.getFlags();
if (sDebug) {
final StringBuilder message = new StringBuilder("Service disabled autofill for ")
.append(mComponentName)
.append(": flags=").append(flags)
.append(", duration=");
TimeUtils.formatDuration(disableDuration, message);
Slog.d(TAG, message.toString());
}
if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
mService.disableAutofillForActivity(mComponentName, disableDuration,
id, mCompatMode);
@@ -742,6 +750,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
id, mCompatMode);
}
// Although "standard" autofill is disabled, it might still trigger augmented autofill
if (triggerAugmentedAutofillLocked() != null) {
mForAugmentedAutofillOnly = true;
if (sDebug) {
Slog.d(TAG, "Service disabled autofill for " + mComponentName
+ ", but session is kept for augmented autofill only");
}
return;
}
if (sDebug) {
final StringBuilder message = new StringBuilder("Service disabled autofill for ")
.append(mComponentName)
.append(": flags=").append(flags)
.append(", duration=");
TimeUtils.formatDuration(disableDuration, message);
Slog.d(TAG, message.toString());
}
sessionFinishedState = AutofillManager.STATE_DISABLED_BY_SERVICE;
}
@@ -3005,6 +3030,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
if (mForAugmentedAutofillOnly) {
pw.print(prefix); pw.println("For Augmented Autofill Only");
}
if (mAugmentedAutofillDestroyer != null) {
pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
}