網路與電話

本指南中的功能說明可在裝置政策控制器 (DPC) 應用程式中實作的網路和電話管理功能。本文件含有程式碼範例,您也可以使用 TestDPC 應用程式做為 Android 企業功能的程式碼範例來源。

DPC 應用程式可以在個人裝置上以設定檔擁有者模式執行,或在全代管裝置上的裝置擁有者模式下執行。下表列出 DPC 以設定檔擁有者模式或裝置擁有者模式執行時可用的功能:

功能 商家檔案擁有者 裝置擁有者
在個人資料中存取工作聯絡人
確保工作流量採用安全的網路連線
跨區域設定單一無線網路 ID
為工作資料夾指定獨立的撥號程式

在個人資料中存取工作聯絡人

EMM 可讓使用者的個人資料夾存取工作聯絡人,以便透過本機搜尋和遠端目錄查詢功能存取使用者的個人和工作聯絡人。在個人裝置上,個人設定檔中的單一撥號程式可以撥打及接聽個人通話和工作通話。此外,工作聯絡人也充分整合至系統 UI。如果工作資料夾已加密,個人設定檔就無法使用其中的資料。

與系統 UI 整合

系統 UI 會使用公事包圖示來表示傳入的工作通話。callLog 也會顯示圖示,指定傳入和傳出工作呼叫。個人撥號程式和聯絡應用程式可以透過遠端目錄查詢,顯示工作聯絡人的來電顯示資訊,因此不需要該聯絡人已在本機裝置上同步。訊息應用程式可以執行當地的來電顯示和搜尋功能。

Android 相容性定義說明文件 (CDD) 包含工作聯絡人要在預設撥號程式中顯示的要求,以及對聯絡人和訊息應用程式標示標記,表明這些聯絡人來自工作資料夾。

可以存取並搜尋工作聯絡人

使用者可以從個人設定檔存取及撥打電話給工作聯絡人,該個人檔案會顯示在撥號應用程式的搜尋畫面中。使用者可以使用自動完成功能搜尋本機聯絡人 (使用自動完成功能),這些聯絡人會同步到裝置,並透過遠端目錄查詢列出。

管理主要設定檔中的工作聯絡人

DPC 可控制搜尋工作聯絡人的權限。在設定檔擁有者模式下,DPC 可管理個人資料夾中工作聯絡人的瀏覽權限。詳情請參閱「建構裝置政策控制器」。

依個人資料夾搜尋工作聯絡人的功能預設為啟用。

針對工作流量確保安全的網路連線

在裝置擁有者模式或設定檔擁有者模式執行時,裝置政策控制器可使用持續待機的虛擬私人網路 (VPN) 連線,強制應用程式透過無法略過的指定 VPN 應用程式傳遞流量。透過永久連線 VPN 連線,DPC 可確保來自工作資料夾或受管理裝置的網路流量透過 VPN 服務傳遞,且無需使用者介入。這項程序會為工作資料夾中的持續流量建立安全的網路連線。

關於永久連線的 VPN 連線

做為系統架構的一部分,系統會自動管理 VPN 轉送,使用者無法略過 VPN 服務。如果 VPN 服務在鎖定模式下中斷,流量就無法流入開放網際網路。對於實作 VpnService 的應用程式,永久連線 VPN 提供透過可信任伺服器管理安全 VPN 連線的架構,並藉此保持連線。無論連線是透過 Wi-Fi 或行動網路,VPN 服務都會自動重新啟動應用程式之間的連線。如果裝置重新啟動,架構會重新啟動 VPN 連線。

使用者可以看到 VPN 服務的連線。如果公司擁有的裝置,使用者不需要確認 VPN 在一律開啟模式下顯示的同意聲明對話方塊。使用者的 VPN 網路設定允許手動啟用永久連線。

如果 DISALLOW_CONFIG_VPNtrue,使用者將無法設定 VPN。啟用 DISALLOW_DEBUGGING_FEATURES 即可禁止使用者使用 ADB 偵錯指令覆寫永久連線的 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 服務的代管設定是由 DevicePolicyManager 使用其 setApplicationRestrictions() 方法進行設定。
  • 受管理的設定會使用任意鍵/值組合,這個範例應用程式會在其他地方使用鍵/值組合來設定 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(...);
}

