Account Transfer API

用户可以使用一键恢复功能将 Google 账号和数据从现有 Android 设备复制到新的 Android 设备。借助 Account Transfer API,还可以让用户复制使用 AbstractAccountAuthenticator 实现并与 AccountManager 集成的自定义账号的凭据。系统会从新设备上运行的“一键恢复”设置向导调用 Account Transfer API。系统还会调用 Account Transfer API,以使用数据线将数据从 Android 手机转移到 Pixel

“一键恢复”欢迎屏幕。 用于选择数据源的“一键恢复”屏幕。

图 1. 系统从新设备上运行的“一键恢复”设置向导调用 Account Transfer API。

如需添加自定义账号转移支持,请在您的应用中集成 Account Transfer API。然后,Google Play 服务可以在现有设备(也称为源设备)和新设备(也称为目标设备)之间建立一个转移账号数据的双向加密通道,如图 2 所示。加密通道无需连接到第三方服务器即可完成转移。

在应用中集成 Account Transfer API 时,请满足以下要求:

  • 源设备必须搭载 Android 4.0.1(API 级别 14)或更高版本。
  • 目标设备必须搭载 Android 8.0(API 级别 26)或更高版本。
  • 源设备和目标设备均必须运行 Google Play 服务 11.2.0 或更高版本。
  • 您必须使用 Google Play 服务 SDK 11.2.0 或更高版本构建应用。

从源设备向目标设备转移账号的图示。

图 2. 转移通过 Google Play 服务在源设备和目标设备之间建立的加密通道进行。

将 Account Transfer API 添加到项目中

如需在您的项目中使用 Account Transfer API,您首先需要使用 Google Play 服务 SDK 设置项目。如需了解有关设置 Google Play 服务 SDK 的详细说明,请参阅设置 Google Play 服务

如果您希望选择性地将 Google Account Transfer API 编译到应用中,请将以下构建规则添加到应用模块目录内 build.gradle 文件的 dependencies 代码块中:

Groovy

plugins {
  id 'com.android.application'
}
...
dependencies {
    // VERSION_NUMBER must be equal to or higher than 11.2.0.
    implementation 'com.google.android.gms:play-services-auth:<VERSION_NUMBER>'
}

Kotlin

plugins {
    id("com.android.application")
}
...
dependencies {
    // VERSION_NUMBER must be equal to or higher than 11.2.0.
    implementation("com.google.android.gms:play-services-auth:<VERSION_NUMBER>")
}

如需添加自定义账号转移支持,您必须在应用的清单中为身份验证器服务声明 START_ACCOUNT_EXPORT 广播接收器:

<receiver android:name=".MyBroadcastReceiver"  android:exported="true">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.START_ACCOUNT_EXPORT"/>
        ...
    </intent-filter>
</receiver>

如果您的应用未安装在 OEM 系统映像中,并且您也不打算将应用放入 OEM 系统映像中,请确保注册并监听源设备上的 ACTION_START_ACCOUNT_EXPORT 广播,以导出账号数据(如上所述)。

如果您将应用安装在 OEM 系统映像上,则还必须注册以下广播:

转移账号数据

用户选择从现有设备中恢复内容后,系统会向与源设备上的相关账号关联的软件包发送 ACTION_START_ACCOUNT_EXPORT 广播。

发送账号数据

如需发送账号数据,请在源设备上启动身份验证器服务,然后在该服务收到 ACTION_START_ACCOUNT_EXPORT 广播后调用 sendData()。您可以通过调用 getAccountTransferClient(Context)getAccountTransferClient(Activity) 引用 AccountTransferClient 对象。 以下代码段说明了如何从源设备发送数据:

Kotlin

val client: AccountTransferClient = AccountTransfer.getAccountTransferClient(this)
val exportTask: Task<Void> = client.sendData(ACCOUNT_TYPE, transferBytes)
try {
    // Wait for the task to either complete or provide the callback.
    Tasks.await(exportTask, TIMEOUT_API, TIME_UNIT)
} catch (e: Exception) {
    when(e) {
        is ExecutionException, is InterruptedException, is TimeoutException -> {
            client.notifyCompletion(ACCOUNT_TYPE,
                    AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE)
            return
        }
        else -> throw e
    }
}

Java

AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
Task<Void> exportTask = client.sendData(ACCOUNT_TYPE, transferBytes);
try {
  // Wait for the task to either complete or provide the callback.
  Tasks.await(exportTask, TIMEOUT_API, TIME_UNIT);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
  client.notifyCompletion(ACCOUNT_TYPE,AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
  return;
}

目标设备上的设置向导会接收账号数据。

接收账号数据

如果该设备上安装了同一身份验证器服务,并已通过监听 ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE 广播注册了其关注的数据,目标设备会向相应软件包发送 ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE 广播。

接收到 ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE 广播后,请在目标设备上启动相应服务并调用 retrieveData(),以检索从源设备发来的数据。以下代码段说明了如何在目标设备上检索数据:

Kotlin

val client: AccountTransferClient = AccountTransfer.getAccountTransferClient(this)
val transportTask: Task<Void> = client.retrieveData(ACCOUNT_TYPE)
try {
    val transferBytes: ByteArray = Tasks.await(transferTask, TIMEOUT_API, TIME_UNIT)
    // Add the transferred account(s) to AccountManager to register with the framework.
} catch (e: Exception) {
    when(e) {
        is ExecutionException, is InterruptedException, is TimeoutException -> {
            client.notifyCompletion(ACCOUNT_TYPE,
                    AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE)
            return
        }
        else -> throw e
    }
 }
client.notifyCompletion(ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS)

Java

AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
Task<Void> transferTask = client.retrieveData(ACCOUNT_TYPE);
try {
  byte[] transferBytes = Tasks.await(transferTask, TIMEOUT_API, TIME_UNIT);
  // Add the transferred account(s) to AccountManager to register with the framework.
} catch (ExecutionException | InterruptedException | TimeoutException e) {
  client.notifyCompletion(ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
  return;
}
client.notifyCompletion(ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);

完成转移

如有必要,目标设备上的身份验证器服务也可以通过调用 sendData() 将数据转移回源设备。

如需在源设备上接收数据,身份验证器服务必须监听 ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE 广播。同样,源设备上的身份验证器服务可以向目标设备发送更多消息。

转移完成后,身份验证器服务必须调用 notifyCompletion() 并传入相应的完成状态。

如果您需要更高的安全性,可以在源设备或目标设备上要求用户验证身份。首先,调用 getDeviceMetaData() 并检查结果,以确认是否可以显示身份验证提示。如果目标设备上的身份验证器服务支持显示身份验证提示,则调用 showUserChallenge() 以显示身份验证提示。

如果转移时目标设备上未安装所需的身份验证器服务,系统会将转移的数据存储在临时本地存储空间中。当用户首次安装并打开应用时,应用可以调用 retrieveData() 以检查临时本地存储空间中是否有任何可用数据。如果存在任何可用数据,Account Transfer API 会返回数据,否则调用失败。如果临时本地存储空间中没有任何可用数据,继续尝试检索数据可能会失败。请勿调用 notifyCompletion(),因为可能会失败。

目标设备将转移的数据存储在临时本地存储空间的图示。

图 3. 如果目标设备上未安装所需的身份验证器服务,系统会将转移的数据存储在临时本地存储空间中。

测试账号转移

设置向导会在您设置新设备时运行。通过定期将设备恢复出厂设置的方式测试账号设置和转移结果既麻烦又耗时。您可以运行设置向导的部分流程,以测试将用户的账号从一部设备转移到另一部设备的情况。

开始测试之前,请确保源设备上至少有一个自定义账号。另外,请确保目标设备未登录任何自定义账号。如果在运行设置向导时,目标设备已登录某个自定义账号,在系统调用 AccountManager.addAccountExplicitly() 方法时尝试添加同一账号会失败。

出于测试目的,您必须使用搭载 Android 8.0(API 级别 26)或更高版本的设备作为目标设备。

您可以使用搭载 Android 4.0.1(API 级别 14)或更高版本且运行 Google Play 服务 11.2.0 或更高版本的设备作为源设备。如需构建需要测试的 APK,您必须使用 Google Play 服务 SDK 11.2.0 或更高版本。

如需测试设置向导流程,请在目标设备上运行以下命令:

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

该命令会启动一个 activity,并显示设置向导,准备好将测试设备与另一个测试设备配对。设备建立连接后,您便可以开始账号转移流程了。