本文來自於騰訊Bugly公衆號(weixinBugly),未經做者贊成,請勿轉載,原文地址:https://mp.weixin.qq.com/s/OQRHEufCUXBA3d3DMZXMKQhtml
本文主要講解了Android 權限管理方面幾個點:java
提到Android 權限管理,業內人士都知道Google 在Android 6.0時提出了運行時權限管理機制,在Android 6.0以前,所申請的權限只須要在AndroidManifest.xml列舉就能夠,從而容易致使一些安全隱患,所以,在Android 6.0 時,Google 爲了更好的保護用戶隱私提出了新的權限管理機制(官網 :Working with System Permissions),同時將其分爲兩大類:android
(1)Normal Permissionsgit
Normal Permissions 通常不涉及用戶隱私,是不須要用戶進行受權的,好比手機震動、訪問網絡等;github
(2)Dangerous Permissionshell
Dangerous Permission通常是涉及到用戶隱私的,須要用戶進行受權(動態申請),好比讀取SIM卡狀態、訪問通信錄、SD卡讀寫等。api
經過 adb shell pm list permissions -d -g 能夠查看 Dangerous Permission (以權限組形式)安全
Dangerous Permission group微信
如上圖所示 :Dangerous Permission 通常以 Permission group 形式存在,只要 Permission group中某一個 permission 被Granted,則整個Permission group下的權限均被Granted (目前是這樣,之後規則說不定會變)。網絡
本節主要介紹介紹如何進行權限檢查及權限兼容,主要分爲如下幾類:
(1)targetSdkVersion>=23,終端設備是6.0(api 23)以上系統;
安裝的時候不會得到權限,在運行時向用戶申請對應權限。這部分權限檢查比較簡單,不涉及權限兼容,使用官方方案就能夠 ,使用 Context::checkSelfPermisson ,建議使用ContextCompat::checkSelfPermisson檢查權限 便可 ,通常檢查流程 以下:
判斷是否有對應權限
(ContextCompat::checkSelfPermisson)
判斷是否須要解釋對應權限用途(ActivityCompat::shouldShowRequestPermissionRationale)
若是須要解釋,則現實自定義權限界面便可
不須要解釋的話,直接請求對應權限
(ActivityCompat::requestPermissions)
上述狀況較爲簡單,在此再也不贅述。
(2)targetSdkVersion<23,終端設備是6.0(api 23)以上系統;
使用的是老的權限機制,在app 安裝時會詢問AndroidManifest.xml文件中的權限,可是用戶能夠在設置列表中關閉相關權限,這種狀況可能會對app正常運行形成必定影響。
(3) 終端設備系統小於6.0(api 23)
你們可能要問,終端設備系統小於6.0狀況還須要考慮嗎,確定是用的老的權限管理機制,在app 安裝時會詢問AndroidManifest.xml文件中的權限,用戶關閉不了,真的是這樣嗎 ?
答案是否認的,在實測中發現,目前有很多國產Rom 手機在6.0以前就有關閉權限的開關。這種狀況也是咱們兼容的對象。
下面將會以本身開發過程當中遇到的問題進行展開 ,目前企鵝FM支持免流了,須要使用READ_PHONE_STATE權限 (讀取SIM卡狀態),因爲以前未對改權限是否關閉沒有進行相關判斷,所以收到了不少例由於上述權限關閉,致使免流失敗的狀況。
適配過程以下 :
(1)使用 try catch 來檢查權限是否關閉
想法很簡單,若是改權限被用戶禁止了,那確定會異常,所以能夠在catch 中作文章,結果發現這一招根本沒有用,爲啥了 ?由於使用 READ_PHONE_STATE 權限的方法內部已經try catch ,外面沒法捕獲,所以該方法失效。
(2)ContextCompat::checkSelfPermisson
既然在6.0 可使用Context::checkSelfPermisson進行權限檢查,那可否使用support v4 中的ContextCompat::checkSelfPermisson 方法了,試一下,發如今api 23 如下失效,爲了探究緣由,查看了ActivityCompat::requestPermissons 內部實現,以下
ActivityCompat::requestPermissons
內部權限檢查方法在api 23 如下,使用的是 PackageManager::checkPermission,再去查看PackageManager::checkPermission方法,以下:發現只要權限在AndroidManifest.xml中註冊過,均會認爲該權限granted ,所以上述方法在api 23 如下也失效。
PackageManager::checkPermission
查閱相關資料和請教組內同事,發現Support V4 下面有一個專門檢查權限的工具類PermissionChecker。
(3)PermissionChecker
查看PermissionChecker源碼發現 ,PermissionChecker內部實際上使用的是AppOpsManagerCompt,而AppOpsManager是在api 19 加入進入的(AppOpsManager後面介紹)
PermissionChecker::checkPermission
進而查看AppOpsManagerCompat 內部實現
AppOpsManagerCompat::permissionToOp
IMPL實現以下:
IMPL實現
從上圖能夠看出:在api 23如下, AppOpsManagerImpl::permissionToOp 直接返回爲null ,這直接致使api 23如下權限檢查將會返回 granted ,所以,該方法在api 23 下,權限檢查方法也會失效。
(4)AppOpsManager
API 19以上 ,Google 官方提供了 AppOpsManager 類來檢查權限,看到這個api 時,腦海浮現出 「天無絕人之路啊」,裏面有兩個比較重要的方法 :AppOpsManager::checkOp(int op ,int uid ,String packageName) (hide方法)和AppOpsManager::checkOp(String op,int uid ,String packageName)(public 方法 ,api 23 以上可用),不經思考,直接寫出了以下兩個方法
1)AppOpsManager::checkOp(int op ,int uid ,String packageName)
須要使用反射 :
AppOpsManager::checkOp(int op ,int uid ,String packageName)
2)AppOpsManager::checkOp(String op,int uid ,String packageName)
API >= 23 纔可使用 :
AppOpsManager::checkOp(String op,int uid ,String packageName)
在實測中發現,api 低於23時 ,OP_READ_PHONE_STATE =51 找不到,致使反射失敗。
仔細察看了一下 6.0 (API 23 )_NUM_OP = 62,以下,爲什麼找不到51 了
6.0 (API 23 )_NUM_OP = 62
難不成 每一個版本還不同,查看其餘版本,驗證了這個想法:
**5.1.1 (API 22 )_NUM_OP = 48**
5.1.1
Op個數限制 (須要找到對應源碼說明**)詳情 參看 :
查看Android 對應版本 _NUM_OP (目前最高支持5.1.1版本)
此時,OP_READ_PHONE_STATE = 51 在6.0(API 23)如下,經過反射是找不到的,所以對於READ_PHONE_STATE權限檢查僅限於6.0及6.0以上。
同時也仔細看了一下AppOpsManager 類介紹,並非爲開發者設計的,不過其餘權限兼容可使用這種方法,前提是 要看OP_*是在什麼版本纔有的,需作兼容方案 。
public class AppOpsManager
extends Object
API for interacting with "application operation" tracking.
This API is not generally intended for third party application developers; most features are only available to system applications. Obtain an instance of it through Context.getSystemService with Context.APP_OPS_SERVICE.
(5)最後查看了幾個第三方權限庫(暫未看完)
既然在這裏講解跳轉到app 管理權限頁面的方法,可想而之,事情絕對不太簡單。Android 碎片化不只在存在於ui適配 ,一樣也存在於這裏,致使咱們沒法使用同一種方式跳轉到app管理權限頁面(適配,Android 開發永遠的痛)。
那有沒有辦法能夠簡化適配工做,減小開發量,方法固然有,不過須要咱們本身去總結和探索的,目前已有方法:
(1)直接跳轉到系統設置頁
Intent intent =newIntent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package",getPackageName(), null)); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
記得要添加上 try catch ,不加可能會crash。這種方式就不須要適配各個廠商的不一樣版本rom,缺點是,用戶只能跳轉到系統設置頁,而後去找對應app 的權限管理(總會有一些用戶找不到)
(2)站在前人的肩上
引用前人經驗:Android各大手機品牌手機跳轉到權限管理界面 (未一一驗證,畢竟沒那麼多手機)
那是否是前人經驗必定對了,那就不必定了,在當時多是對的,在如今可能就行不通了,如今以MIUI跳轉到app 權限管理頁面爲例進行說明。
1)MIUI 6/7
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); localIntent.putExtra("extra_pkgname", context.getPackageName()); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
2)MIUI 8
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR"); localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); localIntent.putExtra("extra_pkgname", context.getPackageName()); try{ startActivity(intent); }catch(Exception exception) { exception.printStackTrace(); }
對比1)和2)發現,在MIUI 6/7 和MIUI 8 上面,權限管理頁面的activity名字不同了,所以使用MIUI6/7的方法在MIUI8上就會失效,若是沒有加上try catch ,就會直接crash。
對於上述變化,做爲一個開發者通常都是不知道的,即使經過反饋發現了這個問題,也有可能不知道對應的activity是什麼,此刻要麼搜索網上有沒有相似解決方案,要麼求助於對應rom 開發廠商的開發者論壇 (有時解決迴應速度至關慢),那有沒有更好的辦法了,方法詳見(3)部分。
(3)查看某個ROM的某個版本的權限管理頁面的activity
這裏以華爲p8爲例簡要說明,詳細步驟以下:
1)經過設置找到對應app的權限管理頁面,以下:
企鵝fm在華爲p8上的權限管理頁面
2)找到對應頁面的activity
方法一:經過add 工具查看棧頂Activity
adb shell dumpsys activity | grep "mFocusedActivity"
企鵝fm在華爲p8上的權限管理頁面對應的activity
更爲詳細的堆棧信息
詳細的堆棧信息
方法二:使用Activity Tracer工具
使用方法:可參見我以前的文章 :Android開發—— 小工具,大效率
使用Activity Tracer查看權限管理頁面對應的activity
文中引用連接:
更多精彩內容歡迎關注騰訊 Bugly的微信公衆帳號:
騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!