Android PackageManager Service详解(5.1源码)(三)

2:PackageManagerService

Android既然基于linux,那我们能不能将c/c++代码交叉编译成可执行文件然后放到目标机器上跑呢?当然可以,不过前提你得有执行权限,事实上,android有一部分后台服务是纯linux程序(不需要davik虚拟机资源),比如service manager和media server等。

Android应用没有权限启动linux程序,同样的也无法主动从zygotefork出一个子进程来执行自身代码,那一个app安装后,如何拿到这个app的入口信息?代码文件(dex、so)以及相关资源释放目录的权限如何设置?APP运行时被准许的权限有哪些?这些都是PMS在扫描完一个app后需要确定的。

    所以,扫描一个APK, 需要做的事情有:

1)  获取APP暴露的所有组件及相关数据

2)  获取APP声明和准许的权限数据

3)  生成app id,然后基于其生成的user id来作为app本地目录的访问权限控制

4)  释放代码文件,包含dex和so文件

5)  将1和2数据缓存到PMS中,供系统运行时使用。

2.1 PMS初始化流程

系统启动后,systemserver会调用如下代码初始化PMS

   public static final PackageManagerService main(Context context, Installer installer,

            boolean factoryTest, boolean onlyCore) {

        PackageManagerService m = new PackageManagerService(context, installer,

                factoryTest, onlyCore);

        ServiceManager.addService(“package”, m);

        return m;

}

除了创建PackageManagerService并将其添加到service manager外,什么也没做,接着看PMS

构造函数:

1)  创建Settings对象,并通过addSharedUserLPw添加android预定义的shared user id

2)  初始化systemConfig,然后调用getPermissions获取系统预先配置的permission以及对应group数据,用来初始化settings.permissions列表

3)  通过调用systemConfig.getSharedLibraries,获取系统预先配置的共享库文件(.jar)并初始化mSharedLibraries

4)  通过mSettings.readLPw读取本地缓存文件(/data/system/package.xml)来初始化Settings,

那些数据需要缓存,我这边总结了下,主要有settings内部的permission和permission tree列表和package内部的grant permission列表,最后修改时间,rename过的包等等,总之,那些在运行期生成或会改变的数据,就需要缓存。

5)  遍历mSharedLibraries,对需要的library做代码优化

6)  调用scanDirLI扫描指定目录下的所有apk文件,主要是系统app目录/system/app和用户app目录/data/app

7)  调用updateAllSharedLibrariesLPw更新所有package的usesLibraryFiles数据,为什么要做更新?因为package在manifest里头使用use-library描述要引用的library,在系统mSharedLibraries里可能是未定义的,所以,需要做数据同步

8)  调用updatePermissionsLPw更新系统permission trees和permission 列表

 

2.2 初始化SystemConfig

PMS在构造时,需要初始化SystemConfig:

   SystemConfig systemConfig = SystemConfig.getInstance();

接着看构造函数:

    SystemConfig() {

        // Read configuration from system

        readPermissions(Environment.buildPath(

                Environment.getRootDirectory(), “etc”, “sysconfig”), false);

        // Read configuration from the old permissions dir

        readPermissions(Environment.buildPath(

                Environment.getRootDirectory(), “etc”, “permissions”), false);

    }

 

代码主要调用readPermissions遍历/etc/sysconfig和/etc/permissions两个目录下是所有.xml文件,读取系统预置的配置信息,主要有:

1)  permission 权限以及对应的group id

2)  share libraries 系统预置了哪些共享库可供app加载使用

3)  available features 系统支持哪些feature,比如是否支持前置摄像头等

 

还有,readPermissions第二个参数,如果为false,那么.xml文件内permission对应的gid会被添加到系统默认global gids并分配给启动的app,也就是说,app无须声明这个permission,默认就会在这个Supplementary GID对应分组中。

 

xml文件内对应的配置数据:

  <permission name=”android.permission.READ_EXTERNAL_STORAGE” >

     <group gid=”sdcard_r” />

 </permission>

 

<library name=”android.test.runner”

         file=”/system/framework/android.test.runner.jar” />

 

<feature name=”android.hardware.camera.front” />

 

2.3 APP权限管理

Linux权限管理是基于UID和GID,可以对目标文件设置用户以及分组访问权限(Read,Write,Execute),Linux进程只有属于对应的用户或分组才能有权限访问该文件。

Android延续了Linux的权限管理方式,每个APK在经过PMS安装成功后,都会分配一个主UID和主GID,还有Supplementary GIDS则是根据system config的global gids + app所有 grant permission对应的gid组合而成。

除了linux的权限管理方式之外,Android还添加了permission机制来对android系统服务做访问控制,app如果要使用某项系统功能,必须要在manifest里声明该功能对应的permission并且该permission被准许后,app才能使用该系统功能。

 

2.3.1 生成UID和GID

APP所属的GID默认跟UID一致,Android UID的管理方式主要有两种:

1) Share User Id,也就是说,多个签名相同的app共享同一个user id,这样多个app就具有相同的权限,可以相互访问资源

