网络和电话

本指南中的功能介绍了网络和电话管理 您可以在设备规范中实现的功能 控制器 (DPC) 应用提供。本文档包含代码 您也可以使用 Test DPC 应用 Android 企业功能的示例代码源代码。

DPC 应用可以在个人设备或设备所有者中以资料所有者模式运行 模式。下表显示了 当设备政策控制器 (DPC) 在资料所有者模式或设备所有者模式下运行时可用 mode

功能 个人资料所有者 设备所有者
访问工作通讯录 跨资料
确保 用于工作流量的安全网络连接
设置 跨区域使用单个无线网络 ID
指定一个 工作资料的单独拨号器

跨资料访问工作联系人

EMM 可允许用户的个人资料访问其工作联系人, 用户的个人和工作联系人可通过本地搜索和 远程目录查找。在个人设备上,个人账号中的单个拨号器 个人资料可以拨打和接听个人电话和工作电话。此外, 工作联系人已完美集成到系统界面中。如果工作资料 加密数据,那么相应个人资料就无法访问相应数据。

与系统界面集成

系统界面使用公文包图标指示工作来电。通过 callLog 还会显示 表示工作来电和去电的图标。个人拨号器和 “通讯录”应用可以使用遥控器显示工作联系人的来电显示信息 因此不要求已在通讯录中同步该联系人 本地设备即时通讯应用可执行本地来电显示和搜索。

Android 兼容性定义 文档 (CDD) 中包括要求 让工作联系人显示在默认拨号器中,以及 通讯录和即时通讯应用带有标记,表示它们来自工作单位 个人资料。

可访问和搜索工作联系人

用户可以通过自己的个人资料访问工作联系人并呼叫他们, 。用户可以搜索单位地址 联系人(使用自动补全功能),这些联系人会在本地同步到设备并列出 查找远程目录

控制主要资料中的工作联系人

DPC 控制搜索工作联系人的权限。正在配置文件所有者中运行 模式时,DPC 管理个人资料中工作联系人的可见性。 如需了解详情,请参阅构建设备政策 控制器

默认情况下,系统会启用按个人资料搜索工作联系人的功能。

确保工作流量的安全网络连接

在设备所有者模式或资料所有者模式下运行,设备政策 控制器可以使用始终开启的虚拟专用网 (VPN) 连接来 强制应用通过指定的 VPN 应用传输流量, 。借助始终开启的 VPN 连接,DPC 可确保网络 工作资料或受管理设备的流量会经由 VPN 服务传输; 而无需用户干预。此过程可创建安全的网络连接, 在工作资料内持续的流量。

关于始终开启的 VPN 连接

作为系统框架的一部分,VPN 路由是自动管理的,因此 用户无法绕过 VPN 服务。如果 VPN 服务在 锁定模式,因此流量不会泄露到开放的互联网。对于应用 正在实现 VpnService, 始终开启的 VPN 提供了一个框架,可通过 因此您可以放心地养成可信任的服务器。VPN 服务会自动重启 无论连接方式是 WLAN 还是 移动网络。如果设备重新启动,框架会重启 VPN 连接。

与 VPN 服务的连接对用户是透明的。对于 公司自有设备,那么用户无需针对 始终开启模式的 VPN。用户的 VPN 网络设置允许启用 手动连接到始终开启的连接。

如果DISALLOW_CONFIG_VPNtrue,则禁止用户配置 VPN。启用 DISALLOW_DEBUGGING_FEATURES 来限制用户使用 adb 调试命令覆盖始终开启的 VPN。 要防止用户卸载 VPN,请调用 DevicePolicyManager.setUninstallBlocked

设置 VPN 服务

使用 Android 企业解决方案的单位会设置 VPN。

  1. 安装一个实现 VpnService。 您可以使用与 动作 VpnService.SERVICE_INTERFACE
  2. 声明 VpnService 在受权限保护的应用清单中 BIND_VPN_SERVICE
  3. 配置 VpnService 因此由系统启动通过以下方式避免将 VPN 应用设置为自行启动 监听系统启动并控制其自己的生命周期。
  4. 代管式 配置 VPN 应用(请参阅下面的示例)。

启用始终开启的 VPN 连接

DPC 可通过特定应用配置始终开启的 VPN 连接,具体方法是: 呼叫 DevicePolicyManager.setAlwaysOnVpnPackage()

此连接会自动授予,并且会在重新启动后持久保留。如果 lockdownEnabled 为 false,那么从 手机重新启动并连接 VPN。如果您不想停止,此功能非常有用 如果 VPN 出现故障或不需要 VPN,则连接网络。

验证始终开启的 VPN 连接

