Skip to content

Most visited

Recently visited

navigation

Building autofill services

An autofill service is an app that makes it easier for users to fill out forms by injecting data into the views of other apps. Autofill services can also retrieve user data from the views in an app and store it to use it at a later time. Autofill services are usually provided by apps that manage user data, such as password managers.

Android makes filling out forms easier with the Autofill Framework available in Android 8.0 (API level 26) and higher. Users can take advantage of autofill features only if there is an app that provides autofill services on their device.

This page shows how to implement an autofill service in your app. If you're looking for a code sample that shows how to implement a service, see the Android AutofillFramework Sample. For further details about how autofill services work, see the reference documentation of the AutofillService and AutofillManager classes.

Manifest declarations and permissions

Apps that provide autofill services must include a declaration that describes the implementation of the service. To specify the declaration, include a <service> element in the app manifest. The <service> element should include the following attributes and elements:

The following example shows an autofill service declaration:

<service
    android:name=".MyAutofillService"
    android:label="My Autofill Service"
    android:permission="android.permission.BIND_AUTOFILL_SERVICE">
    <intent-filter>
        <action android:name="android.service.autofill.AutofillService" />
    </intent-filter>
    <meta-data
        android:name="android.autofill"
        android:resource="@xml/service_configuration" />
</service>

The <meta-data> element includes an android:resource attribute that points to an XML resource with further details about the service. The service_configuration resource in the previous example specifies an activity that allows the users to configure the service. The following example shows the service_configuration XML resource:

<autofill-service
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:settingsActivity="com.example.android.SettingsActivity" />

For more information about XML resources, see Providing Resources.

Prompt to enable the service

An app is used as the autofill service after it declares the BIND_AUTOFILL_SERVICE permission and the user enables it in the device settings. An app can verify if it's the currently enabled service by calling the hasEnabledAutofillServices() method of the AutofillManager class.

If the app isn't the current autofill service, then it can request the user to change the autofill settings by using the ACTION_REQUEST_SET_AUTOFILL_SERVICE intent. The intent returns a RESULT_OK value if the user selected an autofill service that matches the package of the caller.

Fill out client views

The autofill service receives requests to fill out client views when the user interacts with other apps. If the autofill service has user data that satisfies the request then it sends the data in the response. The Android system shows an autofill UI with the available data, as shown in figure 1:

Autofill UI

Figure 1. Autofill UI displaying a dataset.

The autofill framework defines a workflow to fill out views that is designed to minimize the time that the Android system is bound to the autofill service. In each request, the Android system sends an AssistStructure object to the service by calling the onFillRequest() method. The autofill service checks if it can satisfy the request with user data that it has previously stored. If it can satisfy the request, then the service packages the data in Dataset objects. The service calls the onSuccess() method passing a FillResponse object, which contains the Dataset objects. If the service doesn't have data to satisfy the request, it passes null to the onSuccess() method. The service calls the onFailure() method instead if there's an error processing the request. For a detailed explanation of the workflow, see Basic usage. The following code shows an example of the onFillRequest() method:

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for nodes to fill out.
    ParsedStructure parsedStructure = parseStructure(structure);

    // Fetch user data that matches the fields.
    UserData userData = fetchUserData(parsedStructure);

    // Build the presentation of the datasets
    RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
    RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
    passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

    // Add a dataset to the response
    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId,
                            AutofillValue.forText(userData.username), usernamePresentation)
                    .setValue(parsedStructure.passwordId,
                            AutofillValue.forText(userData.password), passwordPresentation)
                    .build())
            .build();

    // If there are no errors, call onSuccess() and pass the response
    callback.onSuccess(fillResponse);
}

class ParsedStructure {
    AutofillId usernameId;
    AutofillId passwordId;
}

class UserData {
    String username;
    String password;
}

A service could have more than one dataset that satisfies the request. In this case, the Android system shows multiple options—one for each dataset—in the autofill UI. The following code example shows how to provide multiple datasets in a response:

// Add multiple datasets to the response
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user1Data.username), username1Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user1Data.password), password1Presentation)
                .build())
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(user2Data.username), username2Presentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(user2Data.password), password2Presentation)
                .build())
        .build();

Autofill services can navigate the ViewNode objects in the AssistStructure to retrieve the autofill data required to fulfill the request. A service can retrieve autofill data using methods of the ViewNode class, such as getAutofillId(). A service should be able to describe the contents of a view to check if it can satisfy the request. Using the autofillHints attribute is the first approach that a service should use to describe the contents of a view. However, client apps must explicitly provide the attribute in their views before it is available to the service. If a client app doesn't provide the autofillHints attribute, a service should use its own heuristics to describe the contents. The service can use methods of other classes to get information about the contents of the view, such as getText() or getHint(). For more information, see Providing hints for autofill. The following example shows how to traverse the AssistStructure and retrieve autofill data from a ViewNode object:

public void traverseStructure(AssistStructure structure) {
    int nodes = structure.getWindowNodeCount();

    for (int i = 0; i < nodes; i++) {
        WindowNode windowNode = structure.getWindowNodeAt(i);
        ViewNode viewNode = windowNode.getRootViewNode();
        traverseNode(viewNode);
    }
}