2) Normal User Id,每个app被会分配一个独立的user id,这说明每个app都运行在独立的user下,无法直接共享进程内部数据,从而保护app内部数据的安全

  

   单纯从user id分配来说,又可以分为系统预定义和动态生成,系统预定义的user id主要有Process.SYSTEM_UID(1000, 对应系统权限)和RADIO_UID(1001,对应通信相关应用权限)等等;动态生成的user id,在10000-19999之间依次分配,相关代码如下:

    // Returns -1 if we could not find an available UserId to assign

    private int newUserIdLPw(Object obj) {

        // Let’s be stupidly inefficient for now…

        final int N = mUserIds.size();

        for (int i = mFirstAvailableUid; i < N; i++) {

            if (mUserIds.get(i) == null) {

                mUserIds.set(i, obj);

                return Process.FIRST_APPLICATION_UID + i;

            }

        }

 

        // None left?

        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {

            return -1;

        }

 

        mUserIds.add(obj);

        return Process.FIRST_APPLICATION_UID + N;

    }

 

Process.FIRST_APPLICATION_UID值就是10000,mUserIds列表保存所有已经申请的package,package对应的user id就是其在mUserIds的索引加上10000;

这个函数先遍历mUserIds,看是否存在已经释放的user id,如果有,则将其重新分配给新申请的package,否则就将申请的packahe追加到在mUserIds。

 

接下去说说ShareUser Id,它对应的数据结构为:

    /**

 * Settings data for a particular shared user ID we know about.

 */

final class SharedUserSetting extends GrantedPermissions {

    final String name;

    int userId;

    // flags that are associated with this uid, regardless of any package flags

    int uidFlags;

    final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();

final PackageSignatures signatures = new PackageSignatures();

 

/**……..*/

}

成员函数相关代码这里就不拷贝了,大家可以直接去看源码

从成员变量的定义可以看出,SharedUserSetting主要保存:

1)  user id

2)  packages 当前share user id的所有package信息

3)  signatures share user id的app对应的签名数据

 

PMS将所有的SharedUserSetting数据保存到Settings. mSharedUsers,然后通过调用Settings. addSharedUserLPw添加新的SharedUserSetting:

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {

        SharedUserSetting s = mSharedUsers.get(name);

        if (s != null) {

            if (s.userId == uid) {

                return s;

            }

            PackageManagerService.reportSettingsProblem(Log.ERROR,

                    “Adding duplicate shared user, keeping first: ” + name);

            return null;

        }

        s = new SharedUserSetting(name, pkgFlags);

        s.userId = uid;

        if (addUserIdLPw(uid, s, name)) {

            mSharedUsers.put(name, s);

            return s;

        }

        return null;

    }

 

上面介绍PMS初始化流程时介绍过,PMS初始化时会添加系统预定义的ShareUserSetting:

  mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

        mSettings.addSharedUserLPw(“android.uid.phone”, RADIO_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

        mSettings.addSharedUserLPw(“android.uid.log”, LOG_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

        mSettings.addSharedUserLPw(“android.uid.nfc”, NFC_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

        mSettings.addSharedUserLPw(“android.uid.bluetooth”, BLUETOOTH_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

        mSettings.addSharedUserLPw(“android.uid.shell”, SHELL_UID,

                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);

有一点要说明下,这里通过addSharedUserLPw添加ShareUserSetting,只设置了name,user id,flags;PackageSetting在scan package调用mSettings.getPackageLPw获取package settings时会被更新设置;

signatues数据,则会在scan package结束调用mSettings.insertPackageSettingLPw(pkgSetting, pkg)时被更新。

 

2.3.2 Supplementary GIDS

主UID和GID决定了app的访问权限,上面也提过,android还支持一套基于android framework的permission机制,这套机制确保app在调用某项android系统服务接口的时候,必须在manifest声明对应permission,然后permission被准许后app方可访问;

这里还有个问题,假如有些permission对应的系统服务接口,需要访问linux底层设备,那app对底层设备的访问权限如何获取?所以permission在被配置时,必须指定对应设备的访问权限信息,也就是SupplementaryGid,这样app启动时才会在对应的补充分组,从而拥有对linux设备的访问权。

比如app要访问外部存储,必须在manifest声明:

android.permission.READ_EXTERNAL_STORAGE

其对应的group id配置信息如下:

  <permission name=”android.permission.READ_EXTERNAL_STORAGE” >

     <group gid=”sdcard_r” />

  </permission>

 

  系统预置的permission配置如何数据获取,可查看 2.2 章节

2.3.3 权限数据管理

2.3.3.1权限定义

PMS会将系统支持的所有permission数据保存到Settings. mPermissions MAP中,对应的数据类型如下:

// Mapping from permission names to info about them.

final ArrayMap<String, BasePermission> mPermissions =

                           new ArrayMap<String, BasePermission>();

 

Key是permission名字,value是对应的permission数据,数据类型为BasePermission,先看下这个类核心字段的定义:

 final class BasePermission {

    final static int TYPE_NORMAL = 0;

    final static int TYPE_BUILTIN = 1;

    final static int TYPE_DYNAMIC = 2;

//权限名

final String name;

//定义权限的包名,如果type为TYPE_BUILTIN,则为android

String sourcePackage;

//定义权限的包数据

    PackageSettingBase packageSetting;

    //权限类型

final int type;

//权限保护等级

int protectionLevel;

//权限数据

    PackageParser.Permission perm;

PermissionInfo pendingInfo;

//权限对应的user id,这个应该是当等级为signature或者permission 类型为tree时有效

int uid;

//权限对应的Supplementary Gids

int[] gids;

}

 

先看type和protectionLevel两个字段

type表示权限定义的类型,主要有三个:

1)  TYPE_NORMAL,指的是正常app在manifest使用permission字段定义的权限

2)  TYPE_BUILTIN,系统内置权限,PMS初始化时从SystemConfig读取

3)  TYPE_DYNAMIC,动态添加的权限,一个app能动态添加权限的前提是,需要在manifest里定义permission tree,也就是权限树的根结点域名,然后app才能使用addPermission来动态添加权限。

 

protectionLevel表示权限的保护等级,默认分四级:

1)  PROTECTION_NORMAL

普通权限,低风险

2)  PROTECTION_DANGEROUS
有风险的权限

3)  PROTECTION_SIGNATURE
申请使用权限的app签名必须跟权限所属app签名一致

4)  PROTECTION_SIGNATURE_OR_SYSTEM

申请使用的必须是系统或者签名一致app

 

接着看perm字段,对应的类为PackageParser.Permission,这个类对应的是manifest里头的permission和permission tree,这两个权限的区别,可以看上头type类型的介绍

PackageParser.Permission类定义:

  public final static class Permission extends Component<IntentInfo> {

        public final PermissionInfo info;

        public boolean tree;

        public PermissionGroup group;

}

tree字段用于判断是否是permission tree,group包含权限分组相关描述数据,info则是权限的描述数据

PermissionInfo类定义:

 public class PermissionInfo extends PackageItemInfo implements Parcelable {

   …..

}

  主要包含permission name和package name等.

接着看Settings.mPermissions这个系统权限Map数据是怎么生成的

《Android PackageManager Service详解(5.1源码)(三)》

在PMS构造时,会调用SystemConfig.getPermissions()获取系统builtin权限数据

  // Propagate permission configuration in to package manager.

            ArrayMap<String, SystemConfig.PermissionEntry> permConfig

                    = systemConfig.getPermissions();

            for (int i=0; i<permConfig.size(); i++) {

                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);

                BasePermission bp = mSettings.mPermissions.get(perm.name);

                if (bp == null) {

                    bp = new BasePermission(perm.name, “android”, BasePermission.TYPE_BUILTIN);

                    mSettings.mPermissions.put(perm.name, bp);

                }

                if (perm.gids != null) {

                    bp.gids = appendInts(bp.gids, perm.gids);

                }

            }

遍历systemConfig定义的permission时,接着判断mSettings.mPermissions是否已经存在该权限的定义,如果不存在,新建一个BasePermission,权限source package为android,type为builtin,接着更新权限对应的supplementary gids;

接着看扫描apk时权限数据的初始化:

PackageParser.parsePermission

  private Permission parsePermission(Package owner, Resources res,

            XmlPullParser parser, AttributeSet attrs, String[] outError)

        throws XmlPullParserException, IOException {

        Permission perm = new Permission(owner);

 

        TypedArray sa = res.obtainAttributes(attrs,

                com.android.internal.R.styleable.AndroidManifestPermission);

 

        if (!parsePackageItemInfo(owner, perm.info, outError,

                “<permission>”, sa,

                com.android.internal.R.styleable.AndroidManifestPermission_name,

                com.android.internal.R.styleable.AndroidManifestPermission_label,

                com.android.internal.R.styleable.AndroidManifestPermission_icon,

                com.android.internal.R.styleable.AndroidManifestPermission_logo,

                com.android.internal.R.styleable.AndroidManifestPermission_banner)) {

            sa.recycle();

            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

            return null;

        }

 

     /……/

 

        owner.permissions.add(perm);

        return perm;

    }

 

PackageParser.parsePermissionTree:

private Permission parsePermissionTree(Package owner, Resources res,

            XmlPullParser parser, AttributeSet attrs, String[] outError)

        throws XmlPullParserException, IOException {

        Permission perm = new Permission(owner);

 

        TypedArray sa = res.obtainAttributes(attrs,

                com.android.internal.R.styleable.AndroidManifestPermissionTree);

 

        if (!parsePackageItemInfo(owner, perm.info, outError,

                “<permission-tree>”, sa,

                com.android.internal.R.styleable.AndroidManifestPermissionTree_name,

                com.android.internal.R.styleable.AndroidManifestPermissionTree_label,

                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,

                com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,

                com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {

            sa.recycle();

            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

            return null;

        }

 

        sa.recycle();

       

        int index = perm.info.name.indexOf(‘.’);

        if (index > 0) {

            index = perm.info.name.indexOf(‘.’, index+1);

        }

        if (index < 0) {

            outError[0] = “<permission-tree> name has less than three segments: “

                + perm.info.name;

            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

            return null;

        }

 

        perm.info.descriptionRes = 0;

        perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;

        perm.tree = true;

 

        owner.permissions.add(perm);

 

        return perm;

    }

可以看出,permission和permission tree都保存到Package.permissions列表中,通过字段perm.tree是true还是false来区分是否是permissiontree

在app扫描结束后,permission数据已经被全部拿到,但是现在数据都还只是保存在Package内部,所以还需要将permission数据添加到PMS permission Map,代码在

PMS.scanPackageDirtyLI,这个函数太长了,所以这里只截取对permission列表处理相关代码段

   N = pkg.permissions.size();

            r = null;

            for (i=0; i<N; i++) {

                PackageParser.Permission p = pkg.permissions.get(i);

                ArrayMap<String, BasePermission> permissionMap =

                        p.tree ? mSettings.mPermissionTrees

                        : mSettings.mPermissions;

                p.group = mPermissionGroups.get(p.info.group);

                if (p.info.group == null || p.group != null) {

                    BasePermission bp = permissionMap.get(p.info.name);

 

                    // Allow system apps to redefine non-system permissions

                    if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {

                        final boolean currentOwnerIsSystem = (bp.perm != null

                                && isSystemApp(bp.perm.owner));

                        if (isSystemApp(p.owner)) {

                            if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {

                                // It’s a built-in permission and no owner, take ownership now

                                bp.packageSetting = pkgSetting;

                                bp.perm = p;

                                bp.uid = pkg.applicationInfo.uid;

                                bp.sourcePackage = p.info.packageName;

                            } else if (!currentOwnerIsSystem) {

                                String msg = “New decl ” + p.owner + ” of permission  “

                                        + p.info.name + ” is system; overriding ” + bp.sourcePackage;

                                reportSettingsProblem(Log.WARN, msg);

                                bp = null;

                            }

                        }

                    }

 

                    if (bp == null) {

                        bp = new BasePermission(p.info.name, p.info.packageName,

                                BasePermission.TYPE_NORMAL);

                        permissionMap.put(p.info.name, bp);

                    }

 

                    if (bp.perm == null) {

                        if (bp.sourcePackage == null

                                || bp.sourcePackage.equals(p.info.packageName)) {

                            BasePermission tree = findPermissionTreeLP(p.info.name);

                            if (tree == null

                                    || tree.sourcePackage.equals(p.info.packageName)) {

                                bp.packageSetting = pkgSetting;

                                bp.perm = p;

                                bp.uid = pkg.applicationInfo.uid;

                                bp.sourcePackage = p.info.packageName;

                                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {

                                    if (r == null) {

                                        r = new StringBuilder(256);

                                    } else {

                                        r.append(‘ ‘);

                                    }

                                    r.append(p.info.name);

                                }

                            } else {

                                Slog.w(TAG, “Permission ” + p.info.name + ” from package “

                                        + p.info.packageName + ” ignored: base tree “

                                        + tree.name + ” is from package “

                                        + tree.sourcePackage);

                            }

                        } else {

                            Slog.w(TAG, “Permission ” + p.info.name + ” from package “

                                    + p.info.packageName + ” ignored: original from “

                                    + bp.sourcePackage);

                        }

                    } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {

                        if (r == null) {

                            r = new StringBuilder(256);

                        } else {

                            r.append(‘ ‘);

                        }

                        r.append(“DUP:”);

                        r.append(p.info.name);

                    }

                    if (bp.perm == p) {

                        bp.protectionLevel = p.info.protectionLevel;

                    }

                } else {

                    Slog.w(TAG, “Permission ” + p.info.name + ” from package “

                            + p.info.packageName + ” ignored: no group “

                            + p.group);

                }

           

 

从代码可以看出,虽然permission和permission true在package内部是保存到一个list的,但是到了Settings,它们被分开了,分别对应mSettings.mPermissions和mSettings.mPermissionTrees,代码逻辑也比较简单,一开始先判断分组是否配置准确:

p.info.group == null || p.group != null

要么不设置权限分组,如果设置了,那就必须在mPermissionGroups能拿到对应数据,这两者都OK,接着根据权限名称从permissionMap拿到对应BasePermission对象,对第一次扫描apk来说,bp肯定为null,接着创建一个normal的BasePermission并添加到permissionMap,然后判断权限数据对象perm 是否为null,如果为null,才可对其进行更新,更新之前,还需要做两次判断:

1)  sourcePackage未定义或者包名相同

2)  判断是否存在匹配的已定义permission tree根节点域名,如果不存在就没问题,反之如果存在,则必须保证permissiontree所属package跟permission是一致的,这也说明,针对app定义在manifest里的permission,可不定义permission tree根节点域名

上面两个条件都满足后,就可以更新bp. packageSetting和bp. sourcePackage等数据了,最后更新bp.protectionLevel权限等级.

在所有apk数据扫描结束后, 最后调用PMS.updatePermissionsLPw刷新PMS permission MAP:

private void updatePermissionsLPw(String changingPkg,

            PackageParser.Package pkgInfo, int flags) {

        // Make sure there are no dangling permission trees.

        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();

        while (it.hasNext()) {

            final BasePermission bp = it.next();

            if (bp.packageSetting == null) {

                // We may not yet have parsed the package, so just see if

                // we still know about its settings.

                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);

            }

            if (bp.packageSetting == null) {

                Slog.w(TAG, “Removing dangling permission tree: ” + bp.name

                        + ” from package ” + bp.sourcePackage);

                it.remove();

            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {

                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {

                    Slog.i(TAG, “Removing old permission tree: ” + bp.name

                            + ” from package ” + bp.sourcePackage);

                    flags |= UPDATE_PERMISSIONS_ALL;

                    it.remove();

                }

            }

        }

 

        // Make sure all dynamic permissions have been assigned to a package,

        // and make sure there are no dangling permissions.

        it = mSettings.mPermissions.values().iterator();

        while (it.hasNext()) {

            final BasePermission bp = it.next();

            if (bp.type == BasePermission.TYPE_DYNAMIC) {

                if (DEBUG_SETTINGS) Log.v(TAG, “Dynamic permission: name=”

                        + bp.name + ” pkg=” + bp.sourcePackage

                        + ” info=” + bp.pendingInfo);

                if (bp.packageSetting == null && bp.pendingInfo != null) {

                    final BasePermission tree = findPermissionTreeLP(bp.name);

                    if (tree != null && tree.perm != null) {

                        bp.packageSetting = tree.packageSetting;

                        bp.perm = new PackageParser.Permission(tree.perm.owner,

                                new PermissionInfo(bp.pendingInfo));

                        bp.perm.info.packageName = tree.perm.info.packageName;

                        bp.perm.info.name = bp.name;

                        bp.uid = tree.uid;

                    }

                }

            }

            if (bp.packageSetting == null) {

                // We may not yet have parsed the package, so just see if

                // we still know about its settings.

                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);

            }

            if (bp.packageSetting == null) {

                Slog.w(TAG, “Removing dangling permission: ” + bp.name

                        + ” from package ” + bp.sourcePackage);

                it.remove();

            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {

                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {

                    Slog.i(TAG, “Removing old permission: ” + bp.name

                            + ” from package ” + bp.sourcePackage);

                    flags |= UPDATE_PERMISSIONS_ALL;

                    it.remove();

                }

            }

        }

 

        // Now update the permissions for all packages, in particular

        // replace the granted permissions of the system packages.

        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);

        }

    }

