做者 / Fred Chungnode
Android 11 的最終版本已正式發佈!該版本延續了以前發行版本里不斷改進的隱私策略,爲用戶提供更加完善的控制機制和透明度,並幫助應用更好地處理自身的數據。android
其中不少優化將當前安全策略的最佳實踐應用於最近的 Android 發行版本中(它們並不只僅針對 Android 11)。在本文中,咱們將如下面四個最佳實踐做爲切入點,助力您的應用設計與時俱進,並計劃開始進行兼容性測試。segmentfault
隨着 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 ,並設計出對隱私更友好的應用。更多資源請參閱: