pms包管理服務分析-權限管理和鑑權過程

Android系統權限是建立在框架層上的一套權限解析分配和鑑權流程,其主要數據結構和校驗流程主要在pms(包管理服務)中實現。

簡單理解系統權限機制主要分爲權限解析、權限分配、鑑權這三個主要內容。

Sdk版本大於等於23後,新增了動態權限管理,讓Android系統權限管理更加靈活和自主。

下面來分析下Android 5.1上面的權限管理和鑑權過程。

權限的數據結構


涉及到系統權限的數據結構如圖7所示,分別來分析每個數據結構的內容和作用。

Ø BasePermission

系統權限的基本表示單元是BasePermission,Settings中維護了一個總的權限映射表mPermissions,所有的權限都會添加到mPermissions列表中,其中key是權限的名字,value是具體的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>();

  我們都知道絕大部分權限已經在系統中定義好,如READ_EXTERNAL_STORAGE讀寫外部存儲權限、READ_PHONE_STATE讀取手機狀態信息權限等,這些權限的定義都在framework-res.apk中,在掃描framework-res.apk過程中解析並添加到mPermissions映射表中。系統權限的定義:

 

[/frameworks/base/core/res/AndroidManifest.xml]

   <!-- Allows an application to send SMS messages. -->
    <permission android:name="android.permission.SEND_SMS"
        android:permissionGroup="android.permission-group.MESSAGES"
        android:protectionLevel="dangerous"
        android:permissionFlags="costsMoney"
        android:label="@string/permlab_sendSms"
        android:description="@string/permdesc_sendSms" />

系統中絕大部分的權限定義都在frameworks/base/services/core/res/AndroidManifest.xml中,最終會打包進framework-res.apk中並經過解析保存到Settings的mPermissions映射表中。

 

Ø PackageParser.Permission

PackageParser.Permission在上面分析PackageParser解析apk過程中有提及過,解析apkAndroidManifest.xml文件中的<permission>標籤後得到的權限表示。

PackageParser.Permission中包含一個對應的PermissionInfo

 

Ø PermissionInfo

權限信息的表示,其中包含權限等級的定義(NORMAL, DANGER, SIGNERATURE),另外實現了序列化,用戶於進程間通信。每個BasePermission實例中包含一個PermissionInfo的實例。

 

以上三個類結構是系統權限部分的定義,下面四個類結構和pkg強相關,表示pkg的權限狀態。

 

Ø GrantedPermissions

表示一個pkg已經賦予的權限,類裏面定義了一個字符串列表grantedPermissions保存pkg已經被賦予的所有權限。

[/frameworks/base/services/core/java/com/android/server/pm/GrantedPermissions.java]

class GrantedPermissions {
    int pkgFlags; 
    ArraySet<String> grantedPermissions = new ArraySet<String>();

 

Ø PackageSettingBase

PackageSettingBase繼承了GrantedPermissions類並添加更多和 pkg相關的信息,是一個pkg信息的基本表示類。PackageSettingBase保存爲了如pkgcodePath, resourcePath, signature等信息,同時PackageSettingBaseGrantedPermissions的子類,因爲也包含了pkg被賦予的權限列表。

 

Ø PackageSetting

PackageSetting繼承了PackageSettingBase類,並新增如PackageParser.PackageSharedUserSetting。那麼以後只要獲得一個pkgPackageSetting實例,就可以獲得對應的SharedUserSetting實例,通過這個SharedUserSetting實例來獲得與其sharedUser的其他pkg信息。同時PackageSettingGrantedPermissions的子類,那麼也就意味着只要拿到pkgPackageSetting實例就可以知道pkg已經被賦予了哪些權限。

 

Ø SharedUserSetting

SharedUserSetting繼承了GrantedPermissions,類裏面保存了一個packages的列表,表示這個列表中的所有應用共享該uid。同時共享這個uid所賦予的所有權限。


權限管理

我們知道在pms構造方法中掃描apk後,創建對應的PackageSetting實例並賦予給pkgmExtras,以後我們就可以通過pkg.mExtras獲得相應的PackageSetting實例。一開始掃描過程中,PackageSetting實例中保存的grantedPermissions列表爲空,權限的賦予會在pms構造方法的結束部分,通過調用updatePermissionsLPw方法遍歷所有已經安裝的pkg,一個一個的根據其請求的權限來確定是否賦予。

首先來看看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方法中主要做兩個事情,檢查無效的permissiontreepermission定義,另外一個就是根據傳入的flags是否帶有UPDATE_PERMISSIONS_ALL來遍歷mPackages包列表來更新所有安裝包的權限信息。重點分析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方法中實現了5.1權限賦予的流程。主要流程首先獲得pkgPackageSettings實例,上面我們知道pkgPackageSettings實例中包含了應用已經獲得的權限列表grantedPermissions。第一次開機掃描應用的過程中,這個列表爲空。通過遍歷包解析後得到的requestPermissions列表的權限的等級來判斷是否授權,如果請求的權限授權成功後,就會保存進入grantedPermissions列表中。

grantedPermissions列表會根據應用的更新而更新。

權限管理的基本流程如上。


鑑權過程

一般客戶端調用checkPermission方法鑑權的流程如下:

①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用戶(uid=0)和System用戶(uid=1000)直接鑑權通過。

⑤PackageManagerService.java--->checkUidPermission


查看pmscheckUidPermission方法:

[/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方法中調用SettingsgetUserIdLPr獲得一個Object實例,這個Object實例就是對應uidpkgSettings實例,裏面包含grantedPermissions信息。最後查詢grantedPermissions列表中是否包含所要檢查的權限即可判斷該uid是否具有該權限。

繼續分析SettingsgetUserIdLPr方法:

[/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);
        }
  }

這個方法首先判斷檢查的uid是否是應用的uid10000以上),如果是,通過uid來查詢mUserIds表來獲得對應uidpkgSettings實例(mUserIds就是uidpkgSettings的映射表)。通過進一步分析,mUserIds列表是在手機開機後讀取packages.xml緩存生成的。

 

鑑權流程2

① 根據PMS解析的申請權限數據,對應用添加gid

② 啓動應用進程時,指定所有對應的gid

 

舉例:android.permission.WRITE_EXTERNAL_STORAGE

① 應用在AndroidManifest.xml中申請android.permission.WRITE_EXTERNAL_STORAGE

② 安裝後,PMS解析該應用,併爲該應用添加sdcard_rw的組ID

③ 啓動該應用進程時,使用上面解析的gid


下一章我們來將證書的校驗流程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章