实现用户账号

Android 企业版注册主要有两种用户身份类型:受管理的 Google Play 账号和受管理的 Google 账号。受管理的 Google Play 账号以设备为中心,这意味着它们与特定用户的 Google 身份无关。相比之下,受管理的 Google 账号会关联到用户的公司 Google 身份,从而让用户在设备上保持登录状态,进而提升用户体验。

过去,Google Play 企业版账号是标准配置。不过,Google 现在鼓励所有新开发项目使用改进后的注册流程,该流程默认创建受管理的 Google 账号。

虽然本文档末尾提供了有关旧版实现的指南以供参考,但所有新开发项目都应遵循此处详述的新注册流程。

概览

改进后的设备注册流程利用了多个新组件,并更改了自定义设备政策控制器 (DPC) 的实现方式,从而简化了设备设置。这种新方法要求自定义 DPC 解决方案与 Android Management API (AMAPI) SDK 和 Android Device Policy 集成,以执行设备准备和用户注册功能。

AMAPI SDK 提供了与设备上的 Android 设备政策进行交互所需的 API。在服务器端,企业移动管理 (EMM) 解决方案将使用 Play EMM API 生成启动设备注册流程所需的注册令牌。

Android Device Policy 应用现在在处理设备端操作方面发挥着核心作用。AMAPI SDK 用于管理设备上的安装和必要更新。Android 设备政策还会接管用户身份验证流程,直接处理用户身份验证并将用户身份提供给 EMM。如果 Google 因任何原因无法对用户进行身份验证,系统会创建新的 Google Play 企业版账号并将其作为后备账号添加到设备。

API 集成

在开始之前,请确认您使用的是最新版本的 Play EMM API 客户端和 AMAPI SDK。

注册实现指南

本指南提供了实现注册所需的步骤。它涵盖了准备环境、处理不同的注册方法以及管理设备生命周期。

准备环境

在开始设置账号之前,必须先准备好设备环境。此准备工作包括将 Play 商店更新到最新版本,以及在设备上静默安装 Android Device Policy (com.google.android.apps.work.clouddpc)。Android Device Policy 安装至关重要,因为其中包含账号设置流程的关键组件。EMM 无需执行手动环境准备。相反,他们应使用 EnvironmentClient,如文档中所述,并遵循提供的代码示例。

示例代码

在能够使用 AccountSetup API 在设备上添加工作账号之前,DPC 必须先验证设备环境是否已准备就绪。

  • 使用 EnvironmentClientFactory 实例化 EnvironmentClient 并调用 prepareEnvironmentprepareEnvironmentAsync

    val notificationReceiverServiceName = ComponentName(context,
    NotificationReceiver::class.java)
    
    // An EMM should implement android.app.admin.DeviceAdminReceiver and use that
    // class to instantiate a ComponentName
    
    val admin = ComponentName(this, com.example.dpc.DeviceAdminReceiver::class.java)
    
    EnvironmentClientFactory.create(context)
        .prepareEnvironment(
            PrepareEnvironmentRequest.builder()
                .setRoles(
                    listOf(
                        Role.builder().setRoleType(
                            Role.RoleType.DEVICE_POLICY_CONTROLLER
                        ).build()
                    )
                )
        .setAdmin(admin)
                .build(),
              notificationReceiverServiceName,
            )
    
    [Proceed with AccountSetup]
    
    

此操作可能需要几秒或几分钟,因为系统可能会安装或更新应用,以验证工作环境是否正常。Google 建议在后台尽早开始此流程,并在用户等待时显示适当的界面。当操作完成时,设备已准备好供 DPC 使用 AccountSetup API。

注册流程

EMM 必须停止为所有设备使用 users.generateAuthenticationToken()users.insert()。EMM 必须调用设备端 API 来执行最终用户身份验证。新 API 会将 userIdemail 返回给 DPC。如果 Google 无法对用户进行身份验证,系统会创建 Google Play 企业版账号并将其添加到设备。在这种情况下,Google 将返回相应账号的 userId

