reference : http://m.blog.csdn.net/blog/langzxz/45308199
reference : http://blog.csdn.net/hyhyl1990/article/details/46842915
Android原生是有應用程式許可權管理的,即是AppOps,只是Google把它預設隱藏了。
本文對AppOps機制做一簡要的分析和描述。
1. AppOps 簡介
AppOps全稱是 Application Operations,類似我們平時常說的應用程式的操作(許可權)管理。AppOps是Google原生Android包含的功能,但是Google在每次版本更新時都會隱藏掉AppOps的入口。
在今年的Google IO大會上,Google透露Android M ( Android 6.0 )會加入Application Permission Manage的功能,該功能應該就是基於AppOps實現的。
注意:AppOps雖然涵蓋了 App的許可權管理,但是Google原生的設計並不僅僅是對“許可權”的管理,而是對App的“動作”的管理。我們平時講的許可權管理多是針對具體的許可權(App開發者在Manifest裡申請的許可權),而AppOps所管理的是所有可能涉及使用者隱私和安全的操作,包括 access notification, keep weak lock, activate vpn, display toast 等等,有些操作是不需要Manifest裡申請許可權的。
2. 功能效果
Setting UI:
AppOps的許可權設定是在系統的Settings App裡, Settings -> Security -> AppOps.
點選某一app,可以檢視該app的許可權管理詳情
3. AppOps總體概覽
核心服務: AppOpsService
系統服務,系統啟動時該服務會啟動執行。
參考以下ActivityManagerService.java,ActivityManagerService啟動過程中:
配置檔案:appops.xml appops_policy.xml
Appops.xml位於 /data/system/目錄下,儲存各個app的許可權設定和操作資訊。
Appops_policy.xml位於 /system/etc/目錄下,該檔案只在appops strict mode enable時才會存在和使用。(根據原始碼的描述是這樣的,還沒有具體分析內容)
API介面: AppOpsManager
AppOpsService實現了大部分的核心功能邏輯,但它不能被其他模組直接呼叫訪問,而是通過AppOpsManager提供訪問介面。
UI層:AppOpsSummary,AppOpsCategory等
上傳UI顯示以及基本邏輯處理。
4. 結構圖
AppOps整體的工作框架基本如下:
Setting UI通過AppOpsManager與AppOpsService 互動,給使用者提供入口管理各個app的操作。
AppOpsService具體處理使用者的各項設定,使用者的設定項儲存在 /data/system/appops.xml檔案中。
AppOpsService也會被注入到各個相關的系統服務中,進行許可權操作的檢驗。
各個許可權操作對應的系統服務(比如定位相關的Location Service,Audio相關的Audio Service等)中注入AppOpsService的判斷。如果使用者做了相應的設定,那麼這些系統服務就要做出相應的處理。
(比如,LocationManagerSerivce的定位相關介面在實現時,會有判斷呼叫該介面的app是否被使用者設定成禁止該操作,如果有該設定,就不會繼續進行定位。)
5. 相關API介面
儘管在Android SDK裡能夠看到部分AppOps的API介面,但是Google對此解釋的很清楚:
This API is not generally intended for third party application developers; most features are only available to system applications. Obtain an instance of it throughContext.getSystemService
withContext.APP_OPS_SERVICE
.
即是說,這些API不是讓第三方app使用的,而是供系統應用呼叫的。
使用Android SDK開發應用,如果要呼叫這些api的話,也會編譯不通過。
但是想使用的話,可以嘗試把Android原始碼裡 AppOpsManager.java打包一下,把jar包匯入自己的工程,就可以使用了。
部分重要的API介面如下:
int checkOp(String op, int uid, String packageName)
Op對應一個許可權操作,該介面來檢測應用是否具有該項操作許可權。
int noteOp(String op, int uid, String packageName)
和checkOp基本相同,但是在檢驗後會做記錄。
int checkOpNoThrow(String op, int uid, String packageName)
和checkOp類似,但是許可權錯誤,不會丟擲SecurityException,而是返回AppOpsManager.MODE_ERRORED.
intnoteOpNoThrow(String op, int uid, String packageName)
類似noteOp,但不會丟擲SecurityException。
void setMode ( int code, int uid, String packageName, int mode)
這個是我們最需要的方法,改變app的許可權設定,但偏偏被google隱藏了。
code代表具體的操作許可權, mode代表要更改成的型別(允許 /禁止 / 提示)
正常情況下(如果OEM廠商沒有做特殊處理),把AppOpsManager.java打包,引入jar包到工程內,是可以使用上述API介面的,
也即是可以自行設計UI,提供入口來改變app許可權。
具體許可權對應的code,可以檢視AppOpsManager.java原始碼裡的描述。
AppOpsManager 是在是在Android 4.4版本支援的 (api 19)。獲得他例項的方法是
Context.getSystemService(Context.APP_OPS_SERVICE)
APP_OPS_SERVICE 的值是 “appops”
AppOpsManager類裡有一函式 int checkOp(String op, int uid, String packageName)
public int checkOp(String op, int uid, String packageName) { return checkOp(strOpToOp(op), uid, packageName); } public static int strOpToOp(String op) { Integer val = sOpStrToOp.get(op); if (val == null) { throw new IllegalArgumentException("Unknown operation string: " + op); } return val; }
功能就是快速檢測某個應用是否具有某些許可權,看上面的原始碼可知道最終呼叫了下面的程式碼
public int checkOp(int op, int uid, String packageName) { try { int mode = mService.checkOperation(op, uid, packageName); if (mode == MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } return mode; } catch (RemoteException e) { } return MODE_IGNORED; }
op 的值是 0 ~ 47,其中0代表粗略定位許可權,1代表精確定位許可權,24代表懸浮窗許可權。
uid 當前應用用到的uid ,可通過 Binder.getCallingUid()獲得。
packageName 應用程式的包名,通過getPackageName獲得。
通過上面的知識,寫一函式 用來判斷 程式是否具有某些許可權的函式
private static int checkOp(Context context, int op){ final int version = Build.VERSION.SDK_INT; if (version >= 19){ Object object = context.getSystemService("appops"); Class c = object.getClass(); try { Class[] cArg = new Class[3]; cArg[0] = int.class; cArg[1] = int.class; cArg[2] = String.class; Method lMethod = c.getDeclaredMethod("checkOp", cArg); return (Integer) lMethod.invoke(object, op, Binder.getCallingUid(), context.getPackageName()); } catch(NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return -1; }
呼叫上面的函式,返回 0 就代表有許可權,1代表沒有許可權,-1函式出錯啦