public void traverseNode(ViewNode viewNode) {
    if(viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {
        // If the client app provides autofill hints, you can obtain them using:
        // viewNode.getAutofillHints();
    } else {
        // Or use your own heuristics to describe the contents of a view
        // using methods such as getText() or getHint().
    }

    for(int i = 0; i < viewNode.getChildCount(); i++) {
        ViewNode childNode = viewNode.getChildAt(i);
        traverseNode(childNode);
    }
}

Save user data

An autofill service needs user data to fill out views in apps. When users manually fill out a view, they're prompted to save the data to the current autofill service, as shown in figure 2.

Autofill save UI

Figure 2. Autofill save UI

To save the data, the service must indicate that it is interested in storing the data for future use. Before the Android system sends a request to save the data, there is a fill request where the service has the opportunity to fill out the views. To indicate that it is interested in saving the data, the service includes a SaveInfo object to the response of the precedent fill request. The SaveInfo object contains at least the following data:

A SaveInfo object is associated with a FillResponse object, as shown in the following code example:

@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
    ...
    // Builder object requires a non-null presentation.
    RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

    FillResponse fillResponse = new FillResponse.Builder()
            .addDataset(new Dataset.Builder()
                    .setValue(parsedStructure.usernameId, null, notUsed)
                    .setValue(parsedStructure.passwordId, null, notUsed)
                    .build())
            .setSaveInfo(new SaveInfo.Builder(
                    SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
                    new AutofillId[] {parsedStructure.usernameId, parsedStructure.passwordId})
                    .build())
            .build();
    ...
}

The autofill service can implement logic to persist the user data in the onSaveRequest() method, which is usually called after the client activity finishes or when the client app calls commit(). The following code shows an example of the onSaveRequest() method:

@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
    // Get the structure from the request
    List<FillContext> context = request.getFillContexts();
    AssistStructure structure = context.get(context.size() - 1).getStructure();

    // Traverse the structure looking for data to save
    traverseStructure(structure);

    // Persist the data, if there are no errors, call onSuccess()
    callback.onSuccess();
}

Autofill services should encrypt sensitive data before persisting it. However, user data includes labels or data that isn't sensitive. For example, a user account can include a label that marks the data as a work or a personal account. Services shouldn't encrypt labels, which allows them to use the labels in presentation views if the user hasn't authenticated, and substitute the labels with the actual data after the user authenticates.

Require user authentication

Autofill services can provide an additional level of security by requiring the user to authenticate before it can fill out views. The following scenarios are good candidates to implement user authentication:

In a scenario where the service requires user authentication before unlocking the data, the service can present boilerplate data or a label and specify the Intent that takes care of authentication. If you need additional data to process the request after the authentication flow is done, you can add such data to the intent. Your authentication activity can then return the data to the AutofillService class in your app. The following code example shows how to specify that the request requires authentication:

RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, "requires authentication");
Intent authIntent = new Intent(this, AuthActivity.class);

// Send any additional data required to complete the request.
authIntent.putExtra(MY_EXTRA_DATASET_NAME, "my_dataset");
IntentSender intentSender = PendingIntent.getActivity(
                this,
                1001,
                authIntent,
                PendingIntent.FLAG_CANCEL_CURRENT
        ).getIntentSender();

// Build a FillResponse object that requires authentication.
FillResponse fillResponse = new FillResponse.Builder()
        .setAuthentication(autofillIds, intentSender, authPresentation)
        .build();

Once the activity completes the authentication flow, it should call the setResult() method passing a RESULT_OK value and set the EXTRA_AUTHENTICATION_RESULT extra to the FillResponse object that includes the populated dataset. The following code shows an example of how to return the result once the authentication flows completes:

Intent intent = getIntent();

// The data sent by the service and the structure are included in the intent.
String datasetName = intent.getStringExtra(MY_EXTRA_DATASET_NAME);
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
ParsedStructure parsedStructure = parseStructure(structure);
UserData userData = fetchUserData(parsedStructure);

// Build the presentation of the datasets.
RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");

// Add the dataset to the response.
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                .setValue(parsedStructure.usernameId,
                        AutofillValue.forText(userData.username), usernamePresentation)
                .setValue(parsedStructure.passwordId,
                        AutofillValue.forText(userData.password), passwordPresentation)
                .build())
        .build();

Intent replyIntent = new Intent();

// Send the data back to the service.
replyIntent.putExtra(MY_EXTRA_DATASET_NAME, datasetName);
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);

setResult(RESULT_OK, replyIntent);

In the scenario where a credit card dataset needs to be unlocked, the service can display UI asking for the CVC. You can hide the data until the dataset is unlocked by presenting boilerplate data, such as the name of the bank and the last four digits of the credit card number. The following example shows how to require authentication for a dataset and hide the data until the user provides the CVC:

// Parse the structure and fetch payment data.
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build the presentation that shows the bank and the last four digits of the credit card number.
// For example 'Bank-1234'.
String maskedPresentation = paymentData.bank + "-" +
    paymentData.creditCardNumber.subString(paymentData.creditCardNumber.length - 4);
RemoteViews authPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
authPresentation.setTextViewText(android.R.id.text1, maskedPresentation);

// Prepare an intent that displays the UI that asks for the CVC.
Intent cvcIntent = new Intent(this, CvcActivity.class);
IntentSender cvcIntentSender = PendingIntent.getActivity(
        this,
        1001,
        cvcIntent,
        PendingIntent.FLAG_CANCEL_CURRENT
).getIntentSender();

// Build a FillResponse object that includes a Dataset that requires authentication.
FillResponse fillResponse = new FillResponse.Builder()
        .addDataset(new Dataset.Builder()
                // The values in the dataset are replaced by the actual
                // data once the user provides the CVC.
                .setValue(parsedStructure.creditCardId, null, authPresentation)
                .setValue(parsedStructure.expDateId, null, authPresentation)
                .setAuthentication(cvcIntentSender)
                .build())
        .build();

Once the activity validates the CVC, it should call the setResult() method passing a RESULT_OK value and set the EXTRA_AUTHENTICATION_RESULT extra to a Dataset object that contains the credit card number and expiration date. The new dataset replaces the dataset that requires authentication and the views are filled out immediately. The following code shows an example of how to return the dataset once the user provides the CVC:

// Parse the structure and fetch payment data.
ParsedStructure parsedStructure = parseStructure(structure);
Payment paymentData = fetchPaymentData(parsedStructure);

// Build a non-null RemoteViews object that we can use as the presentation when
// creating the Dataset object. This presentation isn't actually used, but the
// Builder object requires a non-null presentation.
RemoteViews notUsed = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);

// Create a dataset with the credit card number and expiration date.
Dataset responseDataset = new Dataset.Builder()
        .setValue(parsedStructure.creditCardId,
                AutofillValue.forText(paymentData.creditCardNumber), notUsed)
        .setValue(parsedStructure.expDateId,
                AutofillValue.forText(paymentData.expirationDate), notUsed)
        .build();

Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, responseDataset);

Organize the data in logical groups

Autofill services should organize the data in logical groups that isolate concepts from different domains. These logical groups are referred to as partitions in this page. The following list shows typical examples of partitions and fields:

An autofill service that correctly partitions data is able to better protect the data of its users by not exposing data from more than one partition in a dataset. For example, a dataset that includes credentials not necessarily should include payment information. Organizing your data in partitions allows your service to expose the minimum amount of information required to satisfy a request.

Organizing the data in partitions enables services to fill activities that have views from multiple partitions while sending the minimum amount of data to the client app. For example, consider an activity that includes views for username, password, street, and city, and an autofill service that has the following data:

Partition Field 1 Field 2
Credentials work_username work_password
personal_username personal_password
Address work_street work_city
personal_street personal_city

The service can prepare a dataset that includes the credentials partition for both, the work and personal accounts. When the user chooses one, a subsequent autofill response can provide either, the work or the personal address, depending on the user's first choice.

A service can identify the field that originated the request by calling the isFocused() method while traversing the AssistStructure object. This allows services to prepare a FillResponse with the appropriate partition data.

Advanced autofill scenarios

Paginate datasets
A large autofill response can exceed the allowed transaction size of the Binder object that represents the remotable object required to process the request. To prevent Android system from throwing an exception in these scenarios, you can keep the FillResponse small by adding no more than 20 Dataset objects at a time. If your response needs more datasets, you can add a dataset that lets users know that there's more information and retrieves the next group of datasets when selected. For more information, see addDataset(Dataset).
Saving data split in multiple screens

Apps often split the user data in multiple screens in the same activity, especially in activities used to create a new user account. For example, the first screen asks for a username, and if the username is available, it moves to a second screen, which asks for a password. In these situations, the autofill service must wait until the user enters both fields before the autofill save UI can be shown. A service can follow these steps to handle such scenarios:

  1. In the first fill request, the service adds a client state bundle in the response, containing the autofill IDs of the partial fields present in the screen.
  2. In the second fill request, the service retrieves the client state bundle, gets the autofill IDs set in the previous request from the client state, and adds these IDs and the FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE flag to the SaveInfo object used in the second response.
  3. In the save request, the service uses the proper FillContext objects to get the value of each field. There is one fill context per fill request.

For more information, see Saving when data is split in multiple screens.

Provide initialization and teardown logic for each request

Every time there's an autofill request, the Android system binds to the service and calls its onConnected() method. Once the service processes the request, the Android system calls the onDisconnected() method and unbinds from the service. You can implement onConnected() to provide code that runs before processing a request and onDisconnected() to provide code that runs after processing a request.

Customize the autofill save UI

Autofill services can customize the autofill save UI to help users decide if they want to allow the service to save their data. Services can provide additional information about what would be saved either through a simple text or through a customized view. Services can also change the appearance of the button that cancels the save request and get a notification when the user taps that button. For more information, see the SaveInfo reference documentation.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)