运作方式

借助 customer API,您可以通过编程控制设备和配置 Android 零触摸注册。本文档向企业移动管理 (EMM) 提供商和企业 IT 开发者介绍了此 API。阅读本文档后,您应该已经了解该 API 中使用的核心资源及其交互方式。如果您刚开始接触零触摸注册,请阅读简短的 android.com 简介

概览

客户 API 可帮助购买 Android 零触摸注册设备的组织。您的应用或工具可以帮助 IT 管理员执行以下操作:

  • 创建、修改和删除预配配置。
  • 对设备应用或移除配置。
  • 为以后添加到零触摸注册的所有设备选择默认配置。

通过该 API,IT 管理员还可以为设备取消注册零触摸注册。如需管理其组织的用户或接受服务条款,IT 管理员可使用零触摸注册门户

此 API 的典型用户是:

  • EMM 提供商在其控制台中添加了对零触摸注册的支持。
  • 企业 IT 开发者可以打造用于自动执行零触摸注册任务的工具。

核心资源

配置和设备是您在 API 中使用的核心资源。组织还可以使用零触摸注册门户创建配置和设备。

设备和客户资源关系

配置
IT 管理员使用配置来为设备设置配置选项。 配置包括 EMM 移动设备政策和联系信息,显示以帮助用户。配置是 API 的核心,因此可在许多方法中使用。如需了解详情,请参阅下面的配置
设备
组织从其转销商处购买的支持零触摸注册的 Android 设备。应用配置以将设备包含在零触摸注册中。设备具有硬件 ID 和附加元数据。如需了解详情,请参阅下面的设备
设备政策控制器 (DPC)
对 EMM 的 DPC(设备政策控制器)的只读引用。将 DPC 添加到配置中,以便为设备选择 EMM 解决方案。API 列出的所有 DPC 都支持零触摸注册,并且可以在 Google Play 中找到。如需了解详情,请参阅 Dpc

如需列出您的应用可以使用的所有 API 方法和资源,请参阅 API 参考文档

配置

Configuration API 资源包含以下各项:

  • 设备上安装的 EMM 的 DPC。
  • 已在设备上强制执行 EMM 政策。
  • 设备上显示的联系信息,用于在设置过程中帮助用户。

通过使用 API,您的应用可以为 IT 管理员管理配置。调用该 API 以提取、创建、更新和删除配置。以下示例展示了如何创建新配置:

Java

// Add metadata to help the device user during provisioning.
Configuration configuration = new Configuration();
configuration.setConfigurationName("Sales team");
configuration.setCompanyName("XYZ Corp.");
configuration.setContactEmail("it-support@example.com");
configuration.setContactPhone("+1 (800) 555-0112");
configuration.setCustomMessage("We're setting up your phone. Call or email for help.");

// Set the DPC that zero-touch enrollment downloads and installs from Google Play.
configuration.setDpcResourcePath(dpc.getName());

// Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration.setDpcExtras("{"
      + "\"android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED\":true,"
      + "\"android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE\":{"
      + "\"default_min_password_length\":6,"
      + "\"company_name\":\"XYZ Corp\","
      + "\"management_server\":\"emm.example.com\","
      + "\"terms_url\":\"https://www.example.com/policies/terms/\","
      + "\"allowed_user_domains\":\"[\\\"example.com\\\", \\\"example.org\\\"]\""
      + "}"
      + "}");

// Create the new configuration on the server.
AndroidProvisioningPartner.Customers.Configurations.Create request =
      service.customers().configurations().create(customerAccount, configuration);
Configuration response = request.execute();

.NET

// Add metadata to help the device user during provisioning.
Configuration configuration = new Configuration
{
    ConfigurationName = "Sales team",
    CompanyName = "XYZ Corp.",
    ContactEmail = "it-support@example.com",
    ContactPhone = "+1 (800) 555-0112",
    CustomMessage = "We're setting up your phone. Call or email for help."
};

// Set the DPC that zero-touch enrollment downloads and installs from Google Play.
configuration.DpcResourcePath = dpc.Name;

// Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration.DpcExtras = @"{
    ""android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"":true,
    ""android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE"":{
    ""default_min_password_length"":6,
    ""company_name"":""XYZ Corp"",
    ""management_server"":""emm.example.com"",
    ""terms_url"":""https://www.example.com/policies/terms/"",
    ""allowed_user_domains"":""[\""example.com\"", \""example.org\""]""
  }
}";

// Create the new configuration on the server.
var request = service.Customers.Configurations.Create(configuration, customerAccount);
var response = request.Execute();

Python

# Add metadata to help the device user during provisioning.
configuration = {
    'configurationName': 'Sales team',
    'companyName': 'XYZ Corp.',
    'contactEmail': 'it-support@example.com',
    'contactPhone': '+1 (800) 555-0112',
    'customMessage': 'We\'re setting up your phone. Call or email for help.'}

# Set the DPC that zero-touch enrollment installs from Google Play.
configuration['dpcResourcePath'] = dpc['name']

