Android系統權限(xian)是(shi)建立在(zai)框架層上的(de)一套權限(xian)解析分(fen)配和鑒權流(liu)(liu)程(cheng),其主要(yao)數(shu)據結構和校(xiao)驗流(liu)(liu)程(cheng)主要(yao)在(zai)pms(包(bao)管理(li)服務)中實現。
簡單理解系(xi)統(tong)權(quan)限(xian)機制主要分(fen)為權(quan)限(xian)解析(xi)、權(quan)限(xian)分(fen)配、鑒(jian)權(quan)這三(san)個主要內容。
Sdk版本大于等于23后,新增了動態權限(xian)管(guan)理(li)(li),讓(rang)Android系(xi)統權限(xian)管(guan)理(li)(li)更加靈活(huo)和自主。
下面(mian)來分析(xi)下Android 5.1上面(mian)的權(quan)限管(guan)理和鑒(jian)權(quan)過程(cheng)。
權限的數據結構
涉及(ji)到系統(tong)權限的(de)數(shu)據(ju)結構如上圖所示(shi),分(fen)別(bie)來(lai)分(fen)析每(mei)個(ge)數(shu)據(ju)結構的(de)內容(rong)和(he)作用(yong)。
Ø BasePermission
系統權(quan)限(xian)(xian)的(de)(de)基本表示單(dan)元是BasePermission,Settings中(zhong)(zhong)維護了一個總的(de)(de)權(quan)限(xian)(xian)映射(she)表mPermissions,所有(you)的(de)(de)權(quan)限(xian)(xian)都會(hui)添加(jia)到mPermissions列表中(zhong)(zhong),其中(zhong)(zhong)key是權(quan)限(xian)(xian)的(de)(de)名字(zi),value是具體的(de)(de)BasePermission實例。
[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]
// Mapping from permission names to info about them.
final ArrayMap<String, BasePermission> mPermissions =  new ArrayMap<String, BasePermission>();我(wo)們(men)都(dou)知道絕大部分權限(xian)已經在(zai)系(xi)統中(zhong)定義好,如READ_EXTERNAL_STORAGE讀寫外部存儲權限(xian)、READ_PHONE_STATE讀取(qu)手機(ji)狀態信(xin)息權限(xian)等,這些權限(xian)的定義都(dou)在(zai)framework-res.apk中(zhong),在(zai)掃描framework-res.apk過程中(zhong)解析并(bing)添(tian)加到mPermissions映射表中(zhong)。系(xi)統權限(xian)的定義:
[/frameworks/base/core/res/AndroidManifest.xml]
系統中(zhong)絕大部(bu)分的權限(xian)定義都(dou)在frameworks/base/services/core/res/AndroidManifest.xml中(zhong),最終會打包進framework-res.apk中(zhong)并經過解(jie)析保存到(dao)Settings的mPermissions映射表中(zhong)。
Ø PackageParser.Permission
PackageParser.Permission在上面分(fen)析PackageParser解析apk過程(cheng)中有(you)提及過,解析apk的(de)AndroidManifest.xml文件中的(de)標簽后得(de)到的(de)權限表示。
PackageParser.Permission中(zhong)包含一個對應的(de)PermissionInfo。
Ø PermissionInfo
權(quan)限(xian)信息的表示,其(qi)中包(bao)含權(quan)限(xian)等級的定義(yi)(NORMAL, DANGER, SIGNERATURE),另外實(shi)現(xian)了序列(lie)化(hua),用(yong)戶(hu)于進程(cheng)間通信。每個BasePermission實(shi)例(li)中包(bao)含一個PermissionInfo的實(shi)例(li)。
以上(shang)三個類結構(gou)是系統權限部分的定義,下(xia)面四(si)個類結構(gou)和(he)pkg強相(xiang)關,表示pkg的權限狀態。
Ø GrantedPermissions
表(biao)示一(yi)(yi)個(ge)pkg已經(jing)(jing)賦予(yu)的(de)(de)權限(xian),類里面定義了一(yi)(yi)個(ge)字符串列(lie)表(biao)grantedPermissions保存pkg已經(jing)(jing)被賦予(yu)的(de)(de)所有權限(xian)。
[/frameworks/base/services/core/java/com/android/server/pm/GrantedPermissions.java]
class GrantedPermissions {
    int pkgFlags; 
    ArraySet<String> grantedPermissions = new ArraySet<String>();Ø PackageSettingBase
PackageSettingBase繼承(cheng)了GrantedPermissions類(lei)并添加更多和 pkg相關(guan)的(de)信息(xi),是(shi)一個pkg信息(xi)的(de)基(ji)本表示類(lei)。PackageSettingBase保存為了如pkg的(de)codePath, resourcePath, signature等信息(xi),同時PackageSettingBase是(shi)GrantedPermissions的(de)子類(lei),因為也包(bao)含了pkg被賦予的(de)權(quan)限列(lie)表。
Ø PackageSetting
PackageSetting繼承了PackageSettingBase類(lei)(lei),并新增如PackageParser.Package和SharedUserSetting。那(nei)么以(yi)后(hou)只(zhi)要獲(huo)得一(yi)個(ge)pkg的(de)(de)PackageSetting實(shi)例(li),就(jiu)可以(yi)獲(huo)得對應的(de)(de)SharedUserSetting實(shi)例(li),通(tong)過這個(ge)SharedUserSetting實(shi)例(li)來獲(huo)得與(yu)其(qi)sharedUser的(de)(de)其(qi)他(ta)pkg信息。同(tong)時PackageSetting是GrantedPermissions的(de)(de)子類(lei)(lei),那(nei)么也就(jiu)意味著只(zhi)要拿(na)到(dao)pkg的(de)(de)PackageSetting實(shi)例(li)就(jiu)可以(yi)知(zhi)道(dao)pkg已經被賦(fu)予了哪些(xie)權(quan)限。
Ø SharedUserSetting
SharedUserSetting繼承(cheng)了(le)(le)GrantedPermissions,類里面保存了(le)(le)一個(ge)packages的列(lie)表,表示(shi)這(zhe)個(ge)列(lie)表中(zhong)的所有應用共享該uid。同時共享這(zhe)個(ge)uid所賦予(yu)的所有權限。
權限管理
我(wo)們知道在pms構(gou)造方(fang)法中(zhong)掃(sao)描(miao)apk后,創(chuang)建對應的(de)PackageSetting實例(li)并賦予給pkg的(de)mExtras,以(yi)后我(wo)們就可以(yi)通過(guo)(guo)pkg.mExtras獲得相應的(de)PackageSetting實例(li)。一(yi)開始掃(sao)描(miao)過(guo)(guo)程中(zhong),PackageSetting實例(li)中(zhong)保(bao)存的(de)grantedPermissions列表為空,權(quan)限的(de)賦予會在pms構(gou)造方(fang)法的(de)結束(shu)部分(fen),通過(guo)(guo)調用(yong)updatePermissionsLPw方(fang)法遍歷(li)所有(you)已經(jing)安裝的(de)pkg,一(yi)個一(yi)個的(de)根(gen)據其請求(qiu)的(de)權(quan)限來確定(ding)是否賦予。
首先來看(kan)看(kan)updatePermissionsLPw方法:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
    private void updatePermissionsLPw(String changingPkg,PackageParser.Package pkgInfo, int flags) {
        // Make sure there are no dangling permission trees.
        // 更新permission trees列表,去除無效的permission tree
        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        // 更新permission列表,去除無效無主的permission定義
        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
            for (PackageParser.Package pkg : mPackages.values()) {
                if (pkg != pkgInfo) {
                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
                            changingPkg);
                }
            }
        }
        if (pkgInfo != null) {
            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
        }
    }updatePermissionsLPw方法中主要(yao)做兩個事情,檢查無效的(de)permissiontree和permission定義,另(ling)外(wai)一(yi)個就(jiu)是根(gen)據(ju)傳(chuan)入的(de)flags是否帶有UPDATE_PERMISSIONS_ALL來遍歷mPackages包列表來更新所有安裝包的(de)權(quan)限(xian)信息。重點分析grantPermissionsLPw方法。
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
     private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {        
final PackageSetting ps = (PackageSetting) pkg.mExtras; 
        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
              ArraySet<String> origPermissions = gp.grantedPermissions;
        boolean changedPermission = false;  
        /* 獲得解析pkg中請求的權限數量,循環一個一個去決定是否分配給該應用或者sharedUserId */     
        final int N = pkg.requestedPermissions.size();
        for (int i=0; i<N; i++) {
            final String name = pkg.requestedPermissions.get(i);
            final boolean required = pkg.requestedPermissionsRequired.get(i);
            final BasePermission bp = mSettings.mPermissions.get(name);
            /* 檢查應用申請的這個權限是否存在有效,如果無效,忽略 */
            if (bp == null || bp.packageSetting == null) {
                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                    Slog.w(TAG, "Unknown permission " + name  + " in package " + pkg.packageName);
                }
                continue;
            }
            final String perm = bp.name;
            boolean allowed;
            boolean allowedSig = false;
            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
            if (level == PermissionInfo.PROTECTION_NORMAL || level == PermissionInfo.PROTECTION_DANGEROUS) {
                /* 權限等級為NORMAL和DANGER的權限,只要申請就分配 */
                allowed = (required || origPermissions.contains(perm)
                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
            } else if (bp.packageSetting == null) {
                /* 無頭的權限,直接拒絕 */
                allowed = false;
            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
/* 權限等級要求簽名的,檢查該pkg簽名是否和platform apk簽名一致,一致則返回 */
                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
                if (allowed) { allowedSig = true; }
            } else {
                allowed = false;
            }
            if (allowed) {                
                if (allowed) {
                    if (!gp.grantedPermissions.contains(perm)) {
                        /* 權限允許賦予后,如果pkg已獲得權限列表中沒有,則添加 */
                        changedPermission = true;
                        gp.grantedPermissions.add(perm);
                        gp.gids = appendInts(gp.gids, bp.gids);
                    } 
                } 
            } else {
            /* 權限不允許 */
                if (gp.grantedPermissions.remove(perm)) {
/* 如果已獲取權限列表中存在去除的該權限,說明apk版本有變化 */
                    changedPermission = true;
                    gp.gids = removeInts(gp.gids, bp.gids);
                }
            }
        }
   } grantPermissionsLPw方法中實(shi)現了(le)5.1權(quan)限賦予的(de)(de)(de)(de)(de)流程。主要(yao)流程首先獲(huo)得pkg的(de)(de)(de)(de)(de)PackageSettings實(shi)例(li),上面我們知道pkg的(de)(de)(de)(de)(de)PackageSettings實(shi)例(li)中包含了(le)應(ying)用(yong)已經獲(huo)得的(de)(de)(de)(de)(de)權(quan)限列(lie)(lie)表grantedPermissions。第一次開機掃描應(ying)用(yong)的(de)(de)(de)(de)(de)過程中,這個列(lie)(lie)表為空(kong)。通過遍歷包解析后得到的(de)(de)(de)(de)(de)requestPermissions列(lie)(lie)表的(de)(de)(de)(de)(de)權(quan)限的(de)(de)(de)(de)(de)等(deng)級來判斷是(shi)否授權(quan),如果請求的(de)(de)(de)(de)(de)權(quan)限授權(quan)成功后,就(jiu)會保存(cun)進入grantedPermissions列(lie)(lie)表中。
grantedPermissions列表會根(gen)據應用的(de)更新而更新。
權限管理(li)的基本流程(cheng)如(ru)上。
鑒權過程
一般客戶端調用checkPermission方法(fa)鑒權的流程如(ru)下:
①Context.java--->public int checkPermission(String permission, int pid, int uid)
②ActivityManagerService.java--->public int checkPermission(String permission, int pid, int uid)
③ActivityManagerService.java---> checkComponentPermission
④ActivityManager.java--->checkComponentPermission
Root用(yong)戶(uid=0)和System用(yong)戶(uid=1000)直接鑒(jian)權通(tong)過。
⑤PackageManagerService.java--->checkUidPermission
查看pms的checkUidPermission方法:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
    @Override
    public int checkUidPermission(String permName, int uid) {
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions)obj;
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                ArraySet<String> perms = mSystemPermissions.get(uid);
                if (perms != null && perms.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }發現checkUidPermission方法(fa)中(zhong)(zhong)調用Settings的getUserIdLPr獲(huo)得一(yi)個Object實(shi)例,這個Object實(shi)例就是(shi)(shi)對應uid的pkgSettings實(shi)例,里面包(bao)含(han)grantedPermissions信息。最(zui)后(hou)查詢(xun)grantedPermissions列表中(zhong)(zhong)是(shi)(shi)否(fou)包(bao)含(han)所(suo)要檢查的權限即可判斷(duan)該(gai)uid是(shi)(shi)否(fou)具(ju)有該(gai)權限。
繼續分析Settings的getUserIdLPr方法:
[/frameworks/base/services/core/java/com/android/server/pm/Settings.java]
  public Object getUserIdLPr(int uid) {
        if (uid >= Process.FIRST_APPLICATION_UID) {
            final int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            return index < N ? mUserIds.get(index) : null;
        } else {
            return mOtherUserIds.get(uid);
        }
  }這個方(fang)法首先判斷檢查(cha)的(de)uid是否(fou)是應(ying)用(yong)的(de)uid(10000以上(shang)),如果是,通過(guo)uid來查(cha)詢(xun)mUserIds表(biao)來獲(huo)得對應(ying)uid的(de)pkgSettings實例(mUserIds就是uid和pkgSettings的(de)映(ying)射表(biao))。通過(guo)進一步分析,mUserIds列表(biao)是在手機開機后讀取packages.xml緩(huan)存(cun)生成的(de)。
鑒權流程2:
① 根據(ju)PMS解析的申請權限(xian)數據(ju),對應用添加gid
② 啟動(dong)應用進(jin)程時,指定所有對應的gid
舉(ju)例:android.permission.WRITE_EXTERNAL_STORAGE
① 應(ying)用(yong)在(zai)AndroidManifest.xml中申(shen)請android.permission.WRITE_EXTERNAL_STORAGE
② 安裝后,PMS解(jie)析(xi)該應用,并為該應用添加sdcard_rw的組ID。
③ 啟動該應(ying)用進程(cheng)時,使(shi)用上面解析的(de)gid。


