EMM 集成指南

本指南可帮助企业移动管理 (EMM) 提供商集成 在控制台上进行零触摸注册。 请继续阅读,详细了解注册事宜,并查看 帮助您的 DPC(设备政策控制器)配置设备。如果您有设备政策控制器 (DPC), 您将了解配置设备时的最佳做法,并获取关于 开发和测试

面向 IT 管理员的功能

使用客户 API 可帮助 IT 管理员直接从控制台中设置零触摸注册。以下是 IT 管理员可能会在您的控制台中完成的一些任务:

  • 创建、修改和删除零触摸注册配置,具体取决于您的 移动政策。
  • 设置默认配置,以便您的 DPC 配置未来设备 组织购买。
  • 为设备应用单独的配置,或为设备移除零触摸注册 注册。

要详细了解零触摸注册,请阅读 概览

前提条件

在向 EMM 控制台添加零触摸注册之前,请确认您的 解决方案支持以下各项:

  • 您的 EMM 解决方案需要配置公司自有的 Android 8.0 及更高版本 (Pixel 7.1 及更高版本) 处于全托管式模式公司自有 Android 10 及更高版本设备可以配置为完全托管设备或设有工作资料的设备
  • 由于零触摸注册会自动下载并安装设备政策控制器 (DPC),因此您的 设备政策控制器 (DPC) 必须可从 Google Play 下载。我们维护了一个兼容设备政策控制器 (DPC) 列表 IT 管理员可以使用客户 API 或门户网站进行配置。提交 通过 EMM 提供商社区提交产品修改请求 将您的 DPC 添加到列表中。
  • 您的客户需要拥有零触摸注册账号才能调用客户 API。 当发生以下情况时,合作伙伴转销商会为 IT 管理员的组织设置账号: 组织购买其设备。
  • 设备必须与 Google 移动服务 (GMS) 兼容 和 Google Play 服务必须始终启用零触摸注册 才能正常运行。

调用该 API

您控制台的用户(使用其 Google 账号)将您的 API 请求授权给以下人员: 客户 API。此流程与您针对 其他 EMM API。请阅读授权,了解如何在您的应用中执行此操作。

处理服务条款

您的用户需要先接受最新的《服务条款》 (ToS),然后才能调用该 API。如果 API 调用返回 HTTP 403 Forbidden 状态代码,并且 响应正文包含 TosError,提示用户接受 并登录零触摸注册门户,以遵守服务条款。以下示例展示了实现此目的的一种方法:

Java

// Authorize this method call as a user that hasn't yet accepted the ToS.
final String googleApiFormatHttpHeader = "X-GOOG-API-FORMAT-VERSION";
final String googleApiFormatVersion = "2";
final String tosErrorType =
      "type.googleapis.com/google.android.device.provisioning.v1.TosError";

try {
  // Send an API request to list all the DPCs available including the HTTP header
  // X-GOOG-API-FORMAT-VERSION with the value 2. Import the  exception:
  // from googleapiclient.errors import HttpError
  AndroidProvisioningPartner.Customers.Dpcs.List request =
        service.customers().dpcs().list(customerAccount);
  request.getRequestHeaders().put(googleApiFormatHttpHeader, googleApiFormatVersion);
  CustomerListDpcsResponse response = request.execute();
  return response.getDpcs();

} catch (GoogleJsonResponseException e) {
  // Get the error details. In your app, check details exists first.
  ArrayList<Map> details = (ArrayList<Map>) e.getDetails().get("details");
  for (Map detail : details) {
    if (detail.get("@type").equals(tosErrorType)
          && (boolean) detail.get("latestTosAccepted") != true) {
      // Ask the user to accept the ToS. If they agree, open the portal in a browser.
      // ...
    }
  }
  return null;
}

.NET

// Authorize this method call as a user that hasn't yet accepted the ToS.
try
{
    var request = service.Customers.Dpcs.List(customerAccount);
    CustomerListDpcsResponse response = request.Execute();
    return response.Dpcs;
}
catch (GoogleApiException e)
{
    foreach (SingleError error in e.Error?.Errors)
    {
        if (error.Message.StartsWith("The user must agree the terms of service"))
        {
            // Ask the user to accept the ToS. If they agree, open the portal in a browser.
            // ...
        }
    }
}

Python

# Authorize this method call as a user that hasn't yet accepted the ToS.
tos_error_type = ('type.googleapis.com/'
                  'google.android.device.provisioning.v1.TosError')
portal_url = 'https://partner.android.com/zerotouch'

# Send an API request to list all the DPCs available including the HTTP
# header X-GOOG-API-FORMAT-VERSION with the value 2. Import the exception:
# from googleapiclient.errors import HttpError
try:
  request = service.customers().dpcs().list(parent=customer_account)
  request.headers['X-GOOG-API-FORMAT-VERSION'] = '2'
  response = request.execute()
  return response['dpcs']