# Set the JSON-formatted EMM provisioning extras that are passed to the DPC.
configuration['dpcExtras'] = '''{
    "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED":true,
    "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE":{
      "default_min_password_length":6,
      "company_name":"XYZ Corp",
      "management_server":"emm.example.com",
      "terms_url":"https://www.example.com/policies/terms/",
      "allowed_user_domains":"[\\"example.com\\", \\"example.org\\"]"}
}'''

# Create the new configuration on the server.
response = service.customers().configurations().create(
    parent=customer_account, body=configuration).execute()

使用 Patch API 更新配置时,请务必添加字段掩码,或者为您不希望为 null 的每个字段添加相应的值。有关说明如何高效更新配置的示例,请参阅下文的默认配置

删除配置

如果某个配置仍应用于设备,则无法删除该配置。如果您尝试删除使用中的配置,该 API 方法会返回 HTTP 400 Bad Request 状态代码以及说明有多少设备使用了该配置的消息。请先调用 customers.devices.removeConfiguration 从设备中移除配置,然后重试。

默认配置

当组织设置默认配置并将其应用于其购买的任何新设备时,零触摸注册效果最佳。如果未设置默认配置,请考虑提示 IT 管理员设置默认配置。 以下示例展示了如何通过将 isDefault 设置为 true 来将现有配置设为默认配置:

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()

只能有一个默认配置。如果创建新的默认配置,则会将先前配置的 isDefault 字段设置为 false。您可能需要刷新任何缓存的 Configuration 实例才能看到 isDefault 字段中的正确值。

向导设备用户

零触摸配置会在设备设置向导中显示自定义用户指南,以帮助用户。您需要在配置中包含联系人电话号码和电子邮件地址,以及管理设备的组织的名称。我们还建议在 customMessage 字段中添加一两个句子,以提供有关用户设备所发生情况的更多详细信息。

由于用户无法通过正在设置的设备拨打电话或发送电子邮件,因此请设置电话号码和电子邮件地址的格式,以便于浏览信息。

设备

客户在购买设备以进行零触摸注册时,转销商会创建设备 - IT 管理员无法创建设备。如需使用设备,请对 Device API 资源调用方法。如果您需要搜索设备,请在应用中列出所有设备并在本地过滤每个批次。有关示例,请参阅下面的分页结果

配置设备

将配置应用于设备,即可为设备注册零触摸注册。如需应用配置,请调用 customers.devices.applyConfiguration。应用配置后,设备会在首次启动或下次恢复出厂设置时自动进行预配。以下示例展示了如何将配置应用于一组设备:

Java

List<Device> devices = getDevicesToConfigure(service);
Configuration configurationToApply = getConfigurationToApply(service);

// Loop through the collection and apply the configuration to each device. This might
// take some time if the collection contains many devices.
for (Device device : devices) {
    System.out.println(device.getDeviceIdentifier().getImei());

    // Wrap the device ID in a DeviceReference.
    DeviceReference deviceRef = new DeviceReference();
    deviceRef.setDeviceId(device.getDeviceId());

    // Build and send the request to the API.
    CustomerApplyConfigurationRequest body = new CustomerApplyConfigurationRequest();
    body.setConfiguration(configurationToApply.getName());
    body.setDevice(deviceRef);

    AndroidProvisioningPartner.Customers.Devices.ApplyConfiguration request = service
          .customers()
          .devices()
          .applyConfiguration(customerAccount, body);
    request.execute();
}

.NET

IList<Device> devices = GetDevicesToConfigure(service);
Configuration configurationToApply = GetConfigurationToApply(service);

// Loop through the collection and apply the configuration to each device. This might
// take some time if the collection contains many devices.
foreach (Device device in devices)
{
    Console.WriteLine(device.DeviceIdentifier.Imei);

    // Wrap the device ID in a DeviceReference.
    var deviceRef = new DeviceReference
    {
        DeviceId = device.DeviceId
    };

    // Build and send the request to the API.
    CustomerApplyConfigurationRequest body = new CustomerApplyConfigurationRequest
    {
        Configuration = configurationToApply.Name,
        Device = deviceRef
    };
    var request = service.Customers.Devices.ApplyConfiguration(body,
                                                               customerAccount);
    request.Execute();
}

Python

devices = get_devices_to_configure(service)
configuration = get_configuration_to_apply(service)

# Loop through the collection and apply the configuration to each device.
# This might take some time if the collection contains many devices.
for device in devices:
  print(device['deviceIdentifier']['imei'])

  # Wrap the device ID in a DeviceReference.
  device_ref = {'deviceId': device['deviceId']}

  # Build and send the request to the API.
  body = {'configuration': configuration['name'], 'device': device_ref}
  service.customers().devices().applyConfiguration(
      parent=customer_account, body=body).execute()

如需从设备中移除配置,请调用 customers.devices.removeConfiguration。此更改将在设备恢复出厂设置后生效。

取消声明设备所有权

IT 管理员可以取消对设备的所有权,从而将其从零触摸注册中移除。IT 管理员可能取消对要迁移到其他帐号、出售或退还给转销商的设备的所有权。调用 customers.devices.unclaim 方法,向组织取消声明对设备的所有权。

