[Android Pro] android 4.4 Android原生許可權管理:AppOps

weixin_33941350發表於2015-12-01

 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函式出錯啦

相關文章