跨區域設定單一無線網路 ID

以裝置擁有者模式或設定檔擁有者模式執行時,裝置政策控制器 (DPC) 可將多個憑證授權單位 (CA) 憑證與單一無線網路設定建立關聯。透過這項設定,裝置可以連線至網路名稱或服務集 ID (SSID) 相同的無線存取點,但以不同的 CA 憑證進行設定。如果貴機構的無線網路位於多個地理區域,且每個區域都需要不同的憑證授權單位,這個選項就能派上用場。舉例來說,法律簽章可要求當地主管機關必須有一個地區憑證授權單位。

注意:自 API 18 (Jelly Bean) 起,Android 已支援 setCaCertificate,但 IT 管理員必須分別為每個憑證授權單位佈建網路,確保無論所在區域為何,裝置都能順利執行驗證作業。

指定 CA 憑證來識別伺服器

如要指定使用相同 SSID 識別伺服器的 X.509 憑證清單,請使用 WifiEnterpriseConfig.setCaCertificates() 在無線設定中加入所有相關 CA。

如果伺服器 CA 與其中一個指定憑證相符,就會視為有效。預設名稱會自動指派給憑證,並在設定中使用。WifiManager 會安裝憑證,並在網路啟用時自動儲存設定,並在設定刪除時移除憑證。

如要取得與無線設定相關聯的所有 CA 憑證,請使用 WifiEnterpriseConfig.getCaCertificates() 傳回 X509Certificate 物件清單。

使用多個 CA 憑證新增無線設定

  1. 驗證伺服器的身分:
    1. 載入 X.509 CA 憑證。
    2. 載入用戶端的私密金鑰和憑證。如需讀取憑證檔案的範例,請參閱使用 HTTPS 和 SSL 確保安全性
  2. 建立新的 WifiConfiguration 並設定其 SSID 和金鑰管理。
  3. 在這個 WifiConfiguration 上設定 WifiEnterpriseConfig 執行個體。
    1. 使用 setCaCertificates() 找出具有 X509Certificate 物件清單的伺服器。
    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 以用於呼叫後端的 Voice over IP (VoIP) 應用程式。這可為工作資料夾中的 VoIP 應用程式提供相同的整合式系統 UI 撥號體驗,讓工作流程成為核心功能。工作通話帳戶的來電與個人通話帳戶的來電不同。

使用者可以選擇透過手機帳戶,使用已加入許可清單的工作撥號程式撥打及接聽電話。所有來自該撥號程式或撥打至工作電話帳戶的通話,都會記錄在工作資料夾的 CallLog 供應商服務中。工作撥號程式會保留僅限工作聯絡人存取的工作專用通話記錄。電路切換呼叫會由主要撥號程式處理,並儲存在個人通話記錄中。如果刪除工作資料夾,與該工作資料夾相關聯的通話記錄也會一併刪除,以及所有工作資料夾資料。

第三方應用程式必須實作 ConnectionService

需要撥打電話,且將這些呼叫整合至內建手機應用程式的第三方 VoIP 應用程式可以實作 ConnectionService API。凡是用於工作通話的 VoIP 服務都必須符合這項條件。這類應用程式的優點是將通話視為傳統行動通話,例如顯示在內建系統撥號程式和通話記錄中。如果實作 ConnectionService 的應用程式已安裝在工作資料夾中,則只能由該工作資料夾安裝的撥號程式存取。

開發人員實作 ConnectionService 後,應將其新增至應用程式的資訊清單檔案,並使用 TelecomManager 註冊 PhoneAccount。電話帳戶代表不同的撥打或接聽電話的方式,而且每個 ConnectionService 可以有多個 PhoneAccounts。註冊電話帳戶之後,使用者可以透過撥號程式設定啟用。

系統 UI 整合和通知

系統 UI 可讓使用 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
}