隱私策略更新 | Android 11 應用兼容性適配

做者 / Fred Chungnode

Android 11 的最終版本已正式發佈!該版本延續了以前發行版本里不斷改進的隱私策略,爲用戶提供更加完善的控制機制和透明度,並幫助應用更好地處理自身的數據。android

其中不少優化將當前安全策略的最佳實踐應用於最近的 Android 發行版本中(它們並不只僅針對 Android 11)。在本文中,咱們將如下面四個最佳實踐做爲切入點,助力您的應用設計與時俱進,並計劃開始進行兼容性測試。segmentfault

  1. 處理內容 URI 分享
  2. 遞增式權限申請
  3. 在前臺訪問敏感數據
  4. 使用可重置的標識符

爲其它應用提供合適的 URI 權限

隨着 Android 11 中 軟件包可見性 的策略更新,目標 API 級別爲 30 的應用對設備上已安裝的其它軟件包默認僅擁有受限的可見性。這樣的設計旨在爲應用「查看」設備上的其它已安裝軟件包時,提供更好的「問責」制度。數組

爲了簡化遷移,對於常見的應用場景,咱們提供了 實現指南。一般,應用須要具有對其它已安裝軟件包的可見性(經過 PackageManager API 驗證)才能夠和其它軟件包進行交互。該特性一般應用於諸如:啓動服務,或者訪問屬於其它應用的 Content Provider。安全

您訪問 Content Provider 的模式可能不是經過發送顯式 intent 到某個特定的應用,而是經過發送隱式 intent。這樣的話,您沒法預判接收端應用(最終處理這個 intent 的應用)的目標 API 等級,而這個等級決定了接收端應用是否會受到 Android 11 中引入的應用包可見性限制的影響。app

爲了保證接收端應用可以"查看"您的軟件包,從而可以訪問任何共享的 URI,您須要在 intent 中添加 FLAG_GRANT_READ_URI_PERMISSION 和/或者 FLAG_GRANT_WRITE_URI_PERMISSION。請注意,寫入權限並不包含讀取訪問權限。當被 intent 觸發之後,接收端應用會被授予對相關 URI 的臨時訪問權限。框架

val shareIntent = Intent(Intent.ACTION_VIEW).apply {
    flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    data = //須要向其它應用共享的 Content Uri
}

隨着應用的目標 SDK 版本的更新(即便更新到 Android 11 以前的版本),請您特別關注涉及到與其它應用分享 Content Provider 訪問權限的用例,並確保授予適當的 URI 權限。不管哪一個應用是這 content provider 的擁有者,這個策略都管用。ide

一般,咱們要將數據訪問程度限制爲當前任務所需的水平,若是您遵循了這個最佳實踐,您的 Content Provider 訪問權限應該是按照個別 URI 模式 設定的。只要作到這點,您的 Content Provider 就已經能夠兼容 Android 11 了!post

遞增式申請權限

Android 用戶研究報告 顯示,在請求獲取用戶的受權時,那些符合用戶指望值的請求更有可能被獲准。所以,當您應用中的某個功能須要這些權限時,最佳實踐是在上下文中 請求權限測試

用戶授予權限的緣由排行。來源:Android 用戶研究報告

△ 大多數用戶會爲了使用某個特定的功能而選擇贊成受權

△ 大多數用戶會爲了使用某個特定的功能而選擇贊成受權

這項策略對於敏感權限尤爲適用,如位置訪問權限。從 Android 10 開始,平臺引入了細粒度的位置模型,區分了前臺和後臺位置訪問。大多數位置場景僅須要前臺訪問,好比當用戶在操做 Activity 的時候。

事實上,Google Play 已經出臺了相關政策限制沒必要要的後臺位置訪問。要檢查您的應用可能在哪些地方從後臺訪問位置,請參閱:後臺訪問位置信息文檔。若是您的應用須要後臺位置權限,好比地理圍欄應用,請確保後臺位置對您的功能設計是不可或缺的。

對於適用的應用,須要先申請前臺位置權限,而後在稍晚些再申請後臺位置權限。這種方法爲用戶提供了控制權限授予級別的選擇。此外,您還能夠有策略地顯示一個權限申請的說明,或者設計一個合理的交互界面,爲用戶提供更多信息,以說明用戶授予位置權限以後所得到的的功能提高。

Android 11 要求面向 API 級別爲 30 的應用使用遞增式位置權限請求。任何同時申請前臺位置權限(不管是粗略位置仍是精確位置)和後臺位置權限的請求都會被忽略而且返回以下錯誤信息。

E/GrantPermissionsActivity: Apps targeting 30 must have foreground permission before requesting background and must request background on its own.

請注意在 requestPermissions() API 請求的任何其它非位置權限也會同時被忽略。

