Is it a bird? This is a plane? No, this is your user’s token flying to a new phone

  • Tutorial
Hello, Habr!

Today I’ll talk about the API for developers from Google. We will talk about how not to force the user to re-login in the application after data transfer, or, more precisely, how to use the Android Account Transfer API.

Most likely, each of us bought a new smartphone and he had to transfer all important information and applications to it from the old one. Now this process has become quite simple thanks to Tap & Go technology.. But there is one thing. You have to log in again whenever possible. But what if this is an application such as a fitness tracker, where you logged in once and forgot? Recover password? Headache again. You can say: “But there is Smart Lock!” And you will be right, but we must take into account all the cases. What if a person forgot to save a password? Or is he just paranoid and doesn't store passwords? Or is the application not using Smart Lock ? I think that there are always reasons to forget the authorization data. But now there is a solution, and you can ease the burden of transferring your users credentials. Only now it is not for everyone. Yes, and effectively work at least a year later.

Three questions follow from this:

1. What is the Account Transfer API in general?
2. Why is it not for everyone?
3. Why will it work effectively at least in a year?

Necessary Requirements


As I said before, Account Transfer API allows developers to transfer authorization data, including token and account information, from an old phone to a new one. And what is needed for this?

And here we run into the saddest part needed to implement this technology.

What does it take to make it work?

1. An old device with Android version not lower than 4.0.1 (API Level 14).
Not such a problem, you say, and I agree with this, but look further.

2. The new device on which we want to copy the data must be running an OS not lower than Android 8.0 (API Level 26).

This is the answer to the question “Why will it work effectively at least in a year?”. Yes, this year many devices will receive an Android update to Oreo, but they are unlikely to be sold with an already built-in OS with the necessary API Level. Most likely, the stock versions will remain on the shelves, which will have a maximum of Android Marshmallow on board. And after the purchase they will have to be updated manually. And this is the key moment for our feature. It is very likely that the user will first transfer his data to the device, and only then update it.

In order not to be unfounded, I will show you Google information dated February 5, 2018.



3. Both devices must have Google Play version 11.2.0 and higher. This should not be a big problem, unless, of course, you support devices without Google Play. They cannot organize a transfer.

4. You must build your APK using Google Play services no lower than 11.2.0. It can become a problem only if you have reasons not to use the services of this version or not to use services at all.

5. Requires the already implemented AbstractAccountAuthenticator integrated with the AccountManager . There is an excellent cycle of articles on "Habré" on implementation.

How it works?




Provided that all requirements are met, a secure wired connection or encrypted via Bluetooth is created between the phones. If during the transfer you want to check the validity of the token on your backend, then one of the devices must be connected to the Internet.

Next, the system sends a Broadcast message ( ACTION_START_ACCOUNT_EXPORT or ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE ) about its readiness to start exporting data, upon receipt of which we must start the Foreground Service , intended for data exchange. Those. we need to generate and send user account data via a secure connection to the recipient device.





After that, the system sends another Broadcast message (ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE ) is already on the destination device. Here you also need to run the Foreground Service and process the received information.



If a situation arises in which a service on the recipient device cannot be created at the time of data transportation, the system will save user data in temporary storage. Data will be received when you first start the application.



Implementation


Finally, you decide to begin implementation.

First you need to connect play-services-auth, if this has not been done before.

dependencies {
  // For Account Transfer api, use SDK version11.2.0or higher
  compile 'com.google.android.gms:play-services-auth-base:<VERSION_NUMBER>'
}

Next, declare BroadcastReceiver and Service in the manifest:

<receiverandroid:name=".AccountTransferBroadcastReceiver"android:enabled="true"android:exported="true" ><intent-filter><actionandroid:name="com.google.android.gms.auth.START_ACCOUNT_EXPORT" /></intent-filter><intent-filter><actionandroid:name="com.google.android.gms.auth.ACCOUNT_IMPORT_DATA_AVAILABLE" /></intent-filter><intent-filter><actionandroid:name="com.google.android.gms.auth.ACCOUNT_EXPORT_DATA_AVAILABLE" /></intent-filter></receiver><serviceandroid:name=".AuthenticatorService"><intent-filter><actionandroid:name="android.accounts.AccountAuthenticator"/></intent-filter><meta-dataandroid:name="android.accounts.AccountAuthenticator"android:resource="@xml/authenticator"/></service>

As described above, the service must be started upon receipt of the corresponding Broadcast messages:

publicclassAccountTransferBroadcastReceiverextendsBroadcastReceiver{
  @OverridepublicvoidonReceive(Context context, Intent intent){
     // Long running tasks, like calling Account Transfer API, shouldn't happen here. Start a// foreground service to perform long running tasks.
     Intent serviceIntent = AccountTransferService.getIntent(context, intent.getAction());
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(serviceIntent);
     } else {
        context.startService(serviceIntent);
     }
  }
}

Well and accordingly, a partial implementation of the service:

protectedvoidonHandleIntent(Intent intent){
  String action = intent.getAction();
  if (action == null) {
     return;
  }
  switch (action) {
     case AccountTransfer.ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE:
        importAccount();
        return;
     case ACTION_START_ACCOUNT_EXPORT:
     case AccountTransfer.ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE:
        exportAccount();
        return;
  }
}
privatevoidimportAccount(){
  // Handle to client object
  AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
  // Make RetrieveData api call to get the transferred over data.
  Task<byte[]> transferTask = client.retrieveData(ACCOUNT_TYPE);
  try {
     byte[] transferBytes = Tasks.await(transferTask, TIMEOUT_API, TIME_UNIT);
     //import logic
  } catch (ExecutionException | InterruptedException | TimeoutException | JSONException e) {
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
     return;
  }
  client.notifyCompletion(
        ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);
}
privatevoidexportAccount(){
  byte[] transferBytes = Foo.yourOwnMethodToGetAccountTransferBytes();
  AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
  if (transferBytes == null) {
     // Notifying is important.
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);
     return;
  }
  // Send the data over to the other device.
  Task<Void> exportTask = client.sendData(ACCOUNT_TYPE, transferBytes);
  try {
     Tasks.await(exportTask, TIMEOUT_API, TIME_UNIT);
  } catch (ExecutionException | InterruptedException | TimeoutException e) {
     // Notifying is important.
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
     return;
  }
}

A more detailed example can be found in the googlesamples repository .

For testing, Google suggests launching the Setup Wizard command.

$ adb shell am start -a android.intent.action.MAIN -n com.google.android.gms/.smartdevice.d2d.ui.TargetActivity

And that's all, this is enough for your user to forget about the headache and unnecessary actions when working with your application after reinstalling it on a new device. Perhaps this is precisely what will be the key point that will make it clear to the user whether to continue using the application or to remove it and forget it.

Summing up, I note that Google wants to minimize the time that the user spends on re-authorization, and makes logical steps for this. And it would be nice to support them in this direction. Although it requires some time-consuming implementation, it is worth it. After all, the devil is in the details.

Interesting information on this topic:

Documentation
Video introduction Implementation
example

Also popular now: