Merge changes from topics 'bug_62802026_take_2', 'bug_62802026' into oc-dev

* changes:
  Don't add FillEventHistory events to the wrong session.
  Fixed how FillEventHistory is reset and clarified javadoc.
This commit is contained in:
TreeHugger Robot
2017-06-21 03:45:42 +00:00
committed by Android (Google) Code Review
5 changed files with 96 additions and 25 deletions

View File

@@ -208,12 +208,22 @@ public abstract class AutofillService extends Service {
} }
/** /**
* Returns the {@link FillEventHistory.Event events} since the last {@link FillResponse} was * Gets the events that happened after the last
* returned. * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* call.
* *
* <p>The history is not persisted over reboots. * <p>This method is typically used to keep track of previous user actions to optimize further
* requests. For example, the service might return email addresses in alphabetical order by
* default, but change that order based on the address the user picked on previous requests.
* *
* @return The history or {@code null} if there are not events. * <p>The history is not persisted over reboots, and it's cleared every time the service
* replies to a {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} by calling
* {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)}
* (if the service doesn't call any of these methods, the history will clear out after some
* pre-defined time). Hence, the service should call {@link #getFillEventHistory()} before
* finishing the {@link FillCallback}.
*
* @return The history or {@code null} if there are no events.
*/ */
@Nullable public final FillEventHistory getFillEventHistory() { @Nullable public final FillEventHistory getFillEventHistory() {
AutofillManager afm = getSystemService(AutofillManager.class); AutofillManager afm = getSystemService(AutofillManager.class);

View File

@@ -33,7 +33,20 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Describes what happened after the latest call to {@link FillCallback#onSuccess(FillResponse)}. * Describes what happened after the last
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* call.
*
* <p>This history is typically used to keep track of previous user actions to optimize further
* requests. For example, the service might return email addresses in alphabetical order by
* default, but change that order based on the address the user picked on previous requests.
*
* <p>The history is not persisted over reboots, and it's cleared every time the service
* replies to a
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* by calling {@link FillCallback#onSuccess(FillResponse)} or
* {@link FillCallback#onFailure(CharSequence)} (if the service doesn't call any of these methods,
* the history will clear out after some pre-defined time).
*/ */
public final class FillEventHistory implements Parcelable { public final class FillEventHistory implements Parcelable {
/** /**
@@ -41,6 +54,11 @@ public final class FillEventHistory implements Parcelable {
*/ */
private final int mServiceUid; private final int mServiceUid;
/**
* Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
*/
private final int mSessionId;
@Nullable private final Bundle mClientState; @Nullable private final Bundle mClientState;
@Nullable List<Event> mEvents; @Nullable List<Event> mEvents;
@@ -55,10 +73,17 @@ public final class FillEventHistory implements Parcelable {
return mServiceUid; return mServiceUid;
} }
/** @hide */
public int getSessionId() {
return mSessionId;
}
/** /**
* Returns the client state of the {@link FillResponse}. * Returns the client state set in the previous {@link FillResponse}.
* *
* @return The client state set by the last {@link FillResponse} * <p><b>NOTE: </b>the state is associated with the app that was autofilled in the previous
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* , which is not necessary the same app being autofilled now.
*/ */
@Nullable public Bundle getClientState() { @Nullable public Bundle getClientState() {
return mClientState; return mClientState;
@@ -87,9 +112,10 @@ public final class FillEventHistory implements Parcelable {
/** /**
* @hide * @hide
*/ */
public FillEventHistory(int serviceUid, @Nullable Bundle clientState) { public FillEventHistory(int serviceUid, int sessionId, @Nullable Bundle clientState) {
mClientState = clientState; mClientState = clientState;
mServiceUid = serviceUid; mServiceUid = serviceUid;
mSessionId = sessionId;
} }
@Override @Override
@@ -190,7 +216,7 @@ public final class FillEventHistory implements Parcelable {
new Parcelable.Creator<FillEventHistory>() { new Parcelable.Creator<FillEventHistory>() {
@Override @Override
public FillEventHistory createFromParcel(Parcel parcel) { public FillEventHistory createFromParcel(Parcel parcel) {
FillEventHistory selection = new FillEventHistory(0, parcel.readBundle()); FillEventHistory selection = new FillEventHistory(0, 0, parcel.readBundle());
int numEvents = parcel.readInt(); int numEvents = parcel.readInt();
for (int i = 0; i < numEvents; i++) { for (int i = 0; i < numEvents; i++) {

View File

@@ -489,48 +489,80 @@ final class AutofillManagerServiceImpl {
* Initializes the last fill selection after an autofill service returned a new * Initializes the last fill selection after an autofill service returned a new
* {@link FillResponse}. * {@link FillResponse}.
*/ */
void setLastResponse(int serviceUid, @NonNull FillResponse response) { void setLastResponse(int serviceUid, int sessionId, @NonNull FillResponse response) {
synchronized (mLock) { synchronized (mLock) {
mEventHistory = new FillEventHistory(serviceUid, response.getClientState()); mEventHistory = new FillEventHistory(serviceUid, sessionId, response.getClientState());
} }
} }
/**
* Resets the last fill selection.
*/
void resetLastResponse() {
synchronized (mLock) {
mEventHistory = null;
}
}
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
Slog.w(TAG, method + ": not logging event because history is null");
return false;
}
if (sessionId != mEventHistory.getSessionId()) {
if (sDebug) {
Slog.d(TAG, method + ": not logging event for session " + sessionId
+ " because tracked session is " + mEventHistory.getSessionId());
}
return false;
}
return true;
}
/** /**
* Updates the last fill selection when an authentication was selected. * Updates the last fill selection when an authentication was selected.
*/ */
void setAuthenticationSelected() { void setAuthenticationSelected(int sessionId) {
synchronized (mLock) { synchronized (mLock) {
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null)); mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
} }
} }
}
/** /**
* Updates the last fill selection when an dataset authentication was selected. * Updates the last fill selection when an dataset authentication was selected.
*/ */
void setDatasetAuthenticationSelected(@Nullable String selectedDataset) { void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) {
synchronized (mLock) { synchronized (mLock) {
if (isValidEventLocked("setDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent( mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset)); new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
} }
} }
}
/** /**
* Updates the last fill selection when an save Ui is shown. * Updates the last fill selection when an save Ui is shown.
*/ */
void setSaveShown() { void setSaveShown(int sessionId) {
synchronized (mLock) { synchronized (mLock) {
if (isValidEventLocked("setSaveShown()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null)); mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
} }
} }
}
/** /**
* Updates the last fill response when a dataset was selected. * Updates the last fill response when a dataset was selected.
*/ */
void setDatasetSelected(@Nullable String selectedDataset) { void setDatasetSelected(@Nullable String selectedDataset, int sessionId) {
synchronized (mLock) { synchronized (mLock) {
if (isValidEventLocked("setDatasetSelected()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset)); mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
} }
} }
}
/** /**
* Gets the fill event history. * Gets the fill event history.

View File

@@ -427,6 +427,7 @@ final class RemoteFillService implements DeathRecipient {
mCompleted = true; mCompleted = true;
} }
Slog.w(LOG_TAG, getClass().getSimpleName() + " timed out");
final RemoteFillService remoteService = mWeakService.get(); final RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) { if (remoteService != null) {
fail(remoteService); fail(remoteService);

View File

@@ -407,13 +407,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if ((requestFlags & FLAG_MANUAL_REQUEST) != 0) { if ((requestFlags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this); getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
} }
mService.resetLastResponse();
// Nothing to be done, but need to notify client. // Nothing to be done, but need to notify client.
notifyUnavailableToClient(); notifyUnavailableToClient();
removeSelf(); removeSelf();
return; return;
} }
mService.setLastResponse(serviceUid, response); mService.setLastResponse(serviceUid, id, response);
if ((response.getDatasets() == null || response.getDatasets().isEmpty()) if ((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null) { && response.getAuthentication() == null) {
@@ -444,6 +445,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ id + " destroyed"); + id + " destroyed");
return; return;
} }
mService.resetLastResponse();
} }
LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST)) LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
.setType(MetricsEvent.TYPE_FAILURE) .setType(MetricsEvent.TYPE_FAILURE)
@@ -542,7 +544,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getFillContextByRequestIdLocked(requestId).getStructure(), extras); getFillContextByRequestIdLocked(requestId).getStructure(), extras);
} }
mService.setAuthenticationSelected(); mService.setAuthenticationSelected(id);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId, mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId,
@@ -831,7 +833,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
if (atLeastOneChanged) { if (atLeastOneChanged) {
if (sDebug) Slog.d(TAG, "at least one field changed - showing save UI"); if (sDebug) Slog.d(TAG, "at least one field changed - showing save UI");
mService.setSaveShown(); mService.setSaveShown(id);
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName, getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName,
this); this);
@@ -1362,14 +1364,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
// Autofill it directly... // Autofill it directly...
if (dataset.getAuthentication() == null) { if (dataset.getAuthentication() == null) {
mService.setDatasetSelected(dataset.getId()); mService.setDatasetSelected(dataset.getId(), id);
autoFillApp(dataset); autoFillApp(dataset);
return; return;
} }
// ...or handle authentication. // ...or handle authentication.
mService.setDatasetAuthenticationSelected(dataset.getId()); mService.setDatasetAuthenticationSelected(dataset.getId(), id);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false); setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntent( final Intent fillInIntent = createAuthFillInIntent(
getFillContextByRequestIdLocked(requestId).getStructure(), mClientState); getFillContextByRequestIdLocked(requestId).getStructure(), mClientState);