except HttpError as err:
  # Parse the JSON content of the error. In your app, check ToS exists first.
  error = json.loads(err.content)
  tos_error = error['error']['details'][0]

  # Ask the user to accept the ToS (not shown here). If they agree, then open
  # the portal in a browser.
  if (tos_error['@type'] == tos_error_type
      and tos_error['latestTosAccepted'] is not True):
    if raw_input('Accept the ToS in the zero-touch portal? y|n ') == 'y':
      webbrowser.open(portal_url)

如果您的 Google API 客户端支持详细错误(Java、Python 或 HTTP) 请求),请在 HTTP 标头中添加 X-GOOG-API-FORMAT-VERSION 2。如果您的客户端不支持详细错误(.NET 和 与错误消息一致。

日后我们更新服务条款时,如果您遵循此方法,您的应用 会引导用户重新接受新的服务条款。

IT 管理员可使用零触摸注册门户为自己的用户管理 组织,则无法通过客户 API 提供此 API。IT 管理员还可以 通过该门户管理设备和配置。如果您需要链接到 门户网站或文档,请使用此网址:

https://partner.android.com/zerotouch

您可能需要告知 IT 管理员,系统会提示他们使用 Google 账号登录。

设备注册

零触摸注册是设备注册的一种机制,与 NFC 类似 注册或二维码注册。您的控制台需要支持受管设备,并且您的 DPC 必须能够在完全受管设备模式下运行。

零触摸注册适用于搭载 Android 8.0 或 。IT 管理员必须从合作伙伴处购买受支持的设备 转销商。您的控制台可以通过调用 customers.devices.list 来跟踪 IT 管理员的哪些设备可以进行零触摸注册。

下文简要介绍了注册的运作方式:

  1. 设备会在首次启动(或恢复出厂设置后)与 Google 服务器进行签入,以进行零触摸注册。
  2. 如果 IT 管理员已为设备应用了配置,则零触摸 注册时,系统会运行完全受管设备的 Android 设置向导, 显示配置中元数据的屏幕
  3. 零触摸注册会从 Google Play 下载并安装您的 DPC。
  4. 您的设备政策控制器 (DPC) 接收 ACTION_PROVISION_MANAGED_DEVICE intent 和 配置设备。

如果未连接到互联网,系统会检查 可用。如需详细了解如何通过零触摸注册配置设备, 请参阅下面的预配

默认配置

零触摸注册在 IT 管理员设置默认配置时最有帮助 (适用于客户组织购买的所有新设备)。建议您在控制台中设置默认配置(如果尚未设置)。您可以查看 customers.configurations.isDefault 的值设置为 了解组织是否设置了默认配置。

以下示例展示了如何将现有配置 默认值:

Java

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration();
configuration.setIsDefault(true);
configuration.setConfigurationId(targetConfiguration.getConfigurationId());

// Call the API, including the FieldMask to avoid setting other fields to null.
AndroidProvisioningPartner.Customers.Configurations.Patch request = service
      .customers()
      .configurations()
      .patch(targetConfiguration.getName(), configuration);
request.setUpdateMask("isDefault");
Configuration results = request.execute();

.NET

// Send minimal data with the request. Just the 2 required fields.
// targetConfiguration is an existing configuration that we want to make the default.
Configuration configuration = new Configuration
{
    IsDefault = true,
    ConfigurationId = targetConfiguration.ConfigurationId,
};

// Call the API, including the FieldMask to avoid setting other fields to null.
var request = service.Customers.Configurations.Patch(configuration,
                                                     targetConfiguration.Name);
request.UpdateMask = "IsDefault";
Configuration results = request.Execute();

Python

# Send minimal data with the request. Just the 2 required fields.
# target_configuration is an existing configuration we'll make the default.
configuration = {
    'isDefault': True,
    'configurationId': target_configuration['configurationId']}

# Call the API, including the FieldMask to avoid setting other fields to null.
response = service.customers().configurations().patch(
    name=target_configuration['name'],
    body=configuration, updateMask='isDefault').execute()

引用您的政策控制器 (DPC)

我们建议使用 API 资源名称 customers.dpcs.name 以识别您的 DPC 并在配置中使用。资源名称包含 且唯一且不变的 DPC 标识符。致电 customers.dpcs.list,可获取所有受支持设备的列表 设备政策控制器 (DPC)。由于资源名称还包含客户 ID,因此请过滤列表 使用最后一个路径组件查找匹配的 Dpc 实例。示例 下方显示了如何匹配您的 DPC 并保留供日后在 配置:

Java

// Return a customer Dpc instance for the specified DPC ID.
String myDpcIdentifier = "AH6Gbe4aiS459wlz58L30cqbbXbUa_JR9...xMSWCiYiuHRWeBbu86Yjq";
final int dpcIdIndex = 3;
final String dpcComponentSeparator = "/";
// ...
for (Dpc dpcApp : dpcs) {
    // Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID}, check the
    // fourth component matches the DPC ID.
    String dpcId = dpcApp.getName().split(dpcComponentSeparator)[dpcIdIndex];
    if (dpcId.equals(myDpcIdentifier)) {
        System.out.format("My DPC is: %s\n", dpcApp.getDpcName());
        return dpcApp;
    }
}
// Handle the case when the DPC isn't found...

.NET

// Return a customer Dpc instance for the specified DPC ID.
var myDpcIdentifer = "AH6Gbe4aiS459wlz58L30cqbbXbUa_JR9...fE9WdHcxMSWCiYiuHRWeBbu86Yjq";
const int dpcIdIndex = 3;
const String dpcComponentSeparator = "/";
// ...
foreach (Dpc dpcApp in dpcs)
{
    // Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID}, check the
    // fourth component matches the DPC ID.
    String dpcId = dpcApp.Name.Split(dpcComponentSeparator)[dpcIdIndex];
    if (dpcId.Equals(myDpcIdentifer))
    {
        Console.WriteLine("Matched DPC is: {0}", dpcApp.DpcName);
        return dpcApp;
    }
}
// Handle the case when the DPC isn't found...

Python

# Return a customer Dpc instance for the specified DPC ID.
my_dpc_id = 'AH6Gbe4aiS459wlz58L30cqb...fE9WdHcxMSWCiYiuHRWeBbu86Yjq'
# ...
for dpc_app in dpcs:
  # Because the DPC name is in the format customers/{CUST_ID}/dpcs/{DPC_ID},
  # check the fourth component matches the DPC ID.
  dpc_id = dpc_app['name'].split('/')[3]
  if dpc_id == my_dpc_id:
    return dpc_app

# Handle the case when the DPC isn't found...

如果您需要在控制台的界面中显示设备政策控制器 (DPC) 的名称,请显示 从 customers.dpcs.dpcName 返回的值。

正在预配

请把握这个机会,在设备配置方面提供出色的用户体验。 只需设置用户名和密码即可配置设备。 请注意,转销商可能会直接将设备配送给远程用户。包含 所有其他设置(例如 EMM 服务器或组织部门)中, customers.configuration.dpcExtras

以下 JSON 代码段展示了一部分示例配置:

{
  "android.app.extra.PROVISIONING_LOCALE": "en_GB",
  "android.app.extra.PROVISIONING_TIME_ZONE": "Europe/London",
  "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED": true,
  "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE": {
    "workflow_type": 3,
    "default_password_quality": 327680,
    "default_min_password_length": 6,
    "company_name": "XYZ Corp",
    "organizational_unit": "sales-uk",
    "management_server": "emm.example.com",
    "detail_tos_url": "https://www.example.com/policies/terms/",
    "allowed_user_domains": "[\"example.com\", \"example.org\", \"example.net\"]"
    }
}

零触摸注册会使用 Android intent 来安装和启动 DPC。 系统会将 android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE JSON 属性中的值作为 intent 中的 extra 发送到 DPC。您的 DPC 可从以下位置读取配置设置: PersistableBundle

建议 - 使用以下 intent extra 设置设备政策控制器 (DPC):

不推荐 - 不包含以下内容 您可能在其他注册方法中使用的 extra:

如需了解如何在 DPC 中提取和使用这些设置,请参阅配置 客户设备

开发和测试

如需开发和测试控制台的零触摸注册功能,您需要 以下:

  • 支持的设备
  • 客户零触摸注册账号

在支持零触摸功能的设备上进行开发和测试 注册,例如 Google Pixel。您不必 从转销商合作伙伴处购买开发设备。

与我们联系,获取测试客户账号并访问零触摸注册门户。请通过您的公司电子邮件地址(电子邮件地址为 关联了 Google 账号。请告诉我们一台或两台设备的制造商和 IMEI 号码,我们会将其添加到您的开发者账号中。

请注意,由于零触摸注册会自动下载并安装 DPC,因此您必须先在 Google Play 上架 DPC,然后才能测试配置。您无法使用开发版 DPC 进行测试。

IT 管理员支持服务

如果您需要在控制台界面或文档中为 IT 管理员提供帮助, 如需相关指导,请参阅面向 IT 管理员的零触摸注册。您 也可以将您的控制台用户定向到该帮助中心文章

深入阅读

请阅读以下文档,了解如何在控制台中集成零触摸注册: