ネットワーキングとテレフォニー

このガイドでは、Device Policy Controller(DPC)アプリに実装できるネットワーキング機能とテレフォニー管理機能について説明します。このドキュメントにはコードサンプルが含まれています。また、Android のエンタープライズ機能のサンプルコードのソースとして、Test DPC アプリを使用することもできます。

DPC アプリは、個人のデバイスではプロファイル所有者モードで、完全管理対象デバイスではデバイス所有者モードで実行できます。次の表は、DPC がプロファイル所有者モードまたはデバイス所有者モードで実行されている場合に利用できる機能を示しています。

機能 プロファイル所有者 デバイス所有者
プロファイル間で仕事用の連絡先にアクセスする
仕事用トラフィック用に安全なネットワーク接続を確保する
複数のリージョンに 1 つのワイヤレス ネットワーク ID を設定する
仕事用プロファイル用に個別の電話アプリを指定する

プロファイル間で仕事用の連絡先にアクセスする

EMM を使用すると、ユーザーの個人用プロファイルに仕事用の連絡先へのアクセスを許可できます。これにより、ローカル検索とリモート ディレクトリ検索を使用して、ユーザーの個人用と仕事用の連絡先にアクセスできます。個人用デバイスでは、個人用プロファイルの 1 つの電話アプリで、仕事用の通話に加えて個人用の通話の発着信ができます。さらに、仕事用の連絡先はシステム UI に緊密に統合されています。仕事用プロファイルが暗号化されている場合は、個人用プロファイルでそのデータを使用できません。

システム UI との統合

システム UI では、着信した仕事用通話がブリーフケース アイコンで表示されます。callLog には、仕事用通話の着信と発信を示すアイコンも表示されます。個人用電話アプリと連絡先アプリは、リモート ディレクトリ ルックアップを使用して仕事用の連絡先の発信者 ID 情報を表示できるため、連絡先がローカル デバイスですでに同期されている必要はありません。メッセージ アプリでは、ローカルの発信者番号の確認と検索ができます。

Android 互換性定義ドキュメント(CDD)には、デフォルトの電話アプリに仕事用の連絡先を表示するための要件と、連絡先アプリとメッセージ アプリに仕事用プロファイルのバッジを表示する要件が記載されています。

仕事用の連絡先はアクセス可能で、検索可能です

ユーザーは個人用プロファイルから仕事用の連絡先にアクセスして通話できます。このプロファイルは電話アプリの検索画面に表示されます。ユーザーはオートコンプリートを使用して仕事用の連絡先を検索できます。これはデバイスにローカルに同期され、リモート ディレクトリ ルックアップでリストされます。

プライマリ プロファイルで仕事用の連絡先を管理する

仕事用の連絡先を検索する権限は DPC によって管理されます。プロファイル所有者モードで実行される DPC は、個人用プロファイル内の仕事用連絡先の表示を管理します。詳細については、Device Policy Controller を作成するをご覧ください。

個人用プロファイルによる仕事用の連絡先の検索は、デフォルトで有効になっています。

仕事用トラフィック用に安全なネットワーク接続を確保する

Device Policy Controller は、デバイス所有者モードまたはプロファイル所有者モードのいずれかで実行されているときに、常時接続の Virtual Private Network(VPN)接続を使用して、バイパスできない特定の VPN アプリを経由してトラフィックを通過させることができます。DPC は、常時接続 VPN 接続を使用することで、仕事用プロファイルまたは管理対象デバイスからのネットワーク トラフィックがユーザーの介入なしに VPN サービスを通過するようにします。このプロセスにより、仕事用プロファイル内の継続的なトラフィックに対して安全なネットワーク接続が作成されます。

常時接続 VPN 接続について

システム フレームワークの一部として、VPN ルーティングは自動的に管理されるため、ユーザーは VPN サービスをバイパスできません。ロックダウン モード中に VPN サービスが切断された場合、トラフィックがオープンなインターネットに漏洩することはありません。VpnService を実装するアプリの場合、常時接続 VPN は、信頼できるサーバーを介して安全な VPN 接続を管理し、接続を維持するためのフレームワークを提供します。VPN サービスは、接続が Wi-Fi 経由かモバイル接続経由かにかかわらず、アプリのアップデートが行われるたびに接続を自動的に再開します。デバイスが再起動すると、フレームワークは VPN 接続を再開します。

VPN サービスへの接続はユーザーが透過的です。会社所有デバイスの場合、常時接続モードの VPN では、ユーザーが同意ダイアログを確認する必要はありません。ユーザーの VPN ネットワーク設定で、常時接続を手動で有効にできます。

DISALLOW_CONFIG_VPNtrue の場合、ユーザーは VPN を構成できません。DISALLOW_DEBUGGING_FEATURES を有効にして、ユーザーが adb debug コマンドを使用して常時接続 VPN をオーバーライドできないように制限します。ユーザーが VPN をアンインストールできないようにするには、DevicePolicyManager.setUninstallBlocked を呼び出します。

VPN サービスを設定する

Android 向けのエンタープライズ ソリューションを使用する組織が VPN を設定します。

  1. VpnService を実装する VPN アプリをインストールします。アクション VpnService.SERVICE_INTERFACE に一致するインテント フィルタを使用して、アクティブな VPN サービスを見つけることができます。
  2. 権限 BIND_VPN_SERVICE で保護されているアプリのマニフェストで、VpnService を宣言します。
  3. システムによって起動されるように VpnService を構成します。システム起動をリッスンして自身のライフサイクルを制御することで、VPN アプリが起動するように設定しないでください。
  4. VPN アプリに管理対象構成を設定します(下記の例を参照)。

常時接続 VPN 接続を有効にする

DPC は、DevicePolicyManager.setAlwaysOnVpnPackage() を呼び出すことで、特定のアプリを介した常時接続 VPN 接続を構成できます。

この接続は自動的に許可され、再起動後も保持されます。lockdownEnabled が false の場合、スマートフォンが再起動して VPN が接続されるまでネットワーク トラフィックが保護されない可能性があります。これは、VPN で障害が発生してもネットワーク接続を停止したくない場合や、VPN が必須でない場合に有用です。

常時接続 VPN 接続を確認する

DPC は、DevicePolicyManager.getAlwaysOnVpnPackage(). を使用して、現在のユーザーの常時接続 VPN 接続を管理しているパッケージの名前を読み取ることができます。

そのようなパッケージがない場合、または VPN がシステム設定アプリ内で作成された場合は、null が返されます。

TestDPC アプリでは、AlwaysOnVpnFragment.java はこれらの API を使用して常時接続 VPN 接続の設定を有効にします。

下の例からは以下の情報を読み取ることができます。

  • VPN サービスのマネージド構成は、DevicePolicyManagersetApplicationRestrictions() メソッドを使用して設定します。
  • 管理対象構成では任意の Key-Value ペアを使用します。このサンプルアプリでは、それらのペアを他の場所で使用して、VPN のネットワーク設定を構成しています(管理対象構成を確認するをご覧ください)。
  • この例では、Android パッケージ インストーラを拒否リストに追加しているため、VPN 経由のシステム パッケージは更新されません。仕事用プロファイルまたはデバイス内にあるユーザーのネットワーク トラフィックはすべて、パッケージ インストーラを除き、この VPN アプリを通過します。アップデートでは、オープンなインターネットが使用されます。
  • 次に DevicePolicyManager は、setAlwaysOnVpnPackage() を使用して VPN パッケージの常時接続 VPN 接続を有効にし、ロックダウン モードを有効にします。

Kotlin

// Set VPN's managed configurations
val config = Bundle().apply {
  putString(Extras.VpnApp.ADDRESS, "192.0.2.0")
  putString(Extras.VpnApp.IDENTITY, "vpn.account1")
  putString(Extras.VpnApp.CERTIFICATE, "keystore://auth_certificate")
  putStringArray(Extras.VpnApp.DENYLIST,
        arrayOf("com.android.packageinstaller"))
}

val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager

val admin = myDeviceAdminReceiver.getComponentName(this)

// Name of package to update managed configurations
val vpnPackageName = "com.example.vpnservice"

// Associate managed configurations with DeviceAdminReceiver
dpm.setApplicationRestrictions(admin, vpnPackageName, config)

// Enable always-on VPN connection through VPN package
try {
  val lockdownEnabled = true
  dpm.setAlwaysOnVpnPackage(admin, vpnPackageName, lockdownEnabled)
} catch (ex: Exception) {
  throw PolicyException()
}

Java

// Set VPN's managed configurations
final Bundle config = new Bundle();
config.putString(Extras.VpnApp.ADDRESS, "192.0.2.0");
config.putString(Extras.VpnApp.IDENTITY, "vpn.account1");
config.putString(Extras.VpnApp.CERTIFICATE, "keystore://auth_certificate");
config.putStringArray(Extras.VpnApp.DENYLIST,
                      new String[]{"com.android.packageinstaller"});

DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);

ComponentName admin = myDeviceAdminReceiver.getComponentName(this);

// Name of package to update managed configurations
final String vpnPackageName = "com.example.vpnservice";

// Associate managed configurations with DeviceAdminReceiver
dpm.setApplicationRestrictions(admin, vpnPackageName, config);

// Enable always-on VPN connection through VPN package
try {
  boolean lockdownEnabled = true;
  dpm.setAlwaysOnVpnPackage(admin, vpnPackageName, lockdownEnabled));
} catch (Exception ex) {
  throw new PolicyException(...);
}

複数のリージョンに 1 つのワイヤレス ネットワーク ID を設定する

デバイス所有者モードまたはプロファイル所有者モードで動作する Device Policy Controller(DPC)は、複数の認証局(CA)証明書を 1 つのワイヤレス ネットワーク構成に関連付けることができます。この設定により、デバイスは、ネットワーク名またはサービスセット識別子(SSID)が同じでも、異なる CA 証明書で構成されているワイヤレス アクセス ポイントに接続できます。これは、組織のワイヤレス ネットワークが複数の地理的リージョンに配置され、各リージョンに異なる認証局が必要な場合に役立ちます。たとえば、法的署名には地域の認証局を必要とする地方自治体が必要な場合があります。

注: Android では、API 18(Jelly Bean)以降、setCaCertificate がサポートされていますが、IT 管理者は、デバイスのリージョンに関係なく、各アクセス ポイントでデバイスの認証がシームレスに行われるように、CA ごとにネットワークを個別にプロビジョニングする必要があります。

サーバーを識別するための CA 証明書を指定する

同じ SSID を使用してサーバーを識別する X.509 証明書のリストを指定するには、WifiEnterpriseConfig.setCaCertificates() を使用して、関連するすべての CA をワイヤレス構成に含めます。

サーバーの CA が指定された証明書のいずれかと一致する場合、その証明書は有効です。 デフォルト名は証明書に自動的に割り当てられ、構成内で使用されます。WifiManager は、ネットワークが有効になると証明書をインストールして、構成を自動的に保存します。構成が削除されると証明書を削除します。

ワイヤレス構成に関連付けられているすべての CA 証明書を取得するには、WifiEnterpriseConfig.getCaCertificates() を使用して X509Certificate オブジェクトのリストを返します。

複数の CA 証明書を使用してワイヤレス設定を追加する

  1. サーバーの ID を確認します。
    1. X.509 CA 証明書を読み込みます。
    2. クライアントの秘密鍵と証明書を読み込みます。証明書ファイルの読み取り方法の例については、HTTPS と SSL によるセキュリティをご覧ください。
  2. 新しい WifiConfiguration を作成し、その SSID と鍵管理を設定します。
  3. この WifiConfigurationWifiEnterpriseConfig インスタンスを設定します。
    1. setCaCertificates() を使用して、X509Certificate オブジェクトのリストでサーバーを特定します。
    2. クライアントの認証情報、ID、パスワードを設定します。
    3. 接続を確立する一環として、拡張認証プロトコル(EAP)とフェーズ 2 方式を設定します。
  4. WifiManager を使用してネットワークを追加します。
  5. ネットワークを有効にします。WifiManager は、セットアップ中に設定を自動的に保存します。

この例では、ステップをまとめます。

Kotlin

// Verify the server's identity
val caCert0 = getCaCert("cert0.crt")
val caCert1 = getCaCert("cert1.crt")
val clientKey = getClientKey()
val clientCert = getClientCert()

// Create Wi-Fi configuration
val wifiConfig = WifiConfiguration().apply {
  SSID = "mynetwork"
  allowedKeyManagement.set(KeyMgmt.WPA_EAP)
  allowedKeyManagement.set(KeyMgmt.IEEE8021X)

  // Set up Wi-Fi enterprise configuration
  enterpriseConfig.setCaCertificates(arrayOf<X509Certificate>(caCert0, caCert1))
  enterpriseConfig.setClientKeyEntry(clientKey, clientCert)
  enterpriseConfig.setIdentity("myusername")
  enterpriseConfig.setEapMethod(Eap.TLS)
  enterpriseConfig.setPhase2Method(Phase2.NONE)
}


// Add network
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val netId = wifiManager.addNetwork(wifiConfig)

// Enable network
if (netId < 0) {
  // Error creating new network
} else {
  wifiManager.enableNetwork(netId, true)
}

Java

// Verify the server's identity
X509Certificate caCert0 = getCaCert("cert0.crt");
X509Certificate caCert1 = getCaCert("cert1.crt");
PrivateKey clientKey = getClientKey();
X509Certificate clientCert = getClientCert();

// Create Wi-Fi configuration
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.SSID = "mynetwork";
wifiConfig.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
wifiConfig.allowedKeyManagement.set(KeyMgmt.IEEE8021X);

// Set up Wi-Fi enterprise configuration
wifiConfig.enterpriseConfig.setCaCertificates(new X509Certificate[] {caCert0, caCert1});
wifiConfig.enterpriseConfig.setClientKeyEntry(clientKey, clientCert);
wifiConfig.enterpriseConfig.setIdentity("myusername");
wifiConfig.enterpriseConfig.setEapMethod(Eap.TLS);
wifiConfig.enterpriseConfig.setPhase2Method(Phase2.NONE);

// Add network
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
int netId = wifiManager.addNetwork(wifiConfig);

// Enable network
if (netId < 0) {
  // Error creating new network
} else {
  wifiManager.enableNetwork(netId, true);
}

仕事用プロファイル用に個別の電話アプリを指定する

仕事用プロファイルで使用する個別の電話アプリを許可リストに登録できます。電話アプリ自体か、発信元のバックエンドの ConnectionService API を実装する Voice over IP(VoIP)アプリです。これにより、仕事用プロファイルの VoIP アプリに同じ統合システム UI ダイヤル エクスペリエンスが提供され、仕事用電話アプリがコア機能になります。仕事用通話アカウントへの着信は、個人用通話アカウントの着信と区別されます。

ユーザーは、スマートフォン アカウントで許可リストに登録された仕事用電話アプリで発信と着信を選択できます。その電話アプリからの通話や仕事用の電話アカウントに着信した通話はすべて、仕事用プロファイルの CallLog プロバイダに記録されます。仕事用電話アプリは、仕事用の連絡先にのみアクセスできる仕事用の通話履歴を保持します。回線切り替えの着信はプライマリ 電話アプリによって処理され、個人用通話履歴に保存されます。仕事用プロファイルが削除されると、その仕事用プロファイルに関連付けられている通話履歴だけでなく、すべての仕事用プロファイル データも削除されます。

サードパーティ アプリで ConnectionService を実装する必要がある

電話をかける必要があり、その通話を組み込みの電話アプリに統合する必要があるサードパーティ VoIP アプリでは、ConnectionService API を実装できます。仕事用通話に使用するすべての VoIP サービスに必要です。こうしたアプリでは、通話を従来のモバイル通話と同様に扱うというメリットがあります。たとえば、アプリは組み込みのシステム電話アプリや通話履歴に表示されます。ConnectionService を実装しているアプリが仕事用プロファイルにインストールされている場合、そのアプリにアクセスできるのは、その仕事用プロファイルにもインストールされている電話アプリのみです。

ConnectionService を実装したら、それをアプリのマニフェスト ファイルに追加し、PhoneAccountTelecomManager に登録する必要があります。スマートフォン アカウントは、通話の発着信を行う異なる方法を表し、ConnectionService ごとに複数の PhoneAccounts が存在する場合があります。スマートフォン アカウントの登録後、ユーザーは電話アプリでアカウントを有効にできます。

システム UI の統合と通知

ConnectionService API をバックエンドとして使用するサードパーティ アプリでは、システム UI によって一貫した統合型ダイヤル エクスペリエンスを提供できます。仕事用プロファイルでアプリを使用している場合は、着信とステータスバーにブリーフケースのアイコンが表示されます。仕事用プロファイルにインストールされている ConnectionService を実装しているアプリは、システム電話アプリを使用するか、個別の仕事用電話アプリを構築できます。これらは単一のアプリでも個別のアプリでもかまいません。

電話アプリは android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL フラグを確認することで、仕事用通話を発信または受信しているかどうかを判断します。通話が仕事用通話の場合、電話アプリは仕事用バッジ(ブリーフケースのアイコン)を追加して、ユーザーにその旨を知らせます。

Kotlin

// Call placed through a work phone account. getCurrentCall() is defined by the
// dialer.
val call = getCurrentCall()
if (call.hasProperty(android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL)) {
  // Set briefcase icon
}

Java

// Call placed through a work phone account. getCurrentCall() is defined by the
// dialer.
Call call = getCurrentCall();
if (call.hasProperty(android.telecom.Call.Details.PROPERTY_ENTERPRISE_CALL)) {
  // Set briefcase icon
}