Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d987f0a authored by Felipe Leme's avatar Felipe Leme Committed by android-build-merger
Browse files

Improved documentation for AutofillService package: am: 2ef19c1d

am: 91d763f1

Change-Id: Iecf6e6d7f5461f944b1f3e23e9f97ccca5029983
parents 2e711b27 91d763f1
Loading
Loading
Loading
Loading
+214 −14
Original line number Original line Diff line number Diff line
@@ -19,24 +19,230 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.RemoteException;
import android.provider.Settings;

import com.android.internal.os.HandlerCaller;
import com.android.internal.os.HandlerCaller;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant;
import android.app.Activity;
import android.app.Service;import android.content.Intent;
import android.app.Service;
import android.content.Intent;
import android.os.CancellationSignal;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Looper;
import android.util.Log;
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;


import com.android.internal.os.SomeArgs;
import com.android.internal.os.SomeArgs;


/**
/**
 * Top-level service of the current autofill service for a given user.
 * An {@code AutofillService} is a service used to automatically fill the contents of the screen
 * on behalf of a given user - for more information about autofill, read
 * <a href="{@docRoot}preview/features/autofill.html">Autofill Framework</a>.
 *
 * <p>An {@code AutofillService} is only bound to the Android System for autofill purposes if:
 * <ol>
 *   <li>It requires the {@code android.permission.BIND_AUTOFILL_SERVICE} permission in its
 *       manifest.
 *   <li>The user explicitly enables it using Android Settings (the
 *       {@link Settings#ACTION_REQUEST_SET_AUTOFILL_SERVICE} intent can be used to launch such
 *       Settings screen).
 * </ol>
 *
 * <h3>Basic usage</h3>
 *
 * <p>The basic autofill process is defined by the workflow below:
 * <ol>
 *   <li>User focus an editable {@link View}.
 *   <li>View calls {@link AutofillManager#notifyViewEntered(android.view.View)}.
 *   <li>A {@link ViewStructure} representing all views in the screen is created.
 *   <li>The Android System binds to the service and calls {@link #onConnected()}.
 *   <li>The service receives the view structure through the
 *       {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
 *   <li>The service replies through {@link FillCallback#onSuccess(FillResponse)}.
 *   <li>The Android System calls {@link #onDisconnected()} and unbinds from the
 *       {@code AutofillService}.
 *   <li>The Android System displays an UI affordance with the options sent by the service.
 *   <li>The user picks an option.
 *   <li>The proper views are autofilled.
 * </ol>
 *
 * <p>This workflow was designed to minimize the time the Android System is bound to the service;
 * for each call, it: binds to service, waits for the reply, and unbinds right away. Furthermore,
 * those calls are considered stateless: if the service needs to keep state between calls, it must
 * do its own state management (keeping in mind that the service's process might be killed by the
 * Android System when unbound; for example, if the device is running low in memory).
 *
 * <p>Typically, the
 * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} will:
 * <ol>
 *   <li>Parse the view structure looking for autofillable views (for example, using
 *       {@link android.app.assist.AssistStructure.ViewNode#getAutofillHints()}.
 *   <li>Match the autofillable views with the user's data.
 *   <li>Create a {@link Dataset} for each set of user's data that match those fields.
 *   <li>Fill the dataset(s) with the proper {@link AutofillId}s and {@link AutofillValue}s.
 *   <li>Add the dataset(s) to the {@link FillResponse} passed to
 *       {@link FillCallback#onSuccess(FillResponse)}.
 * </ol>
 *
 * <p>For example, for a login screen with username and password views where the user only has one
 * account in the service, the response could be:
 *
 * <pre class="prettyprint">
 * new FillResponse.Builder()
 *     .addDataset(new Dataset.Builder()
 *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
 *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
 *         .build())
 *     .build();
 * </pre>
 *
 * <p>But if the user had 2 accounts instead, the response could be:
 *
 * <pre class="prettyprint">
 * new FillResponse.Builder()
 *     .addDataset(new Dataset.Builder()
 *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
 *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
 *         .build())
 *     .addDataset(new Dataset.Builder()
 *         .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
 *         .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
 *         .build())
 *     .build();
 * </pre>
 *
 * <p>If the service does not find any autofillable view in the view structure, it should pass
 * {@code null} to {@link FillCallback#onSuccess(FillResponse)}; if the service encountered an error
 * processing the request, it should call {@link FillCallback#onFailure(CharSequence)}. For
 * performance reasons, it's paramount that the service calls either
 * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)} for
 * each {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} received - if it
 * doesn't, the request will eventually time out and be discarded by the Android System.
 *
 * <h3>Saving user data</h3>
 *
 * <p>If the service is also interested on saving the data filled by the user, it must set a
 * {@link SaveInfo} object in the {@link FillResponse}. See {@link SaveInfo} for more details and
 * examples.
 *
 * <h3>User authentication</h3>
 *
 * <p>The service can provide an extra degree of security by requiring the user to authenticate
 * before an app can be autofilled. The authentication is typically required in 2 scenarios:
 * <ul>
 *   <li>To unlock the user data (for example, using a master password or fingerprint
 *       authentication) - see
 * {@link FillResponse.Builder#setAuthentication(AutofillId[], android.content.IntentSender, android.widget.RemoteViews)}.
 *   <li>To unlock a specific dataset (for example, by providing a CVC for a credit card) - see
 *       {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}.
 * </ul>
 *
 * <p>When using authentication, it is recommended to encrypt only the sensitive data and leave
 * labels unencrypted, so they can be used on presentation views. For example, if the user has a
 * home and a work address, the {@code Home} and {@code Work} labels should be stored unencrypted
 * (since they don't have any sensitive data) while the address data per se could be stored in an
 * encrypted storage. Then when the user chooses the {@code Home} dataset, the platform starts
 * the authentication flow, and the service can decrypt the sensitive data.
 *
 *
 * <p>Apps providing autofill capabilities must extend this service.
 * <p>The authentication mechanism can also be used in scenarios where the service needs multiple
 * steps to determine the datasets that can fill a screen. For example, when autofilling a financial
 * app where the user has accounts for multiple banks, the workflow could be:
 *
 * <ol>
 *   <li>The first {@link FillResponse} contains datasets with the credentials for the financial
 *       app, plus a "fake" dataset whose presentation says "Tap here for banking apps credentials".
 *   <li>When the user selects the fake dataset, the service displays a dialog with available
 *       banking apps.
 *   <li>When the user select a banking app, the service replies with a new {@link FillResponse}
 *       containing the datasets for that bank.
 * </ol>
 *
 * <p>Another example of multiple-steps dataset selection is when the service stores the user
 * credentials in "vaults": the first response would contain fake datasets with the vault names,
 * and the subsequent response would contain the app credentials stored in that vault.
 *
 * <h3>Data partitioning</h3>
 *
 * <p>The autofillable views in a screen should be grouped in logical groups called "partitions".
 * Typical partitions are:
 * <ul>
 *   <li>Credentials (username/email address, password).
 *   <li>Address (street, city, state, zip code, etc).
 *   <li>Payment info (credit card number, expiration date, and verification code).
 * </ul>
 * <p>For security reasons, when a screen has more than one partition, it's paramount that the
 * contents of a dataset do not spawn multiple partitions, specially when one of the partitions
 * contains data that is not specific to the application being autofilled. For example, a dataset
 * should not contain fields for username, password, and credit card information. The reason for
 * this rule is that a malicious app could draft a view structure where the credit card fields
 * are not visible, so when the user selects a dataset from the username UI, the credit card info is
 * released to the application without the user knowledge. Similar, it's recommended to always
 * protect a dataset that contains sensitive information by requiring dataset authentication
 * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}).
 *
 * <p>When the service detects that a screen have multiple partitions, it should return a
 * {@link FillResponse} with just the datasets for the partition that originated the request (i.e.,
 * the partition that has the {@link android.app.assist.AssistStructure.ViewNode} whose
 * {@link android.app.assist.AssistStructure.ViewNode#isFocused()} returns {@code true}); then if
 * the user selects a field from a different partition, the Android System will make another
 * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call for that partition,
 * and so on.
 *
 * <p>Notice that when the user autofill a partition with the data provided by the service and the
 * user did not change these fields, the autofilled value is sent back to the service in the
 * subsequent calls (and can be obtained by calling
 * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}). This is useful in the
 * cases where the service must create datasets for a partition based on the choice made in a
 * previous partition. For example, the 1st response for a screen that have credentials and address
 * partitions could be:
 *
 * <pre class="prettyprint">
 * new FillResponse.Builder()
 *     .addDataset(new Dataset.Builder() // partition 1 (credentials)
 *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
 *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
 *         .build())
 *     .addDataset(new Dataset.Builder() // partition 1 (credentials)
 *         .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
 *         .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
 *         .build())
 *     .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD,
 *         new AutofillId[] { id1, id2 })
 *             .build())
 *     .build();
 * </pre>
 *
 * <p>Then if the user selected {@code flanders}, the service would get a new
 * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call, with the values of
 * the fields {@code id1} and {@code id2} prepopulated, so the service could then fetch the address
 * for the Flanders account and return the following {@link FillResponse} for the address partition:
 *
 * <pre class="prettyprint">
 * new FillResponse.Builder()
 *     .addDataset(new Dataset.Builder() // partition 2 (address)
 *         .setValue(id3, AutofillValue.forText("744 Evergreen Terrace"), createPresentation("744 Evergreen Terrace")) // street
 *         .setValue(id4, AutofillValue.forText("Springfield"), createPresentation("Springfield")) // city
 *         .build())
 *     .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD | SaveInfo.SAVE_DATA_TYPE_ADDRESS,
 *         new AutofillId[] { id1, id2 }) // username and password
 *              .setOptionalIds(new AutofillId[] { id3, id4 }) // state and zipcode
 *             .build())
 *     .build();
 * </pre>
 *
 * <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
 * that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
 *
 * <h3>Ignoring views</h3>
 *
 * <p>If the service find views that cannot be autofilled (for example, a text field representing
 * the response to a Captcha challenge), it should mark those views as ignored by
 * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger
 * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
 * focused.
 */
 */
