運行時權限
Android 6.0,代號Marshmallow,自發布伊始,其主要的特征運行時權限就很受關注。因為這一特征不僅改善了用戶對于應用的使用體驗,還使得應用開發者在實踐開發中需要做出改變。
Android中有很多權限,但并非所有的權限都是敏感權限,于是6.0系統就對權限進行了分類,一般為下述幾類:
- 正常(Normal Protection)權限
- 危險(Dangerous)權限
- 特殊(Particular)權限
- 其他權限(一般很少用到)
危險權限實際上才是運行時權限主要處理的對象,這些權限可能引起隱私問題或者影響其他程序運行。Android 6.0發布這么久了,各大廠商也基本已經發布更新,很多應用也都已經適配到targetSdk23,相信大家對運行時權限都已經有所了解,這篇文章也講得很清楚: 聊一聊Android 6.0的運行時權限 ,這里就不多說了。
Android 6.0中,除了危險權限不再在安裝后授予,還有兩個特殊權限: SYSTEM_ALERT_WINDOW (設置懸浮窗,進行一些黑科技)和WRITE_SETTINGS(修改系統設置)。這里我們只關注 WRITE_SETTINGS權限 。
WRITE_SETTINGS
首先看下API文檔是怎么說的
Note:
If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen.
The app requests the user's approval by sending an intent with action ACTION_MANAGE_WRITE_SETTINGS .
The app can check whether it has this authorization by calling Settings.System.canWrite() .
也就是說,關于 WRITE_SETTINGS 權限的授權,做法是使用startActivityForResult,啟動系統設置的授權界面來完成。我們來看看示例代碼如何來請求 WRITE_SETTINGS 權限。
private static final int REQUEST_CODE_WRITE_SETTINGS = 1; private void requestWriteSettings() { Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse(quot;package:quot; getPackageName())); startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS ); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_WRITE_SETTINGS) { if (Settings.System.canWrite(this)) { Log.i(LOGTAG, quot;onActivityResult write settings grantedquot; ); } } }
上述代碼需要注意的是
- 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS啟動隱式Intent
- 使用quot;package:quot; getPackageName()攜帶App的包名信息
- 使用Settings.System.canWrite方法檢測授權結果
關于WRITE_SETTINGS權限,比較少應用會用到,一般也不建議應用申請,不然Android M也不會設立這道障礙,比危險權限的申請還要復雜。
但是,偏偏有應用要申請。不過還真是要感謝這個應用,不然我們也不會這么順利就發現了這個BUG。
BUG現場
如下圖。MM商場安裝完成后啟動,然后就跳到這個界面申請 允許修改系統設置 。這時候理論上是沒有這個權限的,但是彈出來的界面卻顯示已經打開了 允許修改系統設置 這個開關;不做任何更改退出這個界面,MM商場會重復跳轉到這個申請界面;這說明MM商場也檢測到自己并沒有被授予 WRITE_SETTINGS 權限。
MM商場申請WRITE_SETTINGS權限
到底有沒有,我們來一看究竟。首先查看系統內已安裝的三方應用:
adb shell pm list package -3
找到MM商場的包名,dump一下應用信息:
adb shell dumpsys package com.aspire.mm
如圖:
targetSdk
requested permissions
runtime permissions
可以看到:
- MM商店的targetSdk=23;
- requested permissions下面有 WRITE_SETTINGS 權限;
- 但是install permissions和runtime permissions下面并沒有找到 WRITE_SETTINGS 權限。
說明MM商店應用還沒有被授予此權限,那為什么申請此權限時彈出的界面顯示,switch開關狀態是開著的呢?很明顯這是一個bug!
為了驗證這個想法,我們來看看源碼。
源碼里面有真相
從 WRITE_SETTINGS 的API Reference里我們可以看到:
The app can check whether it has this authorization by calling Settings.System.canWrite() .
應用是通過 Settings.System.canWrite() .來判斷自己是否已經被授予了該權限。我們找到源碼,開啟順藤摸瓜模式:
這里會調用AppOpsManager.checkOpNoThrow獲取當前的ops狀態,繼續跟蹤下去:
最終,這里拿到的是 OP_WRITE_SETTINGS 的默認狀態MODE_DEFAULT:
但此時應用的WRITE_SETTINGS權限也沒有授予,canWrite當然返回的是false了。
這樣就說明應用自身判斷是否具有WRITE_SETTINGS權限的邏輯是沒錯的,那就是說很可能是Settings App里面可修改系統設置界面的switch開關狀態是錯誤的,我們繼續看源碼。
抽絲剝繭,找到根源
之前我們已經知道,應用是通過使用 Settings.ACTION_MANAGE_WRITE_SETTINGS 來啟動的設置界面,我們從這里入手,找到這個設置界面的代碼。我們在源碼全局搜索這個action:
最終指向的都是 WriteSettingsDetails.java ,找到switch開關初始化的地方:
這里我們注意到,調用了super的getPermissionInfo方法:
再看for循環里面的這段:
這時候傳入的mPermissions到底是什么呢?看AppStateAppOpsBridge的構造函數:
前面我們知道,AppStateWriteSettingsBridge繼承自AppStateAppOpsBridge:
我就納悶了,這個類名寫得清清楚楚,AppState WriteSettings Bridge,跟CHANGE_NETWORK_STATE有半毛錢關系呀?這里很明顯是個疑點。我們再看看getPermissionInfo里面的邏輯:
如果doseAnyPermissionMatch這個條件命中了,permissionState.staticPermissionGranted就設為true,然后就直接break跳出循環了!等等!如果這個應用同時申請了CHANGE_NETWORK_STATE和WRITE_SETTINGS權限,并且CHANGE_NETWORK_STATE的權限安裝后就會授予,那這里的判斷就出問題了,就會跳過WRITE_SETTINGS的判斷,直接將permissionState.staticPermissionGranted設為true。
那么我們回到WriteSettingsDetails.java,回到switch開關初始化的地方,
boolean canWrite = mWriteSettingsState.isPermissible(); mSwitchPref.setChecked(canWrite);
跟蹤到isPermissible方法:
真是巧啊,恰好MM商店同時申請了CHANGE_NETWORK_STATE和WRITE_SETTINGS權限(見圖 requested permissions ),又恰好OP_WRITE_SETTINGS的默認狀態是 MODE_DEFAULT ,機緣巧合之下,最終導致在WRITE_SETTINGS權限根本沒有被授予的情況下,isPermissible卻返回true,導致了這個BUG。
Android官方已修復
發現Android源碼的BUG了,當然想要提patch給他們了(雖然Android 6.0已經發布這么久了,很可能已經修復),結果到Google Git一看,確實修復了,但是Android 7.0才經修復了這個問題。提交記錄是這么說的:
Google官方的修復,驗證了我們之前分析的是正確的。
Tags: 安卓開發
文章來源:http://www.jianshu.com/p/bab716584316