/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.autofill; import static android.view.autofill.Helper.sVerbose; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * Describes what happened after the last * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} * call. * *
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. * *
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 {
private static final String TAG = "FillEventHistory";
/**
* Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
*/
private final int mSessionId;
@Nullable private final Bundle mClientState;
@Nullable List Note: 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.
*
* @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead.
*/
@Deprecated
@Nullable public Bundle getClientState() {
return mClientState;
}
/**
* Returns the events occurred after the latest call to
* {@link FillCallback#onSuccess(FillResponse)}.
*
* @return The list of events or {@code null} if non occurred.
*/
@Nullable public List Note: on Android {@link android.os.Build.VERSION_CODES#O}, this event was also
* incorrectly reported after a
* {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
* selected and the service returned a dataset in the
* {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT} of the activity launched from that
* {@link IntentSender}. This behavior was fixed on Android
* {@link android.os.Build.VERSION_CODES#O_MR1}.
*/
public static final int TYPE_DATASET_SELECTED = 0;
/**
* A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
* selected. The dataset authenticated can be read from {@link #getDatasetId()}.
*/
public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1;
/**
* A {@link FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[],
* IntentSender, android.widget.RemoteViews) fill response authentication} was selected.
*/
public static final int TYPE_AUTHENTICATION_SELECTED = 2;
/** A save UI was shown. */
public static final int TYPE_SAVE_SHOWN = 3;
/**
* A committed autofill context for which the autofill service provided datasets.
*
* This event is useful to track:
* Note: This event is only generated when:
* See {@link android.view.autofill.AutofillManager} for more information about autofill
* contexts.
*/
public static final int TYPE_CONTEXT_COMMITTED = 4;
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_DATASET_SELECTED,
TYPE_DATASET_AUTHENTICATION_SELECTED,
TYPE_AUTHENTICATION_SELECTED,
TYPE_SAVE_SHOWN,
TYPE_CONTEXT_COMMITTED
})
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
@EventIds private final int mEventType;
@Nullable private final String mDatasetId;
@Nullable private final Bundle mClientState;
// Note: mSelectedDatasetIds is stored as List<> instead of Set because Session already
// stores it as List
@Nullable private final List Note: 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() {
return mClientState;
}
/**
* Returns which datasets were selected by the user.
*
* Note: Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
*/
@NonNull public Set Note: Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
*/
@NonNull public Set For example, server provides:
*
* User select both datasets (for username and password) but after the fields are
* autofilled, user changes them to:
*
* Then the result is the following map:
*
* Note: Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
*
* @return map map whose key is the id of the change fields, and value is the id of
* dataset that has that field and was selected by the user.
*/
@NonNull public Map Note: Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
* service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
* field classification}.
*/
@NonNull public Map For example, server provides:
*
* User doesn't select a dataset but manually enters:
*
* Then the result is the following map:
*
* Note: Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}.
*
* @return map map whose key is the id of the manually-entered field, and value is the
* ids of the datasets that have that value but were not selected by the user.
*/
@NonNull public Map
*
*
*
*
*
*
* FillResponse response = new FillResponse.Builder()
* .addDataset(new Dataset.Builder(presentation1)
* .setId("4815")
* .setValue(usernameId, AutofillValue.forText("MrPlow"))
* .build())
* .addDataset(new Dataset.Builder(presentation2)
* .setId("162342")
* .setValue(passwordId, AutofillValue.forText("D'OH"))
* .build())
* .build();
*
*
*
* username = "ElBarto";
* password = "AyCaramba";
*
*
*
* usernameId => "4815"
* passwordId => "162342"
*
*
*
* FillResponse response = new FillResponse.Builder()
* .addDataset(new Dataset.Builder(presentation1)
* .setId("4815")
* .setValue(usernameId, AutofillValue.forText("MrPlow"))
* .setValue(passwordId, AutofillValue.forText("AyCaramba"))
* .build())
* .addDataset(new Dataset.Builder(presentation2)
* .setId("162342")
* .setValue(usernameId, AutofillValue.forText("ElBarto"))
* .setValue(passwordId, AutofillValue.forText("D'OH"))
* .build())
* .addDataset(new Dataset.Builder(presentation3)
* .setId("108")
* .setValue(usernameId, AutofillValue.forText("MrPlow"))
* .setValue(passwordId, AutofillValue.forText("D'OH"))
* .build())
* .build();
*
*
*
* username = "MrPlow";
* password = "D'OH";
*
*
*
* usernameId => { "4815", "108"}
* passwordId => { "162342", "108" }
*
*
*