I'm an independent developer of a password manager app. What should I do, or what should I implement (interface/API/Service), to make my app an Autofill service provider (in a device with Android Oreo API >= 26)?
I have read all kinds of related documentation, but I can't understand how to do this. Am I missing something?
At the moment I see that only well-known password managers support this feature:
Any hints are welcome.
As usual, Google's own examples repository provides a good starting point for learning the Autofill Framework's API, and covers much more material than I can fit into an answer. Here's an overview of the key concepts. From the description in the documentation, we want to create an Autofill service that will handle requests from other apps (the clients) to store and retrieve Autofill field data.
First, we need to create a service provider class that fulfills this contract. We can extend the base AutofillService
class:
import android.service.autofill.AutofillService;
...
public class MyAutofillService extends AutofillService {
...
@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) { ... }
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) { ... }
}
The service's onFillRequest()
and onSaveRequest()
methods are the most significant for our understanding. The Android system calls onFillRequest()
to determine if our service can autofill fields for a particular activity, and gives the method a FillRequest
which contains the context and view information that our service will examine for fillable fields. When the service finishes, it invokes the provided callback
with the appropriate autofill data.
Here's a dramatically simplified overview of the basic steps needed to provide autofill suggestions for a FillRequest
:
@Override
public void onFillRequest(FillRequest request, CancellationSignal signal, FillCallback callback) {
List<FillContext> contexts = request.getFillContexts();
AssistStructure structure = contexts.get(contexts.size() - 1);
WindowNode windowNode = structure.getWindowNodeAt(0);
ViewNode viewNode = windowNode.getRootViewNode(); // pretend this is an EditText
String suggestionText = "This will appear in the autofill list for 'viewNode'.";
RemoteViews suggestion = new RemoteViews(getPackageName(), R.layout.autofill_suggestion);
suggestion.setTextViewText(R.id.autofill_suggestion, suggestionText);
Dataset suggestionDataset = new Dataset.Builder(suggestion)
.setValue(viewNode.getAutoFillId(), AutofillValue.forText(suggestionText))
.build();
FillResponse response = new FillResponse.Builder()
.addDataset(suggestionDataset)
.build();
callback.onSuccess(response);
}
As we can see, the Autofill API requires a lot of code just to provide a single, static autofill suggestion for a View
that we already know in advance—the example assumes that viewNode
is a text input field that we want to provide autofill suggestions for. In reality, this example is too simple, but I wanted to clearly show the minimum implementation. For each WindowNode
, we need to walk through the view tree of the root ViewNode
and each of its children to find each of the input fields that our service wishes to provide autofill data for, and then create a RemoteViews
and Dataset
that contains the autofill suggestion for each field that we'll add to the FillResponse
using FillResponse.Builder.addDataset()
. This example doesn't show the plain XML layout for the R.layout.autofill_suggestion
TextView
used to create the suggestion display item for a RemoteViews
.
Similarly, Android calls onSaveRequest()
when a user wants to save the data in an activity's fields for future completion requests and injects a SaveRequest
that our service uses to find autofill data to remember.
The specific implementation of each of these methods will depend on the type of Autofill data that our app provides. Autofill services must conscientiously examine the characteristics of each field and carefully select a set of appropriate autofill suggestions to avoid leaking the user's data to a malicious client activity (see comments). For a password manager in particular, we need to pay special attention to properly authenticating a user of the service and providing a safe set of suggestions when requesting and saving autofill data.
We can now register the service in the <application>
block of the project's AndroidManifest.xml:
<service
android:name=".MyAutofillService"
android:label="Multi-Dataset Autofill Service"
android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<meta-data
android:name="android.autofill"
android:resource="@xml/multidataset_service" />
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
</service>
As shown, this binds our Autofill service as an available option that appears in the Autofill services list show in the question. The android:name
attribute must match the name of our AutofillService
class, and our app must declare the BIND_AUTOFILL_SERVICE
permission. Change the value of android:label
to a suitable name for the service (for example, "Autofill with Password Manager"). Alternatively, set this in a string resource. Note also that we should provide a "settings" activity used to configure our service which we specify in the <meta‑data>
for android.autofill
:
<autofill-service android:settingsActivity="foo.bar.SettingsActivity" />
Then, the user can enable our Autofill service from their device Settings. We can broadcast the ACTION_REQUEST_SET_AUTOFILL_SERVICE
intent during setup or first launch to assist the user in finding this screen.