EMM 統合ガイド

このガイドは、企業向けモバイル管理(EMM)プロバイダがゼロタッチ登録をコンソールに統合する際に活用できます。引き続き、登録の詳細と、DPC(Device Policy Controller)によるデバイスのプロビジョニングに役立つおすすめの方法のアドバイスをご確認ください。DPC がある場合は、デバイスをプロビジョニングする際のベスト プラクティスについて確認し、開発とテストに役立つアドバイスを得ることができます。

IT 管理者向けの機能

Customer API を使用すると、IT 管理者がコンソールから直接ゼロタッチ登録を設定できるようになります。IT 管理者がコンソールで行えるタスクには、次のようなものがあります。

  • モバイルポリシーに基づいて、ゼロタッチ登録の設定を作成、編集、削除します。
  • 組織が今後購入するデバイスを DPC がプロビジョニングするように、デフォルトの構成を設定します。
  • デバイスに個別の設定を適用する、またはゼロタッチ登録からデバイスを削除する。

ゼロタッチ登録の詳細については、概要をご覧ください。

前提条件

EMM コンソールにゼロタッチ登録を追加する前に、ソリューションが以下をサポートしていることを確認してください。

  • EMM ソリューションは、会社所有の Android 8.0 以降(Google Pixel 7.1 以降)のデバイスをフルマネージド モードでプロビジョニングする必要があります。会社所有の Android 10 以降のデバイスは、完全管理対象または仕事用プロファイルとしてプロビジョニングできます。
  • ゼロタッチ登録では DPC が自動的にダウンロード、インストールされるため、DPC を Google Play から入手できるようにする必要があります。Google では、IT 管理者が Customer API またはポータルを使用して構成できる互換性のある DPC のリストを保持しています。DPC をリストに追加するには、EMM プロバイダのコミュニティからサービスの変更リクエストを送信してください。
  • お客様が Customer API を呼び出すには、ゼロタッチ登録アカウントが必要です。パートナー販売パートナーは、IT 管理者の組織がデバイスを購入する際に、その組織のアカウントを設定します。
  • ゼロタッチ登録を正しく機能させるには、デバイスが Google モバイル サービス(GMS)に対応している必要があります。また、Google Play 開発者サービスを常に有効にしておく必要があります。

API の呼び出し

コンソールのユーザー(Google アカウントを使用)が、カスタマー API に対する API リクエストを承認します。このフローは、他の EMM API で行う承認とは異なります。アプリでこれを行う方法については、承認をご覧ください。

利用規約を処理する

ユーザーは 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 リクエスト)をサポートしている場合は、リクエストに値 2 を指定した HTTP ヘッダー X-GOOG-API-FORMAT-VERSION を含めます。クライアントが詳細なエラー(.NET など)をサポートしていない場合は、エラー メッセージを照合してください。

今後 Google が利用規約を更新する際は、新しい利用規約に再度同意するよう求めるメッセージがユーザーに表示されます。

IT 管理者は、ゼロタッチ登録ポータルを使用して、組織のユーザーを管理します。カスタマー API を介して提供することはできません。IT 管理者は、ポータルを使用してデバイスと構成を管理することもできます。コンソールまたはドキュメントからポータルにリンクする必要がある場合は、次の URL を使用します。

https://partner.android.com/zerotouch

Google アカウントでログインするように求められることを IT 管理者に知らせることをおすすめします。

デバイスの登録

ゼロタッチ登録はデバイスを登録するメカニズムであり、NFC 登録や QR コード登録に似ています。コンソールは管理対象デバイスをサポートしており、DPC は完全管理対象デバイスモードで実行できなければなりません。

ゼロタッチ登録は、Android 8.0 以降を搭載するサポート対象デバイスで利用できます。IT 管理者は、サポート対象デバイスをパートナーの販売パートナーから購入する必要があります。コンソールで customers.devices.list を呼び出すと、IT 管理者のどのデバイスがゼロタッチ登録できるかを追跡できます。

登録の流れは次のとおりです。

  1. デバイスは、初回起動時(または初期状態にリセットした後)に Google サーバーにチェックインして、ゼロタッチ登録を行います。
  2. IT 管理者がデバイスに構成を適用している場合は、ゼロタッチ登録により完全管理対象デバイスの Android 設定ウィザードが実行され、構成のメタデータで画面がパーソナライズされます。
  3. ゼロタッチ登録により、Google Play から DPC をダウンロードしてインストールできます。
  4. DPC は ACTION_PROVISION_MANAGED_DEVICE インテントを受け取り、デバイスをプロビジョニングします。

インターネット接続がない場合は、インターネット接続が利用可能になったときにチェックが行われます。ゼロタッチ登録によるデバイスのプロビジョニングの詳細については、以下のプロビジョニングをご覧ください。

デフォルト構成

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 インテントを使用して DPC のインストールと起動が行われます。android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE JSON プロパティの値は、インテントのエクストラとして DPC に送信されます。DPC は、同じ鍵を使用して PersistableBundle からプロビジョニング設定を読み取ることができます。

推奨 - 次のインテント エクストラを使用して DPC を設定します。

非推奨 - 他の登録方法で使用される可能性のある、次のような追加要素は含めないでください。

これらの設定を抽出して DPC で使用する方法については、お客様のデバイスのプロビジョニングをご覧ください。

開発とテスト

コンソールのゼロタッチ登録機能を開発してテストするには、次のものが必要です。

  • サポートされているデバイス
  • お客様のゼロタッチ登録アカウント

Google Pixel など、ゼロタッチ登録をサポートするデバイスで開発とテストを行います。販売パートナーから開発デバイスを購入する必要はありません。

Google にお問い合わせのうえ、テスト用の顧客アカウントを取得し、ゼロタッチ登録ポータルにアクセスできるようにしてください。Google アカウントに関連付けられている会社のメールアドレスからメールを送信します。1 ~ 2 台のデバイスのメーカーと IMEI 番号をお知らせいただければ、開発アカウントに追加します。

ゼロタッチ登録では DPC が自動的にダウンロード、インストールされるため、プロビジョニングをテストする前に、Google Play から DPC を利用できる必要があります。DPC の開発版ではテストできません。

IT 管理者向けのサポート

コンソールのインターフェースやドキュメントで IT 管理者を支援する必要がある場合は、IT 管理者向けゼロタッチ登録をご覧ください。また、コンソールのユーザーにヘルプセンターの記事を紹介することもできます。

関連情報

コンソールにゼロタッチ登録を統合するには、次のドキュメントをご覧ください。