DPC 可以读取管理始终开启 VPN 的软件包的名称 为当前用户连接 DevicePolicyManager.getAlwaysOnVpnPackage().

如果没有此类软件包,或者 VPN 是在系统“设置”中创建的 应用,则返回 null

示例

TestDPC 应用中,AlwaysOnVpnFragment.java 使用这些 API 为始终开启的 VPN 连接启用设置。

在以下示例中:

  • 配置 VPN 服务由 DevicePolicyManager 使用 setApplicationRestrictions() 方法。
  • 受管配置使用任意键值对,此示例应用 使用它们来配置 VPN 的网络设置(请参阅 检查托管配置)。
  • 本示例将 Android 软件包安装程序添加到拒绝名单 通过 VPN 更新系统软件包。用户在相应范围内的所有网络流量 工作资料或设备会通过此 VPN 应用(软件包除外) 安装程序;其更新会使用开放的互联网
  • 然后,DevicePolicyManager 会为 VPN 软件包 setAlwaysOnVpnPackage(), 以及启用锁定模式

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(...);
}

设置跨区域使用同一个无线网络 ID

在设备所有者模式或资料所有者模式下运行,设备政策 控制器 (DPC) 可关联多个证书授权机构 (CA) 证书 只需一项无线网络配置即可。使用此配置时, 可以连接到具有相同网络名称的无线接入点,或者 服务集标识符 (SSID),但配置了不同的 CA 证书。如果贵组织的无线网络 位于多个地理区域,每个区域需要不同的 证书授权中心。例如,法定签名要求使用当地 需要区域 CA 的证书授权机构。

注意:Android 设备 setCaCertificate 从 API 18 (Jelly Bean)开始,但 IT 管理员必须配置他们的网络 分别与每个 CA 相关联,以确保设备在每个 CA 上都能顺畅地进行身份验证 无论其位于何处

指定 CA 证书以标识服务器

指定 X.509 证书列表,这些证书使用相同名称 SSID,使用 WifiEnterpriseConfig.setCaCertificates() 在无线配置中包含所有相关 CA。

如果服务器的 CA 与其中一个给定证书匹配,则该服务器的证书有效。 默认名称会自动分配给证书,并在 配置。通过 WifiManager 会安装该证书,并在出现以下情况时自动保存配置: 并在配置启用时移除证书 已删除。

要获取与无线配置关联的所有 CA 证书,请使用 WifiEnterpriseConfig.getCaCertificates(),用于返回 X509Certificate 对象。

添加使用多个 CA 证书的无线配置

  1. 验证服务器的身份: <ph type="x-smartling-placeholder">
      </ph>
    1. 加载 X.509 CA 证书。
    2. 加载客户端的私钥和证书。如需查看有关如何读取证书文件的示例,请参阅 HTTPS 和 SSL 的安全性
  2. 创建新的 WifiConfiguration 并设置其 SSID 和密钥管理。
  3. 设置 WifiEnterpriseConfigWifiConfiguration 上的实例。
    1. 使用列表标识服务器 X509Certificate 使用 setCaCertificates()
    2. 设置客户端凭据、身份和密码。
    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。这样可提供相同的集成系统界面拨号 在工作资料中使用 VoIP 应用, 拨号加入核心功能。工作通话账号的来电: 与个人电话账号的来电区分开来。

用户可以选择使用已列入许可名单的工作拨号器拨打和接听电话 。通过该拨号器拨打或上班的所有电话 都记录在工作资料的 CallLog 提供商。工作拨号器会保存仅限工作的通话记录,且仅访问 工作通讯录电路交换机来电由主拨号器处理 并存储在个人通话记录中如果工作资料被删除,通话记录 与此工作资料相关联的所有工作资料都会被删除 数据。

第三方应用必须实现 ConnectionService

需要拨打电话和进行通话的第三方 VoIP 应用 集成到内置手机应用中的功能可以实现 ConnectionService API。用于工作通话的任何 VoIP 服务都必须满足此要求。这些应用 其好处是: 它们会显示在内置的系统拨号器和通话记录中。如果 应用实现 ConnectionService 安装在工作资料中,则只能通过拨号器访问 已安装到该工作资料中。

开发者实现 ConnectionService, 他们应将其添加到应用的清单文件中,并注册一个 PhoneAccount 替换为 TelecomManager。 电话账号代表一种用于拨打或接听电话的不同方法, 每个标签可以有多个 PhoneAccounts ConnectionService。注册电话账号后,用户 可以通过拨号器设置启用它

系统界面集成和通知

系统界面可为用户提供一致的集成式拨号体验 对于使用 ConnectionService API 作为后端来进行调用。公文包(如果在工作资料中使用应用) 图标会显示在来电和状态栏中。实现 安装在工作资料中的 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
}