以下示例展示了如何通过 IMEI 识别码和制造商名称取消声明设备:

Java

// Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
// Then wrap the DeviceIdentifier in a DeviceReference.
DeviceIdentifier identifier = new DeviceIdentifier();
identifier.setImei("123456789012347");
identifier.setManufacturer("Google");
DeviceReference reference = new DeviceReference();
reference.setDeviceIdentifier(identifier);

// Create the body of the request.
CustomerUnclaimDeviceRequest body = new CustomerUnclaimDeviceRequest();
body.setDevice(reference);

// Call the API method to unclaim the device from the organization.
service.customers().devices().unclaim(customerAccount, body).execute();

.NET

// Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
// Then wrap the DeviceIdentifier in a DeviceReference.
DeviceIdentifier identifier = new DeviceIdentifier
{
    Imei = "123456789012347",
    Manufacturer = "Google"
};
DeviceReference reference = new DeviceReference();
reference.DeviceIdentifier = identifier;

// Create the body of the request.
CustomerUnclaimDeviceRequest body = new CustomerUnclaimDeviceRequest();
body.Device = reference;

// Call the API method to unclaim the device from the organization.
service.Customers.Devices.Unclaim(body, customerAccount).Execute();

Python

# Wrap the hardware ID and manufacturer values in a DeviceIdentifier.
# Then wrap the DeviceIdentifier in a DeviceReference.
identifier = {'imei': '123456789012347', 'manufacturer': 'Google'}
reference = {'deviceIdentifier': identifier}

# Create the body of the request.
body = {'device': reference}

# Call the API method to unclaim the device from the organization.
service.customers().devices().unclaim(
    parent=customer_account, body=body).execute()

设备元数据

IT 管理员可以查看转销商为设备附加的元数据。在您的应用中显示此设备元数据,以帮助 IT 管理员识别设备。

如需详细了解您可能会看到的元数据,请参阅面向转销商的设备元数据指南。

分页结果

customers.devices.list API 方法可能会返回非常大的设备列表。为了减小响应大小,此方法和其他 API 方法(如 customers.list)支持分页结果。通过分页结果,您的应用可以迭代方式请求和处理大型列表,一次一页。

调用 API 方法后,检查响应是否包含 nextPageToken 的值。如果 nextPageToken 不是 null,您的应用可以通过再次调用该方法,使用它来提取设备的另一页。您需要在 pageSize 参数中设置设备数量上限。如果 nextPageTokennull,则表示您的应用请求了最后一个页面。

以下示例方法展示了您的应用如何才能逐页输出设备列表:

Java

private void printDevices(AndroidProvisioningPartner service, String customerAccount,
      String pageToken) throws IOException {

    // Call the API to get a page of Devices. Send a page token from the method argument.
    // If the page token is null, the API returns the first page.
    AndroidProvisioningPartner.Customers.Devices.List request =
          service.customers().devices().list(customerAccount);
    request.setPageSize(50L);
    request.setPageToken(pageToken);
    CustomerListDevicesResponse response = request.execute();

    // Print the devices included in this page of results.
    for (Device device : response.getDevices()) {
        System.out.format("Device: %s\n", device.getName());
    }
    System.out.println("---");

    // Check to see if another page of devices is available. If yes, fetch & print the devices.
    if (response.getNextPageToken() != null) {
        this.printDevices(service, customerAccount, response.getNextPageToken());
    }
}

.NET

private void PrintDevices(AndroidProvisioningPartnerService service, String customerAccount,
                          String pageToken)
{
    // Call the API to get a page of Devices. Send a page token from the method argument.
    // If the page token is null, the API returns the first page.
    var request = service.Customers.Devices.List(customerAccount);
    request.PageSize = 50;
    request.PageToken = pageToken;
    var response = request.Execute();

    // Print the devices included in this page of results.
    foreach (Device device in response.Devices)
    {
        Console.WriteLine("Device: {0}", device.Name);
    }
    Console.WriteLine("---");

    // Check to see if another page of devices is available. If yes, fetch and print the devices.
    if (response.NextPageToken != null)
    {
        this.PrintDevices(service, customerAccount, response.NextPageToken);
    }
}

Python

def print_devices(service, customer_account, page_token):
  """Demonstrates how to loop through paginated lists of devices."""

  # Call the API to get a page of Devices. Send a page token from the method
  # argument. If the page token is None, the API returns the first page.
  response = service.customers().devices().list(
      parent=customer_account, pageSize=50, pageToken=page_token).execute()

  # Print the devices included in this page of results.
  for device in response['devices']:
    print('Device: {0}'.format(device['name']))
  print('---')

  # Check to see if another page of devices is available. If yes,
  # fetch and print the devices.
  if 'nextPageToken' in response:
    print_devices(service, customer_account, response['nextPageToken'])

开始使用

接下来,请阅读授权以了解如何对 API 调用进行授权。如果您想探索这些 API,请查看 Java.NETPython 快速入门指南。您可以使用 Colab 查看 API 调用示例,并尝试自行调用 API。