Android-6.0之PMS解析上篇

本文转载于:http://www.iloveandroid.net/2016/06/20/Android_PackageManagerService-2/


前面介绍了pm命令如何使用以及PMS运行时的一些规则和行为,现在就可以尽情享受PMS的代码了。

PMS的入口点

PMS是由SystemServer启动的。

1
Android6.0/frameworks/base/services/java/com/android/server/SystemServer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void startBootstrapServices(){

 // Wait for installd to finish starting up so that it has a chance to
        // create critical directories such as /data/user with the appropriate
        // permissions.  We need this to complete before we initialize other services.

Installer installer = mSystemServiceManager.startService(Installer.class);
......................................

 // Start the package manager.
        Slog.i(TAG, "Package Manager");
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
......................................
}

这样就找到了分析PMS的入口点:

1
Android6.0/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java的main方法

main函数中的第二个参数installer负责和native层中的installd守护进程进行socket通信,这里在启动Installer 的时候会有installd创建一些关键目录,后续会详细介绍installd守护进程。

第四个参数mOnlyCore用于判断是否仅仅扫描系统的目录,只有在与data分区加解密时才会设置为true,其他情况一般都为false。

main函数也很简单如下所示:

1
2
3
4
5
6
7
public static PackageManagerService main(Context context, Installer installer,
          boolean factoryTest, boolean onlyCore) {
      PackageManagerService m = new PackageManagerService(context, installer,
              factoryTest, onlyCore);
      ServiceManager.addService("package", m);
      return m;
  }

这里主要做了两件事:

  1. 创建PackageManagerService对象,也就是PMS的实体

  2. 将PMS向SMS中注册,即加入SMS中,方便后续其他进程或者app通过SMS获得PMS服务

PMS的构造函数中,做了大量的工作,总结起来就是扫描Android系统中的apk,并且建立相应的数据结构去管理Package的信息,四大组件的信息,权限信息等内容。

PMS构造函数分析之Settings

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
        ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

首先判断build类型,build有三种:user,userdebug,eng。当为eng时,意味着不需要进行odex优化。

接着创建一个DisplayMetrics对象,用于保存屏幕像素参数。

然后就是本次分析的重点Settings了.

创建一个Settings对象。

源码路径:

1
Android6.0/frameworks/base/services/core/java/com/android/server/pm/Settings.java

其构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Settings(Object lock) {
    this(Environment.getDataDirectory(), lock);
}

Settings(File dataDir, Object lock) {
    mLock = lock;

    mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);

    mSystemDir = new File(dataDir, "system");
    mSystemDir.mkdirs();
    FileUtils.setPermissions(mSystemDir.toString(),
            FileUtils.S_IRWXU|FileUtils.S_IRWXG
            |FileUtils.S_IROTH|FileUtils.S_IXOTH,
            -1, -1);
    mSettingsFilename = new File(mSystemDir, "packages.xml");
    mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
    mPackageListFilename = new File(mSystemDir, "packages.list");
    FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);

    // Deprecated: Needed for migration
    mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
    mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}

dataDir为

1
/data

所以mSystemDir为

1
/data/system

该构造函数中主要创建了前面文章中介绍的关于PMS的若干配置文件的File对象,并且设置了权限等。

mSettings.addSharedUserLPw

addSharedUserLPw方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        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, pkgPrivateFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

该方法主要是创建共享UID的相关信息。

这里先以name为key在mSharedUsers中查看找是否已经有名为name的共享UID了。如果有的话,判断此UID和传入的uid是否相等,不相等的就报错。这意味着,不能对已有的共享UID信息,绑定新的uid。

如果没有的话,创建一个新的SharedUserSetting,并将其加入mSharedUsers中去。

pkgFlags为ApplicationInfo.FLAG_SYSTEM表明该app是system app.

pkgPrivateFlags为ApplicationInfo.PRIVATE_FLAG_PRIVILEGED表明具备system权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private boolean addUserIdLPw(int uid, Object obj, Object name) {
    if (uid > Process.LAST_APPLICATION_UID) {
        return false;
    }

    if (uid >= Process.FIRST_APPLICATION_UID) {
        int N = mUserIds.size();
        final int index = uid - Process.FIRST_APPLICATION_UID;
        while (index >= N) {
            mUserIds.add(null);
            N++;
        }
        if (mUserIds.get(index) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate user id: " + uid
                    + " name=" + name);
            return false;
        }
        mUserIds.set(index, obj);
    } else {
        if (mOtherUserIds.get(uid) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared id: " + uid
                            + " name=" + name);
            return false;
        }
        mOtherUserIds.put(uid, obj);
    }
    return true;
}

主要针对普通uid和system uid进行不同处理。

每个普通的app安装的时候,都会被分配一个uid,这和Android系统一些系统服务和具备高权限的app的uid是不在一个区间的。

普通的app的uid加入mUsrIds,其他的加入mOtherUserIds.

PMS构造方法中创建了android.uid.system,android.uid.phone,android.uid.log,android.uid.nfc,android.uid.bluetooth,android.uid.shell这些共享uid。主要给以下场景使用提供便利:

在一些系统app的AndroidManifest.xml中会有下面的信息:

1
android:sharedUserId="android.uis.system"

或者android.uid.phone等,如果不提前创建好,就没办法拥有这些共享uid所具备的权限了。

创建dexopt优化器对象

源码:

1
2
3
4
5
6
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(this);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

mOnPermissionChangeListeners = new OnPermissionChangeListeners(
        FgThread.get().getLooper());

mInstaller是传入的与installd守护进程通信的installer。

创建PackageDexOptimizer对象,该类主要用来执行ART中的patchoat命令,用来对oat文件的偏移值进行随机化。该类是Android M 中才有的。

创建监听权限更显的监听者。因为Android M中允许动态修改App权限。

创建和初始化SystemConfig

源码:

1
2
3
4
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();

SystemConfig会读取

1
/system/etc/permissions

文件夹中的相关文件。

看看其构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
        // Only read features from OEM config
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), true);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), true);
    }

其中rootDirectory是“/system”.oemDirectory是”/oem”.也就是会尝试依次读取

1
/system/etc/sysconfig
/system/etc/permissions
/oem/etc/sysconfig
/oem/etc/permissions

这四个目录中的permission文件。

permission文件的读取是通过readPermissions函数完成的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void readPermissions(File libraryDir, boolean onlyFeatures) {
     // Read permissions from given directory.
     if (!libraryDir.exists() || !libraryDir.isDirectory()) {
         if (!onlyFeatures) {
             Slog.w(TAG, "No directory " + libraryDir + ", skipping");
         }
         return;//如果文件夹不存在,或者其不是一个文件夹,退出
     }
     if (!libraryDir.canRead()) {
         Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
         return;//如果不可读,则退出
     }

     // Iterate over the files in the directory and scan .xml files
     File platformFile = null;
     for (File f : libraryDir.listFiles()) {
         // We'll read platform.xml last
         if (f.getPath().endsWith("etc/permissions/platform.xml")) {
             platformFile = f;
             continue;//先不处理platform.xml,最后会单独处理
         }

         if (!f.getPath().endsWith(".xml")) {
             Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
             continue;//只处理xml文件
         }
         if (!f.canRead()) {
             Slog.w(TAG, "Permissions library file " + f + " cannot be read");
             continue;//如果xml文件不可读,跳过
         }

         readPermissionsFromXml(f, onlyFeatures);//解析xml文件
     }

     // Read platform permissions last so it will take precedence
     if (platformFile != null) {
        //最后单独处理platform.xml文件
         readPermissionsFromXml(platformFile, onlyFeatures);
     }

readPermissions方法的作用就是读取指定目录下的xml文件,然后调用readPermissionsFromXml方法来解析xml文件。

这些xml文件中指明了当前设备的硬件特性:

1
2
3
<permissions>
    <feature name="android.hardware.nfc" />
</permissions>

解析后,存储在ArrayMap类型的变量mAvailableFeatures中。

还可能指明了一些运行行时除了加载framework中的库之外,还要加载的一些java库:

1
2
3
4
<permissions>
 <library name="com.qualcomm.qcrilhook"
          file="/system/framework/qcrilhook.jar"/>
</permissions>

这些library字段中的name属性值,将会存放到PMS的成员变量mSharedLibrsries中。

其中platform中,指明了当前设备中定义的相关权限:

1
2
3
4
5
6
7
8
9
..................

<permission name="android.permission.BLUETOOTH_ADMIN" >
       <group gid="net_bt_admin" />
</permission>

..................
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
..................

其中permission表示name中的字符串表示的权限赋予group标签中的属性gid中的用户组。解析的时候,创建一个PermissionEntry,它是SystemConfig的一个内部类

1
2
3
4
5
6
7
8
9
10
public static final class PermissionEntry {
    public final String name;
    public int[] gids;
    public boolean perUser;

    PermissionEntry(String name, boolean perUser) {
        this.name = name;
        this.perUser = perUser;
    }
}

SystemConfig中有一个ArrayMap的mPermissions变量,创建的PermissionEntry会存入该变量中。而group中的uid则会保存在SystemConfig中的mGlobalGids整型数组中。

assign-permission表示把属性name中的字符串表示的权限赋予属性uid中的用户。uid和name则存入SystemConfig中的SparseArray<arrayset> 类型的mSystemPermissions变量中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值