Google 现在引入了注册令牌的使用,该令牌必须传递给身份验证 API。EMM 会确定何时以及如何创建令牌,并且该令牌可以作为现有注册载荷(例如二维码或零接触配置)的一部分。

不过,Google 建议按需创建令牌,并使用新 API 替换现有 API 以管理 Google Play 管理式账号,从而尽可能减少更改。

DPC 与旧版 API 的典型集成
图 1. DPC 与之前 API 的典型集成
DPC 与无用户设备的新 API 集成的示例
图 2. 示例:DPC 与无用户设备的新 API 集成
示例:将 DPC 与用户设备的新 API 集成
图 3. 示例:DPC 与用户设备的新 API 集成

改进后的自定义 DPC 注册流程包括以下步骤:

  1. 创建注册令牌:EMM 使用 Play EMM API 创建注册令牌。
  2. 准备环境:自定义 DPC 使用准备环境流程来验证设备是否已准备好进行注册。
  3. 启动注册:自定义 DPC 调用 AMAPI SDK 中的 startAccountSetup API,并传递注册令牌。 注意:在调用此 API 之前,DPC 必须是设备所有者或资料所有者。
  4. 启动 Google 身份验证 activity:如果需要,自定义 DPC 会在 AMAPI SDK 中调用 launchAuthenticationActivity API,并传递 AccountSetupAttempt。此方法会启动 Google 身份验证 activity,并在身份验证成功后将用户返回到自定义 DPC。用户也可以跳过此流程。在这种情况下,系统会将 Google Play 企业版账号添加到设备中。可以使用 googleAuthenticationOptions 配置此选项。
  5. 完成注册:AMAPI SDK 会将注册结果通知自定义 DPC。
  6. 启用 Google 服务:当用户使用受管理的 Google 账号的设备符合企业政策时,EMM 必须调用 Devices.setState()。此操作可让设备上的账号访问 Google 服务。 如果没有此调用,Play 商店和其他 Google 服务将无法正常运行。

账号设置 - 示例代码

  1. 如需发起账号设置尝试,调用应用可以使用 AccountSetupClient 并调用 startAccountSetup()startAccountSetupFuture() 方法。如需查看实现示例,请参阅以下代码示例:

    // Create AccountSetupClient
    val client = AccountSetupClientFactory.create(
        this,
        activityResultRegistry
    )
    lifecycle.addObserver(client.lifecycleObserver)
    
    // Create adminComponent
    val notificationReceiver = ComponentName(this, AccountSetupNotificationReceiver::class.java)
    // Helper method to get enrollment token created with Play EMM API
    val enrollmentToken = getEnrollmentToken()
    val request =
              StartAccountSetupRequest.builder()
                  .setEnrollmentToken(enteredText)
                  .setNotificationReceiverServiceComponentName(notificationReceiver)
                  .setAdminComponentName(
                      ComponentName(this, com.example.dpc.DeviceAdminReceiver::class.java))
                  .build()
    try {
        val accountSetupAttempt = client.startAccountSetup(request)
        // handle attempt
    } catch (e: Exception) {
        // handle exception
    }
      ```
    
  2. 实现 AccountSetupListener 接口,并提供有关如何处理收到的状态更新的实现。

  3. 扩展 NotificationReceiverService 并通过替换 getAccountSetupListener() 提供第 2 步中创建的 AccountSetupListener 实例。

    // Handles account setup changes
    class AccountSetupNotificationReceiver :
          NotificationReceiverService(),
          AccountSetupListener {
    
        override fun getAccountSetupListener(): AccountSetupListener = this
    
        override fun onAccountSetupChanged(accountSetupAttempt:
      AccountSetupAttempt) {
    
            when (accountSetupAttempt.state.kind) {
                StateCase.ADDED_ACCOUNT -> {
                    val enterpriseAccount = state.addedAccount()
                    val userId = enterpriseAccount.userId
                    val deviceId = enterpriseAccount.deviceId
                    // Handle account added state.
    
                }
                StateCase.AUTHENTICATION_ACTIVITY_LAUNCH_REQUIRED -> {
                    val request = LaunchAuthenticationActivityRequest.builder()
                .setAccountSetupAttempt(accountSetupAttempt)
                .build();
                    // Send the attempt to the foreground activity to call:
                    accountSetupClient.launchAuthenticationActivity(request)
                }
                StateCase.ACCOUNT_SETUP_ERROR -> {
                    // Handle error state.
                    val failureReason = state.accountSetupError().failureReason
                }
                else -> {
                    // Handle unknown account setup attempt state.
                }
            }
        }
    }
    
      ```
    
  4. 将扩展的 NotificationReceiverService 类添加到 AndroidManifest.xml,并验证它是否已导出。

      <application>
        <service
            android:name = ".accountsetup.AccountSetupNotificationReceiver"
            android:exported = "true" />
      </application>
    

    如果您的应用以 SDK 30 或更高版本为目标平台,则需要在 AndroidManifest.xml 中添加 queries 元素,以指定该应用将与 ADP 互动。

      <queries>
        <package android:name="com.google.android.apps.work.clouddpc" />
      </queries>
    

测试指南

本部分提供了一组用于测试实现情况的指南和最佳实践。

测试 PrepareEnvironment

  1. 获取设备的当前状态:EMM 运行

    adb shell dumpsys package com.google.android.apps.work.clouddpc | grep versionName
    

    以获取设备上存在的 Android Device Policy 的版本。如果未安装 Android 设备政策,则预期输出为空。

  2. 集成 PrepareEnvironment:自定义 DPC 在 AMAPI SDK 中调用 prepareEnvironment API,并传递正确的请求。

  3. 等待 PrepareEnvironment 结果:自定义 DPC 等待 prepareEnvironment 完成。

  4. 确认 PrepareEnvironment 成功:完成后,EMM 会再次运行

    adb shell dumpsys package com.google.android.apps.work.clouddpc | grep versionName
    

    这次,Android 设备政策版本应高于第 1 步中的版本。

测试 Google 账号身份验证

  1. 创建测试企业:EMM 创建一个与测试 EMM 相关联的测试网域 Google 企业,并带有 enterprises.generateSignupUrl
  2. 启用 Google 身份验证:EMM 按照 Google 管理控制台中的这些说明为测试企业启用 Google 身份验证。
  3. 创建注册令牌:EMM 使用 Play EMM API 创建类型为 userDevice 的注册令牌。
  4. 启动注册:自定义 DPC 调用 AMAPI SDK 中的 startAccountSetup API,并传递注册令牌。
  5. 需要启动 activity:AMAPI SDK 会通知自定义 DPC 必须启动 activity 才能对用户进行身份验证。
  6. 对用户进行身份验证:自定义 DPC 调用 launchAuthenticationActivity 以启动 activity。用户使用受管理的 Google 账号(在第 1 步中创建的企业账号的一部分)进行身份验证。
  7. 完成注册:AMAPI SDK 会将注册结果通知自定义 DPC。

测试跳过 Google 身份验证

我们将使用之前描述的设置。

这次,在第 7 步中,用户按了跳过,而不是使用 Google 账号进行身份验证。注册成功完成,设备上有一个服务账号(即 AuthenticationType 为匿名)。

测试无用户设备

改进后的自定义 DPC 注册流程在 Google 身份验证被停用时会执行以下步骤:

  1. 创建测试企业:这可以与之前创建的企业相同。
  2. 创建注册令牌:EMM 使用 Play EMM API 创建类型为 userlessDevice 的注册令牌。
  3. 启动注册:自定义 DPC 调用 AMAPI SDK 中的 startAccountSetup API,并传递注册令牌。
  4. 完成注册:AMAPI SDK 会将注册结果通知自定义 DPC。