public abstract class AutofillService extends Service {
public abstract class AutofillService extends Service {
    private static final String TAG = "AutofillService";
    private static final String TAG = "AutofillService";
@@ -132,11 +338,6 @@ public abstract class AutofillService extends Service {


    private HandlerCaller mHandlerCaller;
    private HandlerCaller mHandlerCaller;


    /**
     * {@inheritDoc}
     *
     * <strong>NOTE: </strong>if overridden, it must call {@code super.onCreate()}.
     */
    @CallSuper
    @CallSuper
    @Override
    @Override
    public void onCreate() {
    public void onCreate() {
@@ -162,8 +363,7 @@ public abstract class AutofillService extends Service {
    }
    }


    /**
    /**
     * Called by the Android system do decide if an {@link Activity} can be autofilled by the
     * Called by the Android system do decide if a screen can be autofilled by the service.
     * service.
     *
     *
     * <p>Service must call one of the {@link FillCallback} methods (like
     * <p>Service must call one of the {@link FillCallback} methods (like
     * {@link FillCallback#onSuccess(FillResponse)}
     * {@link FillCallback#onSuccess(FillResponse)}
@@ -181,7 +381,7 @@ public abstract class AutofillService extends Service {
            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);


    /**
    /**
     * Called when user requests service to save the fields of an {@link Activity}.
     * Called when user requests service to save the fields of a screen.
     *
     *
     * <p>Service must call one of the {@link SaveCallback} methods (like
     * <p>Service must call one of the {@link SaveCallback} methods (like
     * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
     * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
@@ -226,7 +426,7 @@ public abstract class AutofillService extends Service {
     * @return The history or {@code null} if there are no events.
     * @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);
        final AutofillManager afm = getSystemService(AutofillManager.class);


        if (afm == null) {
        if (afm == null) {
            return null;
            return null;
+29 −21
Original line number Original line Diff line number Diff line
@@ -31,17 +31,23 @@ import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.ArrayList;


/**
/**
 * A set of data that can be used to autofill an {@link android.app.Activity}.
 * A dataset object represents a group of key/value pairs used to autofill parts of a screen.
 *
 *
 * <p>It contains:
 * <p>In its simplest form, a dataset contains one or more key / value pairs (comprised of
 * {@link AutofillId} and {@link AutofillValue} respectively); and one or more
 * {@link RemoteViews presentation} for these pairs (a pair could have its own
 * {@link RemoteViews presentation}, or use the default {@link RemoteViews presentation} associated
 * with the whole dataset). When an autofill service returns datasets in a {@link FillResponse}
 * and the screen input is focused in a view that is present in at least one of these datasets,
 * the Android System displays a UI affordance containing the {@link RemoteViews presentation} of
 * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
 * dataset from the affordance, all views in that dataset are autofilled.
 *
 *
 * <ol>
 * <p>In a more sophisticated form, the dataset value can be protected until the user authenticates
 *   <li>A list of values for input fields.
 * the dataset - see {@link Dataset.Builder#setAuthentication(IntentSender)}.
 *   <li>A presentation view to visualize.
 *   <li>An optional intent to authenticate.
 * </ol>
 *
 *
 * @see android.service.autofill.FillResponse for examples.
 * @see android.service.autofill.AutofillService for more information and examples about the
 * role of datasets in the autofill workflow.
 */
 */
public final class Dataset implements Parcelable {
public final class Dataset implements Parcelable {


@@ -113,7 +119,7 @@ public final class Dataset implements Parcelable {
    }
    }


    /**
    /**
     * A builder for {@link Dataset} objects. You must to provide at least
     * A builder for {@link Dataset} objects. You must provide at least
     * one value for a field or set an authentication intent.
     * one value for a field or set an authentication intent.
     */
     */
    public static final class Builder {
    public static final class Builder {
@@ -175,9 +181,9 @@ public final class Dataset implements Parcelable {
         * credit card information without the CVV for the data set in the {@link FillResponse
         * credit card information without the CVV for the data set in the {@link FillResponse
         * response} then the returned data set should contain the CVV entry.
         * response} then the returned data set should contain the CVV entry.
         *
         *
         * <p></><strong>Note:</strong> Do not make the provided pending intent
         * <p><b>NOTE:</b> Do not make the provided pending intent
         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
         * platform needs to fill in the authentication arguments.</p>
         * platform needs to fill in the authentication arguments.
         *
         *
         * @param authentication Intent to an activity with your authentication flow.
         * @param authentication Intent to an activity with your authentication flow.
         * @return This builder.
         * @return This builder.
@@ -191,7 +197,7 @@ public final class Dataset implements Parcelable {
        }
        }


        /**
        /**
         * Sets the id for the dataset.
         * Sets the id for the dataset so its usage history can be retrieved later.
         *
         *
         * <p>The id of the last selected dataset can be read from
         * <p>The id of the last selected dataset can be read from
         * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
         * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
@@ -217,10 +223,9 @@ public final class Dataset implements Parcelable {
         * @param value value to be autofilled. Pass {@code null} if you do not have the value
         * @param value value to be autofilled. Pass {@code null} if you do not have the value
         *        but the target view is a logical part of the dataset. For example, if
         *        but the target view is a logical part of the dataset. For example, if
         *        the dataset needs an authentication and you have no access to the value.
         *        the dataset needs an authentication and you have no access to the value.
         *        Filtering matches any user typed string to {@code null} values.
         * @return This builder.
         * @return This builder.
         * @throws IllegalStateException if the builder was constructed without a presentation
         * @throws IllegalStateException if the builder was constructed without a
         * ({@link RemoteViews}).
         * {@link RemoteViews presentation}.
         */
         */
        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
            throwIfDestroyed();
            throwIfDestroyed();
@@ -232,7 +237,8 @@ public final class Dataset implements Parcelable {
        }
        }


        /**
        /**
         * Sets the value of a field, using a custom presentation to visualize it.
         * Sets the value of a field, using a custom {@link RemoteViews presentation} to
         * visualize it.
         *
         *
         * @param id id returned by {@link
         * @param id id returned by {@link
         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
@@ -272,10 +278,12 @@ public final class Dataset implements Parcelable {
        }
        }


        /**
        /**
         * Creates a new {@link Dataset} instance. You should not interact
         * Creates a new {@link Dataset} instance.
         * with this builder once this method is called. It is required
         *
         * that you specified at least one field. Also it is mandatory to
         * <p>You should not interact with this builder once this method is called.
         * provide a presentation view to visualize the data set in the UI.
         *
         * <p>It is required that you specify at least one field before calling this method. It's
         * also mandatory to provide a presentation view to visualize the data set in the UI.
         *
         *
         * @return The built dataset.
         * @return The built dataset.
         */
         */
+0 −1
Original line number Original line Diff line number Diff line
@@ -30,7 +30,6 @@ import android.util.ArrayMap;
import android.util.SparseIntArray;
import android.util.SparseIntArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillId;


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.LinkedList;


/**
/**
+30 −8
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.view.View;


import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;


@@ -32,7 +33,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.List;


/**
/**
 * This class represents a request to an {@link AutofillService autofill provider}
 * This class represents a request to an autofill service
 * to interpret the screen and provide information to the system which views are
 * to interpret the screen and provide information to the system which views are
 * interesting for saving and what are the possible ways to fill the inputs on
 * interesting for saving and what are the possible ways to fill the inputs on
 * the screen if applicable.
 * the screen if applicable.
@@ -40,8 +41,29 @@ import java.util.List;
 * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
 * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
 */
 */
public final class FillRequest implements Parcelable {
public final class FillRequest implements Parcelable {

    /**
    /**
     * Indicates autofill was explicitly requested by the user.
     * Indicates autofill was explicitly requested by the user.
     *
     * <p>Users typically make an explicit request to autofill a screen in two situations:
     * <ul>
     *   <li>The app disabled autofill (using {@link View#setImportantForAutofill(int)}.
     *   <li>The service could not figure out how to autofill a screen (but the user knows the
     *       service has data for that app).
     * </ul>
     *
     * <p>This flag is particularly useful for the second case. For example, the service could offer
     * a complex UI where the user can map which screen views belong to each user data, or it could
     * offer a simpler UI where the user picks the data for just the view used to trigger the
     * request (that would be the view whose
     * {@link android.app.assist.AssistStructure.ViewNode#isFocused()} method returns {@code true}).
     *
     * <p>An explicit autofill request is triggered when the
     * {@link android.view.autofill.AutofillManager#requestAutofill(View)} or
     * {@link android.view.autofill.AutofillManager#requestAutofill(View, int, android.graphics.Rect)}
     * is called. For example, standard {@link android.widget.TextView} views that use
     * an {@link android.widget.Editor} shows an {@code AUTOFILL} option in the overflow menu that
     * triggers such request.
     */
     */
    public static final int FLAG_MANUAL_REQUEST = 0x1;
    public static final int FLAG_MANUAL_REQUEST = 0x1;


@@ -79,14 +101,14 @@ public final class FillRequest implements Parcelable {
    }
    }


    /**
    /**
     * @return The unique id of this request.
     * Gets the unique id of this request.
     */
     */
    public int getId() {
    public int getId() {
        return mId;
        return mId;
    }
    }


    /**
    /**
     * @return The flags associated with this request.
     * Gets the flags associated with this request.
     *
     *
     * @see #FLAG_MANUAL_REQUEST
     * @see #FLAG_MANUAL_REQUEST
     */
     */
@@ -95,7 +117,7 @@ public final class FillRequest implements Parcelable {
    }
    }


    /**
    /**
     * @return The contexts associated with each previous fill request.
     * Gets the contexts associated with each previous fill request.
     */
     */
    public @NonNull List<FillContext> getFillContexts() {
    public @NonNull List<FillContext> getFillContexts() {
        return mContexts;
        return mContexts;
@@ -104,10 +126,10 @@ public final class FillRequest implements Parcelable {
    /**
    /**
     * Gets the extra client state returned from the last {@link
     * Gets the extra client state returned from the last {@link
     * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
     * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
     * fill request}.
     * fill request}, so the service can use it for state management.
     * <p>
     *
     * Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
     * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
     * save request} is made the client state is cleared.
     * save request} is made, the client state is cleared.
     *
     *
     * @return The client state.
     * @return The client state.
     */
     */
+14 −107

File changed.

Preview size limit exceeded, changes collapsed.

Loading