刷新的目的有两个,一个是依次看看mSettings.mPermissionTrees和mSettings.mPermissions是否存在未设置owner package的,如果有,再根据sourcePackage name尝试从mPackages列表中获取对应的package,如果能拿到,更新之,否则说明这个BasePermission数据已经失效,将其删除.

最后尝试刷新所有app的grantpermission数据.

2.3.3.2权限使用

对大部分APP来说,如果要使用某权限,必须要在manifest进行声明, 比如:

<uses-permissionandroid:name=”android.permission.INTERNET” />

然后PackageParser. parseBaseApk时,会调用parseUsesPermission对声明解析使用的权限数据:

private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,

                                        AttributeSet attrs, String[] outError)

            throws XmlPullParserException, IOException {

        TypedArray sa = res.obtainAttributes(attrs,

                com.android.internal.R.styleable.AndroidManifestUsesPermission);

 

        // Note: don’t allow this value to be a reference to a resource

        // that may change.

        String name = sa.getNonResourceString(

                com.android.internal.R.styleable.AndroidManifestUsesPermission_name);

/*

        boolean required = sa.getBoolean(

                com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);

*/

        boolean required = true; // Optional <uses-permission> not supported

 

        int maxSdkVersion = 0;

        TypedValue val = sa.peekValue(

                com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion);

        if (val != null) {

            if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {

                maxSdkVersion = val.data;

            }

        }

 

        sa.recycle();

 

        if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) {

            if (name != null) {

                int index = pkg.requestedPermissions.indexOf(name);

                if (index == -1) {

                    pkg.requestedPermissions.add(name.intern());

                    pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);

                } else {

                    if (pkg.requestedPermissionsRequired.get(index) != required) {

                        outError[0] = “conflicting <uses-permission> entries”;

                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

                        return false;

                    }

                }

            }

        }

 

        XmlUtils.skipCurrentTag(parser);

        return true;

    }

app申请的所有权限都保存于pkg.requestedPermissions,请求使用的权限则会被保存到pkg.requestedPermissionsRequired中,required默认为true,说明所有申请的权限在默认情况下,都会被添加到请求使用列表。

权限请求了,接下去还去要对其进行准许(grant)操作,针对准许过后的权限,PMS定义了一个类GrantPermissions用于保存这些数据:

  class GrantedPermissions {

    int pkgFlags;

    ArraySet<String> grantedPermissions = new ArraySet<String>();

    int[] gids;

}

grantedPermissions保存app所有准许过的权限,gids之前说过,它保存有app使用准许权限所需要的Supplementary Gids。

PMS根据app user id的不同,准许过的权限保存位置也不同:

1)  normal user id, 对应PackageSetting

2)  share user id,对应PackageSetting. sharedUser

 

在扫描apk结束后,会调用:

void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags)

来更新系统权限数据,第二个参数pkgInfo用于指定要re-grant的包,第三个参数则是用于指定是否要re-grant所有app包。对于PMS初始化结束后,由于扫描了所有的包,所以需要re-grant所有的app包数据:

updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL

                    | (regrantPermissions

                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)

                            : 0));

接着看函数代码:

private void updatePermissionsLPw(String changingPkg,

            PackageParser.Package pkgInfo, int flags) {

        // Make sure there are no dangling permission trees.

        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();

        while (it.hasNext()) {

            final BasePermission bp = it.next();

            if (bp.packageSetting == null) {

                // We may not yet have parsed the package, so just see if

                // we still know about its settings.

                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);

            }

            if (bp.packageSetting == null) {

                Slog.w(TAG, “Removing dangling permission tree: ” + bp.name

                        + ” from package ” + bp.sourcePackage);

                it.remove();

            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {

                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {

                    Slog.i(TAG, “Removing old permission tree: ” + bp.name

                            + ” from package ” + bp.sourcePackage);

                    flags |= UPDATE_PERMISSIONS_ALL;

                    it.remove();

                }

            }

        }

 

        // Make sure all dynamic permissions have been assigned to a package,

        // and make sure there are no dangling permissions.

        it = mSettings.mPermissions.values().iterator();

        while (it.hasNext()) {

            final BasePermission bp = it.next();

            if (bp.type == BasePermission.TYPE_DYNAMIC) {

                if (DEBUG_SETTINGS) Log.v(TAG, “Dynamic permission: name=”

                        + bp.name + ” pkg=” + bp.sourcePackage

                        + ” info=” + bp.pendingInfo);

                if (bp.packageSetting == null && bp.pendingInfo != null) {

                    final BasePermission tree = findPermissionTreeLP(bp.name);

                    if (tree != null && tree.perm != null) {

                        bp.packageSetting = tree.packageSetting;

                        bp.perm = new PackageParser.Permission(tree.perm.owner,

                                new PermissionInfo(bp.pendingInfo));

                        bp.perm.info.packageName = tree.perm.info.packageName;

                        bp.perm.info.name = bp.name;

                        bp.uid = tree.uid;

                    }

                }

            }

            if (bp.packageSetting == null) {

                // We may not yet have parsed the package, so just see if

                // we still know about its settings.

                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);

            }

            if (bp.packageSetting == null) {

                Slog.w(TAG, “Removing dangling permission: ” + bp.name

                        + ” from package ” + bp.sourcePackage);

                it.remove();

            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {

                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {

                    Slog.i(TAG, “Removing old permission: ” + bp.name

                            + ” from package ” + bp.sourcePackage);

                    flags |= UPDATE_PERMISSIONS_ALL;

                    it.remove();

                }

            }

        }

 

        // Now update the permissions for all packages, in particular

        // replace the granted permissions of the system packages.

        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);

        }

    }

这个函数主要做如下事情:

1)  同步mPermissionTrees和mPermissions数据,清除没有owner package的权限

2)  如果flags有设置UPDATE_PERMISSIONS_ALL,则依次对所有的package调用grantPermissionsLPw

3)  如果pkgInfo不为空,则需要对这个指定的package调用grantPermissionsLPw

 

grantPermissionsLPw就是针对package来进行具体的权限准许操作了:

  private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,

            String packageOfInterest) {

        final PackageSetting ps = (PackageSetting) pkg.mExtras;

        if (ps == null) {

            return;

        }

        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;

        ArraySet<String> origPermissions = gp.grantedPermissions;

        boolean changedPermission = false;

 

        if (replace) {

            ps.permissionsFixed = false;

            if (gp == ps) {

                origPermissions = new ArraySet<String>(gp.grantedPermissions);

                gp.grantedPermissions.clear();

                gp.gids = mGlobalGids;

            }

        }

 

        if (gp.gids == null) {

            gp.gids = mGlobalGids;

        }

 

        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 (DEBUG_INSTALL) {

                if (gp != ps) {

                    Log.i(TAG, “Package ” + pkg.packageName + ” checking ” + name + “: ” + bp);

                }

            }

 

            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;

            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {

                // Keep track of app op permissions.

                ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);

                if (pkgs == null) {

                    pkgs = new ArraySet<>();

                    mAppOpPermissionPackages.put(bp.name, pkgs);

                }

                pkgs.add(pkg.packageName);

            }

            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;

            if (level == PermissionInfo.PROTECTION_NORMAL

                    || level == PermissionInfo.PROTECTION_DANGEROUS) {

                // We grant a normal or dangerous permission if any of the following

                // are true:

                // 1) The permission is required

                // 2) The permission is optional, but was granted in the past

                // 3) The permission is optional, but was requested by an

                //    app in /system (not /data)

                //

                // Otherwise, reject the permission.

                allowed = (required || origPermissions.contains(perm)

                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));

            } else if (bp.packageSetting == null) {

                // This permission is invalid; skip it.

                allowed = false;

            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {

                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);

                if (allowed) {

                    allowedSig = true;

                }

            } else {

                allowed = false;

            }

            if (DEBUG_INSTALL) {

                if (gp != ps) {

                    Log.i(TAG, “Package ” + pkg.packageName + ” granting ” + perm);

                }

            }

            if (allowed) {

                if (!isSystemApp(ps) && ps.permissionsFixed) {

                    // If this is an existing, non-system package, then

                    // we can’t add any new permissions to it.

                    if (!allowedSig && !gp.grantedPermissions.contains(perm)) {

                        // Except…  if this is a permission that was added

                        // to the platform (note: need to only do this when

                        // updating the platform).

                        allowed = isNewPlatformPermissionForPackage(perm, pkg);

                    }

                }

                if (allowed) {

                    if (!gp.grantedPermissions.contains(perm)) {

                        changedPermission = true;

                        gp.grantedPermissions.add(perm);

                        gp.gids = appendInts(gp.gids, bp.gids);

                    } else if (!ps.haveGids) {

                        gp.gids = appendInts(gp.gids, bp.gids);

                    }

                } else {

                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {

                        Slog.w(TAG, “Not granting permission ” + perm

                                + ” to package ” + pkg.packageName

                                + ” because it was previously installed without”);

                    }

                }

            } else {

                if (gp.grantedPermissions.remove(perm)) {

                    changedPermission = true;

                    gp.gids = removeInts(gp.gids, bp.gids);

                    Slog.i(TAG, “Un-granting permission ” + perm

                            + ” from package ” + pkg.packageName

                            + ” (protectionLevel=” + bp.protectionLevel

                            + ” flags=0x” + Integer.toHexString(pkg.applicationInfo.flags)

                            + “)”);

                } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {

                    // Don’t print warning for app op permissions, since it is fine for them

                    // not to be granted, there is a UI for the user to decide.

                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {

                        Slog.w(TAG, “Not granting permission ” + perm

                                + ” to package ” + pkg.packageName

                                + ” (protectionLevel=” + bp.protectionLevel

                                + ” flags=0x” + Integer.toHexString(pkg.applicationInfo.flags)

                                + “)”);

                    }

                }

            }

        }

 

        if ((changedPermission || replace) && !ps.permissionsFixed &&

                !isSystemApp(ps) || isUpdatedSystemApp(ps)){

            // This is the first that we have heard about this package, so the

            // permissions we have now selected are fixed until explicitly

            // changed.

            ps.permissionsFixed = true;

        }

        ps.haveGids = true;

    }

先拿到package对应的PackageSetting对象实例,然后根据是否是share user id来拿到对应的GrantPermission对象,接着遍历package 申请的所有权限,如果权限保护等级是normal或dangerous的,只要权限被请求准许或者app是系统应用,那么准许通过;对于保护等级是signature的,则需要调用grantSignaturePermission来核对签名是否一致,如果一致,准许通过,最后调用gp.grantedPermissions.add(perm);保存准许通过的权限,同时调用

gp.gids= appendInts(gp.gids, bp.gids);保存BasePermission对应的supplementary gids.

至此,app对应的静态权限数据已经全部生成。

2.3.3.3动态权限

上面说过,权限分三种,BUILTIN(系统预置),NORMAL(manifest定义)和DYNAMIC(动态添加),如果一个app要动态添加和准许权限,需要具备:

1)  在manifest定义permission tree

2)  具有”android.permission.GRANT_REVOKE_PERMISSIONS”权限,也就是准许和撤销权限的权限。

这个权限定义在framework-res.apk对应的manifest中:

<permission android:name=”android.permission.GRANT_REVOKE_PERMISSIONS”

        android:label=”@string/permlab_grantRevokePermissions”

        android:description=”@string/permdesc_grantRevokePermissions”

        android:protectionLevel=”signature” />

其保护等级是signature的,也就是说,用这个权限的app,必须要有系统权限,对于普通app来说,你可以定义permission tree并且动态添加permission,但是你没有权限去准许和撤销这个权限,添加和准许权限对应的函数为PMS.addPermission和PMS.grantPermission。

先看addPermission,其最终将会调用addPermissionLocked:

boolean addPermissionLocked(PermissionInfo info, boolean async) {

        if (info.labelRes == 0 && info.nonLocalizedLabel == null) {

            throw new SecurityException(“Label must be specified in permission”);

        }

        BasePermission tree = checkPermissionTreeLP(info.name);

        BasePermission bp = mSettings.mPermissions.get(info.name);

        boolean added = bp == null;

        boolean changed = true;

        int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);

        if (added) {

            enforcePermissionCapLocked(info, tree);

            bp = new BasePermission(info.name, tree.sourcePackage,

                    BasePermission.TYPE_DYNAMIC);

        } else if (bp.type != BasePermission.TYPE_DYNAMIC) {

            throw new SecurityException(

                    “Not allowed to modify non-dynamic permission “

                    + info.name);

        } else {

            if (bp.protectionLevel == fixedLevel

                    && bp.perm.owner.equals(tree.perm.owner)

                    && bp.uid == tree.uid

                    && comparePermissionInfos(bp.perm.info, info)) {

                changed = false;

            }

        }

        bp.protectionLevel = fixedLevel;

        info = new PermissionInfo(info);

        info.protectionLevel = fixedLevel;

        bp.perm = new PackageParser.Permission(tree.perm.owner, info);

        bp.perm.info.packageName = tree.perm.info.packageName;

        bp.uid = tree.uid;

        if (added) {

            mSettings.mPermissions.put(info.name, bp);

        }

        if (changed) {

            if (!async) {

                mSettings.writeLPr();

            } else {

                scheduleWriteSettingsLocked();

            }

        }

        return added;

    }

这个函数首先调用checkPermissionTreeLP并传入权限名来判断调用app是否声明了对应的权限树根节点:

      private BasePermission checkPermissionTreeLP(String permName) {

        if (permName != null) {

            BasePermission bp = findPermissionTreeLP(permName);

            if (bp != null) {

                if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {

                    return bp;

                }

                throw new SecurityException(“Calling uid “

                        + Binder.getCallingUid()

                        + ” is not allowed to add to permission tree “

                        + bp.name + ” owned by uid ” + bp.uid);

            }

        }

        throw new SecurityException(“No permission tree found for ” + permName);

    }

先通过findPermissionTreeLP从mSettings.mPermissionTrees找到对应的权限树描述对象,如果没找到,抛出异常,如果找到了,接着通过UID来判断调用应用和权限树定义app是否一致,如果不一致,抛出异常。

   checkPermissionTreeLP通过后,接着看对应的权限是否已经定义,如果未定义,就继续创建DYNAMIC类型的权限并添加到权限列表,如果已经定义了,接着判断已经存在的权限类型是否为DYNAMIC,如果不是,抛出异常,如果是,则更新权限数据。

 

接着看grantPermission:

   @Override

    public void grantPermission(String packageName, String permissionName) {

        mContext.enforceCallingOrSelfPermission(

                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);

        synchronized (mPackages) {

            final PackageParser.Package pkg = mPackages.get(packageName);

            if (pkg == null) {

                throw new IllegalArgumentException(“Unknown package: ” + packageName);

            }

            final BasePermission bp = mSettings.mPermissions.get(permissionName);

            if (bp == null) {

                throw new IllegalArgumentException(“Unknown permission: ” + permissionName);

            }

 

            checkGrantRevokePermissions(pkg, bp);

 

            final PackageSetting ps = (PackageSetting) pkg.mExtras;

            if (ps == null) {

                return;

            }

            final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;

            if (gp.grantedPermissions.add(permissionName)) {

                if (ps.haveGids) {

                    gp.gids = appendInts(gp.gids, bp.gids);

                }

                mSettings.writeLPr();

            }

        }

    }

从代码可以看出,要想成功grant,必须满足:

1)  packageName对应的package要存在

2)  permissionName对应的权限要已经添加到系统权限列表

3)  packageName对应的package要声明request这个权限

4)  只有权限保护等级为normal或dangerous,并且在对应的package内还未被required的权限允许grant,这个基本也就DYNAMIC permission能满足了

以上四点必须全部满足,否则就会抛出异常

接着将permission name添加到grantedPermissions,然后append对应的supplementary GID。

2.3.3.4权限查询

App在调用某些系统函数的时候,函数的开头会检查app是否拥有对应的权限,比如:

mContext.enforceCallingOrSelfPermission(

                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);

这个调用绕一大圈,最终还是通过调用PMS.checkPermission来判断调用app是否拥有对应的权限

@Override

    public int checkPermission(String permName, String pkgName) {

        synchronized (mPackages) {

            PackageParser.Package p = mPackages.get(pkgName);

            if (p != null && p.mExtras != null) {

                PackageSetting ps = (PackageSetting)p.mExtras;

                if (ps.sharedUser != null) {

                    if (ps.sharedUser.grantedPermissions.contains(permName)) {

                        return PackageManager.PERMISSION_GRANTED;

                    }

                } else if (ps.grantedPermissions.contains(permName)) {

                    return PackageManager.PERMISSION_GRANTED;

                }

            }

        }

        return PackageManager.PERMISSION_DENIED;

    }

代码很简单,先查找pkgName对应的Package,接着拿到PackageSetting对象,然后从grantedPermissions中查找是否包含已经对应的permission来确认该权限是否被grant。

    原文作者:拿节电池
    原文地址: https://blog.csdn.net/zhejiang9/article/details/52412387
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