由於 requestPermissions API 接受一個由所需權限組成的數組做爲參數,您現有的代碼可能已有了同時申請多個權限的狀況(以下所示),所以這裏咱們鼓勵你們檢查和審覈一下本身的代碼,若是代碼修改影響到用戶交互,則須要按需進行相應設計。

若是啓用了 ActivityCompat 或者框架 API:

requestPermissions(
        arrayOf(
            // 不要同時申請前臺位置權限和後臺位置權限
            // 由於全部同時申請的權限都會被忽略
            // 而是經過增量式請求位置權限
            android.Manifest.permission.ACCESS_COARSE_LOCATION,         
            android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,

            ...)

        )

一樣地,若是啓用了 Jetpack Activity 庫

// 使用 Activity 庫
val requestPermissionsLauncher =
                registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
                    map: MutableMap<String, Boolean> ->
                        ...
                }
...

requestPermissionsLauncher.launch(
        arrayOf(
            // 不要同時申請前臺位置權限和後臺位置權限
            // 由於全部同時申請的權限都會被忽略
            // 而是經過增量式請求位置權限
            android.Manifest.permission.ACCESS_COARSE_LOCATION,         
            android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,

            ...)
    )

合理訪問位置、麥克風和相機

Android 的系統設計支持公開透明地訪問敏感數據,好比麥克風、相機和位置。例如,應用在前臺的時候,也就是用戶能看到應用界面的時候,纔可使用麥克風和相機。這樣能夠提升公開透明性,因此用戶能夠在知情的狀況下啓用相關特性。

若是您的應用包含訪問敏感數據的前臺服務,請確認應用場景中包含直接的用戶交互,使用戶能夠控制所執行的任務。例如,在一個視頻會議應用中,您可使用一個前臺服務來支持活躍的會議進程,其中會涉及到訪問麥克風和相機。其中應該包含一個對於用戶可見的用於啓動和中止會議進程的操做,也就是該前臺服務。

此外,您的應用必須正確設置 foregroundServiceType 屬性來代表位置、麥克風或者相機的用途。這樣能夠爲應用增長系統可見性,同時在 Android 11 中也是必須配置的屬性。更多信息請訪問:Android 11 中的前臺服務

您可能須要在 AndroidManifest 中聲明多種數據類型的用途。

android:foregroundServiceType = "microphone location camera"

 
若是您的功能是基於 WorkManager 的 長時間運行的 worker 來實現的,那麼它其實是運行在叫作 SystemForegroundService 的前臺服務上。您應該在應用的 AndroidManifest 中包含適合的前臺服務類型,它會同 Jetpack 庫的 AAR AndroidManifest 文件 合併。  

在應用的 AndroidManifest 中添加下面的聲明,而且在其中定義所需的前臺服務類型。

<service
    android:name="androidx.work.impl.foreground.SystemForegroundService"
    android:foregroundServiceType="location|microphone"//這裏選擇和您的應用相關的服務類型
    tools:node="merge" />

當您須要將 worker 之前臺服務運行時,您須要將合適的前臺服務類型傳入 ForegroundInfo 對象。傳入的服務類型必須和上面在 AndroidManifest 中添加的聲明一致或者是其子集。

setForeground(
    ForegroundInfo(NOTIF_ID,
           notification.build(),
           // 前臺服務類型
        ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION or ...)
)

棄用不可重置標識符

Android 系統使用了一些不可重置的硬件標識符,好比 IMEI 以支持各類操做系統功能。出於隱私方面的考慮,這些相對「強大」的持久性和惟一性的標識符不適合用於大部分應用場景。

從 Android 10 開始,系統對不可重置的設備標識符 實施了限制。好比,只有帶有 READ_PRIVILEGED_PHONE_STATE 權限的系統應用才能夠經過 getSimSerialNumber()) 方法訪問 SIM 卡的硬件標識符。在 Android 11 中,操做系統對 getIccId()) 方法也增長了相似的限制來進一步 限制訪問權限,如今該方法僅返回空字符串。

對於須要使用 SIM 卡信息做爲惟一性標識的應用,須要在 Android 11 裏進行「空字符串」的兼容性檢查。一個替代方案是使用 getSubscriptionId()) 方法,它會針對設備上指定的 SIM 卡信息返回一個以數字 1 開頭的惟一索引值,也就是說,若是同一張 SIM 卡被從新安裝到設備上的話,它會保持以前的訂閱標識符,除非設備恢復出廠設置。更多請參閱:惟一標識符最佳作法

平臺和 Google Play 服務爲應用提供了一些其它的 標識符,提供各類惟一性、可重置性和有做用域限制的標識符,適用於各類不一樣的應用場景。更多請參閱:惟一標識符最佳作法

以上內容可以幫助你們更快更新適配最新的 API ,並設計出對隱私更友好的應用。更多資源請參閱:

相關文章
相關標籤/搜索