基于Android P版本PKMS详解一


[TOC]

# 一 修改记录

| 版本  | 修改日期   | 作者   | 修改内容 |
| :----| ----------| ----- | ------- |
| v1.0 | 2019.05.09 | 初版   |

# 二 概述

PackageManagerService (简称 `PKMS`),是 Android 系统中核心服务之一,管理着所有跟 package 相关的工作,常见的比如安装、卸载、更新应用等。

![image](res/IPackageManager.png)

- IPackageManager.aidl 中定义了 Binder 通信中的业务方法,编译后生成的 IPackageManager.java 接口类中定义了内部类 Stub,该类继承自 Binder 并实现了 IPackageManager 接口;

- PackageManagerService 继承自 IPackageManager.Stub 类,作为服务端参与 Binder 通信;

- IPackageManager.Stub 类中定义了一个内部类 Proxy,该类有一个 IBinder 类型的成员变量 mRemote,mRemote 用于和服务端 PackageManagerService 通信;

- 客户端一般通过 Context 的 getPackageManager 方法获取一个类型为 PackageManager 的对象,该对象的实际类型是 PackageManager 的子类 ApplicationPackageManager。ApplicationPackageManager 并没有直接参与 Binder 通信,而是通过其 IPackageManager 类型的成员变量 mPM,它指向一个 IPackageManager.Stub.Proxy 类型的对象。

本文基于 Android P 分析。

# 三 SystemServer 启动 PKMS

`SystemServer` 是由 `Zygote` 孵化而来的一个进程(进程名为:`system_server`),Android 系统中几乎所有的核心服务都是在这个进程中,例如:ActivityManagerService、PackageManagerService、PowerManagerService 和 WindowManagerService 等。

> frameworks/base/services/java/com/android/server/SystemServer.java

```java {.line-numbers}
public final class SystemServer {
    ...
    private final int mFactoryTestMode;

    private Context mSystemContext;
    private SystemServiceManager mSystemServiceManager;

    ...

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        // 创建 SystemServer 对象并执行其 run 方法
        new SystemServer().run();
    }

    public SystemServer() {
        // Check for factory test mode.
        mFactoryTestMode = FactoryTest.getMode();
        ...
    }

    private void run() {
        try {
            traceBeginAndSlog("InitBeforeStartServices");
            ...
            // Initialize the system context.
            // 初始化系统上下文
            createSystemContext();

            // Create the system service manager.
            // 创建 SystemServiceManager 对象,用于管理 system_server 进程中的各种 service.
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            ...
        } finally {
            traceEnd();  // InitBeforeStartServices
        }

        ...
        // Start services.
        try {
            traceBeginAndSlog("StartServices");
            // 启动引导服务
            startBootstrapServices();
            ...
            // 启动核心服务
            startCoreServices();
            ...
            // 启动其他服务
            startOtherServices();
            ...
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }

        ...
        // Loop forever.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

    ...

    private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        ...
    }

    ...
}
```

## 3.1 startBootstrapServices

`system_server` 的 `startBootstrapServices` 方法会启动一些引导服务,其中 `PKMS` 就是在这里启动:

```java {.line-numbers}

    private PackageManagerService mPackageManagerService;
    private PackageManager mPackageManager;
    ...
    private boolean mOnlyCore;
    private boolean mFirstBoot;
    ...

    /**
     * Starts the small tangle of critical services that are needed to get
     * the system off the ground.  These services have complex mutual dependencies
     * which is why we initialize them all in one place here.  Unless your service
     * is also entwined in these dependencies, it should be initialized in one of
     * the other functions.
     */
    private void startBootstrapServices() {
        Slog.i(TAG, "Reading configuration...");
        ...

        // 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.
        traceBeginAndSlog("StartInstaller");
        // 启动 Installer 服务
        Installer installer = mSystemServiceManager.startService(Installer.class);
        traceEnd();

        ...

        // We need the default display before we can initialize the package manager.
        traceBeginAndSlog("WaitForDisplay");
        mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
        traceEnd();

        // 处于加密状态则仅仅解析核心应用
        // Only run "core" apps if we're encrypting the device.
        String cryptState = SystemProperties.get("vold.decrypt");
        if (ENCRYPTING_STATE.equals(cryptState)) {
            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
            mOnlyCore = true;
        } else if (ENCRYPTED_STATE.equals(cryptState)) {
            Slog.w(TAG, "Device encrypted - only parsing core apps");
            mOnlyCore = true;
        }

        ...
        traceBeginAndSlog("StartPackageManagerService");
        // 创建 PKMS 对象并注册到 ServiceManager
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        // 判断本次是否为初次启动,当 Zygote 或 SystemServer 退出时,init 会再次启动他们
        // 这里 FirstBoot 是指开机后的第一次启动
        mFirstBoot = mPackageManagerService.isFirstBoot();
        // 获取 PackageManager
        mPackageManager = mSystemContext.getPackageManager();
        traceEnd();

        ...
    }
```

## 3.2 startOtherServices

`system_server` 的 `startOtherServices` 方法会启动其他服务,其中也会对 `PKMS` 做一些操作:

```java {.line-numbers}
    ...
    private ActivityManagerService mActivityManagerService;
    ...
    /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
     * and organized.
     */
    private void startOtherServices() {
        ...

        if (!mOnlyCore) {
            traceBeginAndSlog("UpdatePackagesIfNeeded");
            try {
                // 将调用 performDexOptUpgrade
                mPackageManagerService.updatePackagesIfNeeded();
            } catch (Throwable e) {
                reportWtf("update packages", e);
            }
            traceEnd();
        }

        traceBeginAndSlog("PerformFstrimIfNeeded");
        try {
            // 执行 FSTRIM,即磁盘维护操作。
            // 对整个分区执行 TRIM 操作,将标记为删除的文件彻底从磁盘上移除
            // 而不是等到写入时再移除,目的是提高写入时效率。
            mPackageManagerService.performFstrimIfNeeded();
        } catch (Throwable e) {
            reportWtf("performing fstrim", e);
        }
        traceEnd();

        ...

        traceBeginAndSlog("StartBootPhaseSystemServicesReady");
        mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
        traceEnd();

        ...

        traceBeginAndSlog("MakePackageManagerServiceReady");
        // 通知系统 PKMS 就绪
        mPackageManagerService.systemReady();
        traceEnd();

        ...

        traceBeginAndSlog("StartBootPhaseDeviceSpecificServicesReady");
        mSystemServiceManager.startBootPhase(SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
        traceEnd();

        ...

        // We now tell the activity manager it is okay to run third party
        // code.  It will call back into us once it has gotten to the state
        // where third party code can really run (but before it has actually
        // started launching the initial applications), for us to complete our
        // initialization.
        mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
            traceBeginAndSlog("StartActivityManagerReadyPhase");
            mSystemServiceManager.startBootPhase(
                    SystemService.PHASE_ACTIVITY_MANAGER_READY);
            traceEnd();

            ...

            // Wait for all packages to be prepared
            mPackageManagerService.waitForAppDataPrepared();

            ...

        }, BOOT_TIMINGS_TRACE_LOG);
    }
```

`PKMS` 启动后将参与一些系统优化的工作,然后调用 systemReady 方法通知系统进入就绪状态。

整个 `system_server` 进程启动过程,涉及 `PKMS` 的主要有以下几个操作:

- PKMS.main

- PKMS.updatePackagesIfNeeded

- PKMS.performFstrimIfNeeded

- PKMS.systemReady

- PKMS.waitForAppDataPrepared

# 四 PKMS.main

PackageManagerService.main 过程主要是创建 PKMS 服务,并注册到 ServiceManager:

> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

```java {.line-numbers}
public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
    ...

    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        // 检查系统属性
        PackageManagerServiceCompilerMapping.checkProperties();

        // 创建 PKMS
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        // split system user
        m.enableSystemUserPackages();
        // 注册到 ServiceManager
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

    ...
}
```

main 方法看似只有几行代码很简单,但执行时间却很长,主要原因是 `PKMS` 在其构造方法中做了很多“重体力活”,这也是影响 Android 启动速度慢的主要原因之一。

`PKMS` 构造方法的主要功能是:扫描 Android 系统中几个目标目录中的 APK 并进行解析,从而建立合适的数据结构以管理诸如 Package 信息、四大组件信息、权限信息等各种信息。

`PKMS` 的工作流程相对简单,复杂的是其中用于保存各种信息的数据结构和他们之间的关系,以及影响最终结果的策略控制(例如前面代码中的 mOnlyCore 变量,用于判断是否只扫面系统目录)。

```java {.line-numbers}

    ...
    // Lock for state used when installing and doing other long running
    // operations.  Methods that must be called with this lock held have
    // the suffix "LI".
    final Object mInstallLock = new Object();

    // ----------------------------------------------------------------

    // Keys are String (package name), values are Package.  This also serves
    // as the lock for the global state.  Methods that must be called with
    // this lock held have the prefix "LP".
    @GuardedBy("mPackages")
    final ArrayMap<String, PackageParser.Package> mPackages =
            new ArrayMap<String, PackageParser.Package>();

    ...

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());

        ...

        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ...

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);

            ...

            if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                ...
            }

            ...

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());

            ...

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());
            ...
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)

        ...

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    ...

```

`PKMS` 的构造方法涉及的知识点较多,代码段也很长,分为以下几个阶段,每个阶段会输出相对应的 EventLog:除了第一阶段的开头部分代码,后续代码都是同时持有同步锁 mPackages 和 mInstallLock 的过程中执行的。

- BOOT_PROGRESS_PMS_START

- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

- BOOT_PROGRESS_PMS_DATA_SCAN_START

- BOOT_PROGRESS_PMS_SCAN_END

- BOOT_PROGRESS_PMS_READY

接下来分别分析这 5 个阶段:

## 4.1 PMS_START

第一阶段 `PMS_START` 主要由两部分组成,无需加锁的部分和同时持有两个锁的部分。由于代码较长,大致分成三段,先分析开头一段代码:

```java {.line-numbers}
    ...
    private static final int RADIO_UID = Process.PHONE_UID;
    private static final int LOG_UID = Process.LOG_UID;
    private static final int NFC_UID = Process.NFC_UID;
    private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
    private static final int SHELL_UID = Process.SHELL_UID;
    private static final int SE_UID = Process.SE_UID;

    ...

    final int mSdkVersion = Build.VERSION.SDK_INT;

    final Context mContext;
    final boolean mFactoryTest;
    final boolean mOnlyCore;
    final DisplayMetrics mMetrics;
    ...

    @GuardedBy("mPackages")
    final Settings mSettings;
    ...
    // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
    // LOCK HELD.  Can be called with mInstallLock held.
    @GuardedBy("mInstallLock")
    final Installer mInstaller;
    ...

    // TODO remove this and go through mPermissonManager directly
    final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
    private final PermissionManagerInternal mPermissionManager;

    ...

    static UserManagerService sUserManager;

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...

        // SDK 版本检查
        if (mSdkVersion <= 0) {
            Slog.w(TAG, "**** ro.build.version.sdk not set!");
        }

        mContext = context;

        // 假定为 false,即运行在非工厂模式下
        mFactoryTest = factoryTest;
        // 假定为 false,即运行在普通模式下,不仅仅加载核心服务
        mOnlyCore = onlyCore;
        // 存储与显示屏相关的一些属性,例如屏幕的宽高尺寸,分辨率信息等
        mMetrics = new DisplayMetrics();
        // 创建 Installer 对象,该对象与 Native 进程 installd 交互
        mInstaller = installer;

        // Create sub-components that provide services / data. Order here is important.
        synchronized (mInstallLock) {
        synchronized (mPackages) {
            // Expose private service for system components to use.
            LocalServices.addService(
                    PackageManagerInternal.class, new PackageManagerInternalImpl());
            // 创建用户管理服务
            sUserManager = new UserManagerService(context, this,
                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
            // 创建权限管理服务
            mPermissionManager = PermissionManagerService.create(context,
                    new DefaultPermissionGrantedCallback() {
                        @Override
                        public void onDefaultRuntimePermissionsGranted(int userId) {
                            synchronized(mPackages) {
                                mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
                            }
                        }
                    }, mPackages /*externalLock*/);
            mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
            // Settings 类用于存储系统运行过程中的一些设置
            mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
        }
        }
        // 添加 system、phone、log、nfc、bluetooth、shell 这六种 shareUserId 到 mSettings
        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);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

        ...
    }

    ...
```

刚进入构造方法,就遇到了第一个较为复杂的数据结构 `Settings`,以及它的 `addShareUserLPw` 方法。

### 4.1.1 创建 Settings

> frameworks/base/services/core/java/com/android/server/pm/Settings.java

```java {.line-numbers}
/**
 * Holds information about dynamic settings.
 */
public final class Settings {
    ...

    private final File mSettingsFilename;
    private final File mBackupSettingsFilename;
    private final File mPackageListFilename;
    private final File mStoppedPackagesFilename;
    private final File mBackupStoppedPackagesFilename;
    /** The top level directory in configfs for sdcardfs to push the package->uid,userId mappings */
    private final File mKernelMappingFilename;

    ...

    private final File mSystemDir;


    Settings(PermissionSettings permissions, Object lock) {
        this(Environment.getDataDirectory(), permissions, lock);
    }

    Settings(File dataDir, PermissionSettings permission, Object lock) {
        ...

        // 创建“/data/system/”目录
        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);
        // 用于描述系统所安装的 Package 信息
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        // "packages.xml" 的备份
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        // 存储系统中的所有非系统自带的应用信息,即 UID 大于 10000 的应用
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);

        // sdcardfs 相关的文件
        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;

        // Deprecated: Needed for migration
        // 记录系统中被强制停止运行的应用信息
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        // "packages-stopped.xml" 的备份
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }

    ...
}
```

`Settings` 的构造方法的主要作用:建立与某些系统配置文件、目录之间的关联。首先,它会创建指向 `/data/system/` 目录的 File 实例,这个目录下会保存很多系统文件。其次,就是创建 `/data/system/` 目录下的某些 .xml 文件或其他文件的 File 实例。

上面源码中涉及到 5 个文件:

   ✨ packages.xml: PKMS 扫描完目标目录后,会创建 packages.xml。当系统进行程序安装、卸载和更新等操作时,均会更新该文件;

   ✨ packages-backup.xml:packages.xml 文件的备份;

   ✨ packages.list:用于描述系统中存在的所有非系统自带的 APK 信息。当这些 APK 有变化时,PKMS就会更新该文件;

   ✨ packages-stopped.xml:记录被用户强行停止的应用的 Package 信息(例如,从设置进入某个应用,然后点击强行停止,那么应用的Package信息就会被记录);

   ✨ packages-stopped-back.xml:packages-stopped.xml 文件的备份。

上面的介绍中涉及到了两个 back-up 文件,因为 Android 系统在修改 packages.xml、packages-stopped.xml 之前,会先对它们进行备份。当对它们的修改操作正常完成,则会删掉备份的文件。如果在修改过程中系统出现问题重启了,会再次去读取这两个文件;如果此时发现它们的备份文件还存在,则说明上一次对两份文件的修改操作发生了异常,这两份文件的内容可能已经不准确了,这时系统会去使用之前备份的文件的内容。

### 4.1.2 addShareUserLPw

```java {.line-numbers}
    ...
    final ArrayMap<String, SharedUserSetting> mSharedUsers =
            new ArrayMap<String, SharedUserSetting>();
    private final ArrayList<Object> mUserIds = new ArrayList<Object>();
    private final SparseArray<Object> mOtherUserIds =
            new SparseArray<Object>();
    ...

    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        // 根据 name 从 map 中取出值
        SharedUserSetting s = mSharedUsers.get(name);
        // 如果值不为 null 并且保存的 uid 和传递过来的一致,就直接返回结果
        // uid 不一致则返回 null
        if (s != null) {
            if (s.userId == uid) {
                return s;
            }
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared user, keeping first: " + name);
            return null;
        }
        // 若 s 为 null,则根据传递过来的参数新创建对象
        s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
        s.userId = uid;
        // 在系统中保存值为 uid 的 用户 id,成功返回 true
        if (addUserIdLPw(uid, s, name)) {
            // 将 name 与 s 键值对添加到 mSharedUsers 中保存
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

    ...

    private boolean addUserIdLPw(int uid, Object obj, Object name) {
        // 系统所支持的最大的应用 Package 的 UID,不能超出限制 19999
        if (uid > Process.LAST_APPLICATION_UID) {
            return false;
        }

        // 第一个应用 Package(非系统安装应用)的起始 UID
        if (uid >= Process.FIRST_APPLICATION_UID) {
            int N = mUserIds.size();
            // 计算索引,其值是 uid 和 FIRST_APPLICATION_UID 的差
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) {
                mUserIds.add(null);
                N++;
            }
            // 如果数组的目标索引值位置有不为 null,说明已经添加过
            if (mUserIds.get(index) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate user id: " + uid
                        + " name=" + name);
                return false;
            }
            // 应用 Package 的 uid 由 mUserIds 保存
            mUserIds.set(index, obj);
        } else {
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared id: " + uid
                                + " name=" + name);
                return false;
            }
            // 系统 Package 的 uid 由 mOtherUserIds 保存
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }

    ...
```

注意到 `addSharedUserLPw` 方法传入的四个参数中有一个 `uid`。`UID` 为 `用户 ID` 的缩写,`GID` 为 `用户组 ID` 的缩写。一般来说,每一个进程都会有一个对应的 `UID`(即标示该进程属于哪个用户,不同用户拥有不同权限)。一个进程也可分属不用的用户组(每个用户都有对应的权限)。`UID/GID` 和进程的权限有关。

在 Android 系统中,`UID/GID` 在 `Process.java` 文件中定义,如下所示(列举部分):

> frameworks/base/core/java/android/os/Process.java

```java {.line-numbers}
/**
 * Tools for managing OS processes.
 */
public class Process {
    ...

    /**
     * Defines the root UID.
     * @hide
     */
    public static final int ROOT_UID = 0;

    /**
     * Defines the UID/GID under which system code runs.
     */
    public static final int SYSTEM_UID = 1000;

    /**
     * Defines the UID/GID under which the telephony code runs.
     */
    public static final int PHONE_UID = 1001;

    /**
     * Defines the UID/GID for the user shell.
     * @hide
     */
    public static final int SHELL_UID = 2000;

    /**
     * Defines the UID/GID for the log group.
     * @hide
     */
    public static final int LOG_UID = 1007;

    /**
     * Defines the UID/GID for the WIFI supplicant process.
     * @hide
     */
    public static final int WIFI_UID = 1010;

    ...

    /**
     * Defines the start of a range of UIDs (and GIDs), going from this
     * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
     * to applications.
     */
    public static final int FIRST_APPLICATION_UID = 10000;

    /**
     * Last of application-specific UIDs starting at
     * {@link #FIRST_APPLICATION_UID}.
     */
    public static final int LAST_APPLICATION_UID = 19999;

    ...
}
```

`Settings` 中有一个 `mSharedUsers` 成员,该成员存储的是 【“name” 与 “SharedUserSetting” 键值对】,也就是说可以通过 `name` 为 key 得到对应的 `SharedUserSetting` 对象。

### 4.1.3 SharedUserSetting

为了了解 `SharedUserSetting` ,我们以 `Settings` 作为例子:

> packages/apps/Settings/AndroidManifest.xml

```xml {.line-numbers}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.settings"
        coreApp="true"
        android:sharedUserId="android.uid.system">

    ...

</manifest>
```

在 `Settings` 的 `AndroidManifest.xml` 中,声明了一个名为 `android:sharedUserId` 的属性:`android.uid.system`。

Android 系统中每一个应用都运行在单独的虚拟机中,以便提高系统的稳定性,每个应用进程都是由 Linux 系统用户所创建。当 APK 安装的时候,`userId` 这个标志就会产生,APK 在设备上的整个声明周期中,这个 `userId` 不再改变。相同的 `sharedUserId` 的应用归属相同的 Linux 用户,资源共享就有很多的便利可以利用。如果想要不同的 APK 之间通过 sharedUserId 共享数据则需要满足以下条件:

- APK 的签名必须相同;

- `android:sharedUserId` 的值必须相同(且必须包含一个 ".");

- 如果想要运行在同一个进程中,必须设置 `android:process` 的值相同。

通过声明特定的 `sharedUserId` ,该 APK 所在的进程将被赋予指定的 `UID`,例如 `Settings` 声明了 `system` 的 uid,则运行 `Settings` 的进程就可以享受 `system 用户` 所对应的权限。

如果组织一种数据结构来包括上述内容,则有以下关键点需要注意:


   ✨ `AndroidManifest.xml` 中 `android:sharedUserId` 属性指定了一个字符串,它是 `UID` 的字符串描述,故对应数据结构中也应该有这样一个字符串,这样就把代码和 XML 中的属性联系起来了;

   ✨ 在 LINUX 系统中,真正的 uid 是一个整数,所以该数据结构中必然有一个整型变量;

   ✨ 多个 APK 可声明同一个 `sharedUserId`,因此该数据结构必然会保存那些声明了相同 `sharedUserId` 的 APK 的某些信息;

`Settings` 类中定义了一个 `mSharedUsers` 成员,它是一个 `ArrayMap`,以字符串(如:`android.uid.system`)为 key,对应的 value 是一个 `SharedUserSetting` 对象。

```java {.line-numbers}
    final ArrayMap<String, SharedUserSetting> mSharedUsers =
            new ArrayMap<String, SharedUserSetting>();
```

`SharedUserSetting` 定义了一个成员变量 `packages`,类型为 `ArraySet`,用于保存声明了相同 `sharedUserId` 的 Package 的权限设置信息。

> frameworks/base/services/core/java/com/android/server/pm/SharedUserSetting.java

```java {.line-numbers}
/**
 * Settings data for a particular shared user ID we know about.
 */
public final class SharedUserSetting extends SettingBase {
    final String name;

    int userId;

    // flags that are associated with this uid, regardless of any package flags
    int uidFlags;
    int uidPrivateFlags;

    // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so
    // that all apps within the sharedUser run in the same selinux context.
    int seInfoTargetSdkVersion;

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

    final PackageSignatures signatures = new PackageSignatures();
    Boolean signaturesChanged;

    SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
        super(_pkgFlags, _pkgPrivateFlags);
        uidFlags =  _pkgFlags;
        uidPrivateFlags = _pkgPrivateFlags;
        name = _name;
        seInfoTargetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
    }

    ...
}
```

每个 Package 有自己的权限设置。权限的概念由 `PackageSeting` 类表达。该类继承自 `PackageSettingBase` 类,`PackageSettingBase` 又继承自 `SettingBase`。`SettingBase` 对象持有 `PermissionsState` 对象,用于表示可用的权限。

![image](res/SettingsBase.png)

如上图所示,`Settings` 对象中持有多个 `SharedUserSetting` 对象,每个 `SharedUserSetting` 对象又会持有多个 `PackageSetting` 对象。

从继承关系来看,`SharedUserSetting` 和 `PackageSetting` 对象,最终都将继承 `SettingBase` 对象。

从图上可以看出,`SettingBase` 对象持有 `PermissionsState` 对象,用于表示可用的权限。

因此,`SharedUserSetting` 对象和 `PackageSetting` 对象中都将包含有 `PermissionsState`。

从而我们可以据此推测出,`SharedUserSetting` 中持有的是一组 Package 共有的权限;`PackageSetting` 中持有的是单个 Package 独有的权限。

### 4.1.4 SystemConfig

分析完 PMS 构造方法第一阶段前半部分后,接下来就要继续回到构造方法中分析剩下的代码:

```java {.line-numbers}
    ...
    final int mDefParseFlags;
    final String[] mSeparateProcesses;
    ...
    final ProtectedPackages mProtectedPackages;
    ...
    @GuardedBy("mAvailableFeatures")
    final ArrayMap<String, FeatureInfo> mAvailableFeatures;
    ...
    private final PackageDexOptimizer mPackageDexOptimizer;
    ...

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...

        // 该值和调试有关,一般不设置该属性
        // 这个属性可以强制应用程序组件在自己的进程中运行,有两种方法可以使用这个:
        // 所有的进程都会受到影响:setprop debug.separate_processes
        // 指定进程:setprop debug.separate_processes “com.xxx, com.xxx”
        String separateProcesses = SystemProperties.get("debug.separate_processes");
        if (separateProcesses != null && separateProcesses.length() > 0) {
            if ("*".equals(separateProcesses)) {
                mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
                mSeparateProcesses = null;
                Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
            } else {
                mDefParseFlags = 0;
                mSeparateProcesses = separateProcesses.split(",");
                Slog.w(TAG, "Running with debug.separate_processes: "
                        + separateProcesses);
            }
        } else {
            mDefParseFlags = 0;
            mSeparateProcesses = null;
        }

        // 对应用进行 dexopt 优化的辅助类
        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        ...

        // 获取当前设备的显示屏信息
        getDefaultDisplayMetrics(context, mMetrics);

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
        // 通过 SystemConfig 读取系统的 feature、permession 等配置,
        // 并初始化 mGlobalGids/mAvailableFeatures 成员
        SystemConfig systemConfig = SystemConfig.getInstance();
        mAvailableFeatures = systemConfig.getAvailableFeatures();
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        mProtectedPackages = new ProtectedPackages(mContext);

        ...
    }
```

`SystemConfig` 主要作用是读取 XML 获取系统配置信息:

> frameworks/base/core/java/com/android/server/SystemConfig.java

```java {.line-numbers}
/**
 * Loads global system configuration info.
 */
public class SystemConfig {
    static final String TAG = "SystemConfig";

    static SystemConfig sInstance;

    ...

    public static SystemConfig getInstance() {
        synchronized (SystemConfig.class) {
            if (sInstance == null) {
                sInstance = new SystemConfig();
            }
            return sInstance;
        }
    }

    ...

    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);

        // Vendors are only allowed to customze libs, features and privapp permissions
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
            // For backward compatibility
            vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
        }
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);

        // Allow ODM to customize system configs as much as Vendor, because /odm is another
        // vendor partition other than /vendor.
        int odmPermissionFlag = vendorPermissionFlag;
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);

        // Allow OEM to customize features and OEM permissions
        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);

        // Allow Product to customize system configs around libs, features, permissions and apps
        int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
    }

    ...
}
```

`SystemConfig` 的构造方法所做的工作就是通过 `readPermissions` 方法读取并解析指定目录下的所有 XML 文件,然后把这些信息保存到 `SystemConfig` 中,涉及到的目录有如下:

- /system/etc/sysconfig

- /system/etc/permissions

- /vendor/etc/sysconfig

- /vendor/etc/permissions

- /odm/etc/sysconfig

- /odm/etc/permissions

- /oem/etc/sysconfig

- /oem/etc/permissions

- /product/etc/sysconfig

- /product/etc/permissions

其中比较重要的是 `system/etc/permissions` 目录,该目录文件大多来源于代码中的 `frameworks/base(or native)/data/etc`,这些文件的作用是表明系统支持的 feature 有哪些,例如是否支持蓝牙、wifi 等。

### 4.1.5 readPermissions

```java {.line-numbers}
    void readPermissions(File libraryDir, int permissionFlag) {
        // Read permissions from given directory.
        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
            if (permissionFlag == ALLOW_ALL) {
                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;
        // 遍历 libraryDir 目录下所有的文件
        for (File f : libraryDir.listFiles()) {
            // We'll read platform.xml last
            // "etc/permissions/platform.xml" 放在最后读取
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                platformFile = f;
                continue;
            }

            // 仅读取 XML 文件
            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }

            // 调用 readPermissionsFromXml 解析此 XML 文件
            readPermissionsFromXml(f, permissionFlag);
        }

        // Read platform permissions last so it will take precedence
        // "etc/permissions/platform.xml" 文件的解析优先级最高
        if (platformFile != null) {
            readPermissionsFromXml(platformFile, permissionFlag);
        }
    }
```

`readPermissions` 方法其实就是调用 `readPermissionsFromXml` 方法解析 `/xxx/etc/permissions(or sysconfig)/` 目录下的 XML 文件。以 `system` 下对应目录举例:

![image](res/read_from_sysconfig.png)
![image](res/read_from_permissions.png)

再以 `/system/etc/permissions/platform.xml` 为例,该文件实际上在 framework 中:

> frameworks/base/data/etc/platform.xml

```xml {.line-numbers}
<?xml version="1.0" encoding="utf-8"?>
...
<permissions>

    <!-- ================================================================== -->
    <!-- ================================================================== -->
    <!-- ================================================================== -->

    <!-- The following tags are associating low-level group IDs with
         permission names.  By specifying such a mapping, you are saying
         that any application process granted the given permission will
         also be running with the given group ID attached to its process,
         so it can perform any filesystem (read, write, execute) operations
         allowed for that group. -->

    <!-- 建立权限名与 gid 的映射关系。如下面声明的 BLUETOOTH_ADMIN 权限,
         它对应的用户组是 net_bt_admin。注意,该文件中的 permission 标签只对
         那些需要通过读写设备(蓝牙/cameta)/创建 socket 等进程划分了 gid。
         因为这些权限涉及和 Linux 内核交互,所以需要在底层权限(由不用的用户组界定)
         和 Android 层权限(由不同的字符串界定)之间建立映射关系。 -->

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

    <permission name="android.permission.BLUETOOTH" >
        <group gid="net_bt" />
    </permission>

    ...

    <!-- The following tags are assigning high-level permissions to specific
         user IDs.  These are used to allow specific core system users to
         perform the given operations with the higher-level framework.  For
         example, we give a wide variety of permissions to the shell user
         since that is the user the adb shell runs under and developers and
         others should have a fairly open environment in which to
         interact with the system. -->

    <!-- 赋予对应 uid 相应的权限。如下面一行表示 uid 为 media
         赋予它 MODIFY_AUDIO_SETTINGS 的权限,其实就是把它加到对应的用户组中 -->
    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="media" />

    ...

    <!-- This is a list of all the libraries available for application
         code to link against. -->

    <!-- 系统提供的 Java 库,应用程序运行时必须要链接这些库,该工作由系统自动完成 -->
    <library name="android.test.base"
            file="/system/framework/android.test.base.jar" />

    ...

</permissions>
```

platform.xml 文件中主要使用了如下 4 个标签:

 ✨ permission 和 group 用于建立 Linux 层 gid 和 Andrid 层 permission 之间的映射关系。

 ✨ assign-permission 用于向指定的 uid 赋予相应的权限。这个权限由 Android 定义,用于字符串表示。

 ✨ library 用于指定系统库。当应用程序运行时,系统会自动为这些进程加载这些库。

### 4.1.6 readPermissionsFromXml

`readPermissionFromXml` 作用就是将 XML 文件中的标签以及它们之间的关系转换成代码中的相应数据结构:

```java {.line-numbers}
    ...

    // Group-ids that are given to all packages as read from etc/permissions/*.xml.
    int[] mGlobalGids;

    // These are the built-in uid -> permission mappings that were read from the
    // system configuration files.
    final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();

    // These are the built-in shared libraries that were read from the
    // system configuration files.  Keys are the library names; strings are the
    // paths to the libraries.
    final ArrayMap<String, String> mSharedLibraries  = new ArrayMap<>();

    // These are the features this devices supports that were read from the
    // system configuration files.
    final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();

    // These are the features which this device doesn't support; the OEM
    // partition uses these to opt-out of features from the system image.
    final ArraySet<String> mUnavailableFeatures = new ArraySet<>();
    ...

    private void readPermissionsFromXml(File permFile, int permissionFlag) {
        FileReader permReader = null;
        try {
            permReader = new FileReader(permFile);
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
            return;
        }

        // 低内存设备
        final boolean lowRam = ActivityManager.isLowRamDeviceStatic();

        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(permReader);

            ...

            while (true) {
                ...
                String name = parser.getName();
                // 解析 group 标签
                if ("group".equals(name) && allowAll) {
                    String gidStr = parser.getAttributeValue(null, "gid");
                    if (gidStr != null) {
                        // 转换 XML 中的 gid 字符串为整型,并保存到 mGlobalGids 中
                        int gid = android.os.Process.getGidForName(gidStr);
                        mGlobalGids = appendInt(mGlobalGids, gid);
                    } else {
                        Slog.w(TAG, "<group> without gid in " + permFile + " at "
                                + parser.getPositionDescription());
                    }

                    XmlUtils.skipCurrentTag(parser);
                    continue;
                // 解析 permission 标签
                } else if ("permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");
                    ...
                    perm = perm.intern();
                    readPermission(parser, perm);

                // 解析 assign-permission 标签
                } else if ("assign-permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");
                    ...
                    String uidStr = parser.getAttributeValue(null, "uid");
                    ...
                    // 如果是 assign-permission,则取出 uid 字符串,然后获得 Linux 平台上的整型 uid 值
                    int uid = Process.getUidForName(uidStr);
                    ...
                    perm = perm.intern();
                    // 和 assign 相关的信息保存在 mSystemPermissions 中
                    ArraySet<String> perms = mSystemPermissions.get(uid);
                    if (perms == null) {
                        perms = new ArraySet<String>();
                        mSystemPermissions.put(uid, perms);
                    }
                    perms.add(perm);
                    XmlUtils.skipCurrentTag(parser);

                // 解析 library 标签
                } else if ("library".equals(name) && allowLibs) {
                    String lname = parser.getAttributeValue(null, "name");
                    String lfile = parser.getAttributeValue(null, "file");
                    if (lname == null) {
                        Slog.w(TAG, "<library> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (lfile == null) {
                        Slog.w(TAG, "<library> without file in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
                        // 将 XML 中的 name 和 library 属性值存储到 mSharedLibraries 中
                        mSharedLibraries.put(lname, lfile);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                // 解析 feature 标签
                } else if ("feature".equals(name) && allowFeatures) {
                    String fname = parser.getAttributeValue(null, "name");
                    int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
                    boolean allowed;
                    if (!lowRam) {
                        allowed = true;
                    } else {
                        String notLowRam = parser.getAttributeValue(null, "notLowRam");
                        allowed = !"true".equals(notLowRam);
                    }
                    if (fname == null) {
                        Slog.w(TAG, "<feature> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (allowed) {
                        addFeature(fname, fversion);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                // 解析 unavailable-feature 标签
                } else if ("unavailable-feature".equals(name) && allowFeatures) {
                    String fname = parser.getAttributeValue(null, "name");
                    if (fname == null) {
                        Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        mUnavailableFeatures.add(fname);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                // 其他标签
                }
                ...
            }
        }

        ...

        if (ActivityManager.isLowRamDeviceStatic()) {
            addFeature(PackageManager.FEATURE_RAM_LOW, 0);
        } else {
            addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
        }

        for (String featureName : mUnavailableFeatures) {
            removeFeature(featureName);
        }
    }

    private void addFeature(String name, int version) {
        FeatureInfo fi = mAvailableFeatures.get(name);
        if (fi == null) {
            fi = new FeatureInfo();
            fi.name = name;
            fi.version = version;
            mAvailableFeatures.put(name, fi);
        } else {
            fi.version = Math.max(fi.version, version);
        }
    }

    private void removeFeature(String name) {
        if (mAvailableFeatures.remove(name) != null) {
            Slog.d(TAG, "Removed unavailable feature " + name);
        }
    }
```

解析 `permission` 标签的时候,会创建一个 `PermissionEntry` 类,它关联了 `gids` 和 `permission name`:

```java {.line-numbers}
    ...

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

    // These are the permission -> gid mappings that were read from the
    // system configuration files.
    final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();

    ...

    void readPermission(XmlPullParser parser, String name)
            throws IOException, XmlPullParserException {
        if (mPermissions.containsKey(name)) {
            throw new IllegalStateException("Duplicate permission definition for " + name);
        }

        final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
        final PermissionEntry perm = new PermissionEntry(name, perUser);
        mPermissions.put(name, perm);

        int outerDepth = parser.getDepth();
        int type;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
               && (type != XmlPullParser.END_TAG
                       || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG
                    || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if ("group".equals(tagName)) {
                // 获取 Android 系统定义的组 ID 字串
                String gidStr = parser.getAttributeValue(null, "gid");
                if (gidStr != null) {
                    // 获取对应的 gid
                    int gid = Process.getGidForName(gidStr);
                    perm.gids = appendInt(perm.gids, gid);
                } else {
                    Slog.w(TAG, "<group> without gid at "
                            + parser.getPositionDescription());
                }
            }
            XmlUtils.skipCurrentTag(parser);
        }
    }
```

### 4.1.7 others

前面分析的 `Settings` 和 `XML 解析` 作为第一阶段的前两部分,还有剩下最后一部分:

```java {.line-numbers}
    ...
    final ServiceThread mHandlerThread;

    final PackageHandler mHandler;

    private final ProcessLoggingHandler mProcessLoggingHandler;
    ...
    private final InstantAppRegistry mInstantAppRegistry;
    ...
    ComponentName mCustomResolverComponentName;

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...

        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            // 创建名为“PackageManager”的 handler 线程
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            // 建立 PackageHandler 消息循环,用于处理外部的安装请求等消息
            // 比如 adb install、packageinstaller 安装APK时
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            mProcessLoggingHandler = new ProcessLoggingHandler();
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
            mInstantAppRegistry = new InstantAppRegistry(this);

            // 获取共享库
            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
            final int builtInLibCount = libConfig.size();
            for (int i = 0; i < builtInLibCount; i++) {
                String name = libConfig.keyAt(i);
                String path = libConfig.valueAt(i);
                addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                        SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
            }

            // SEAndroid 安全机制
            SELinuxMMAC.readInstallPolicy();

            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
            // 读取 "frameworks/base/core/res/res/raw/fallback_categories.csv"
            FallbackCategoryProvider.loadFallbacks();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
            // 是否首次开机启动
            mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

            // Clean up orphaned packages for which the code path doesn't exist
            // and they are an update to a system app - caused by bug/32321269
            // 清理那些代码路径不存在的异常 package
            final int packageSettingCount = mSettings.mPackages.size();
            for (int i = packageSettingCount - 1; i >= 0; i--) {
                PackageSetting ps = mSettings.mPackages.valueAt(i);
                if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
                        && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                    mSettings.mPackages.removeAt(i);
                    mSettings.enableSystemPackageLPw(ps.name);
                }
            }

            // 请求复制 preopted 文件
            if (!mOnlyCore && mFirstBoot) {
                requestCopyPreoptedFiles();
            }

            // 代替 framework-res.apk 中缺省的 ResolverActivity
            String customResolverActivity = Resources.getSystem().getString(
                    R.string.config_customResolverActivity);
            if (TextUtils.isEmpty(customResolverActivity)) {
                customResolverActivity = null;
            } else {
                mCustomResolverComponentName = ComponentName.unflattenFromString(
                        customResolverActivity);
            }

            // 至此,第一阶段结束
            long startTime = SystemClock.uptimeMillis();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
            ...
        }
        }

        ...
    }
```

这里特别注意下 `mFirstBoot` 的赋值:

> frameworks/base/services/core/java/com/android/server/pm/Settings.java

```java {.line-numbers}
    ...
    /** List of packages that installed other packages */
    final ArraySet<String> mInstallerPackages = new ArraySet<>();
    ...
    // For reading/writing settings file.
    private final ArrayList<Signature> mPastSignatures =
            new ArrayList<Signature>();
    private final ArrayMap<Long, Integer> mKeySetRefs =
            new ArrayMap<Long, Integer>();
    ...
    final StringBuilder mReadMessages = new StringBuilder();

    /**
     * Used to track packages that have a shared user ID that hasn't been read
     * in yet.
     * <p>
     * TODO: make this just a local variable that is passed in during package
     * scanning to make it less confusing.
     */
    private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>();
    ...
    boolean readLPw(@NonNull List<UserInfo> users) {
        FileInputStream str = null;
        // "packages.xml" 的备份
        if (mBackupSettingsFilename.exists()) {
            try {
                str = new FileInputStream(mBackupSettingsFilename);
                mReadMessages.append("Reading from backup settings file\n");
                PackageManagerService.reportSettingsProblem(Log.INFO,
                        "Need to read from backup settings file");
                // 同时存在 "packages.xml",则删除该文件
                if (mSettingsFilename.exists()) {
                    // If both the backup and settings file exist, we
                    // ignore the settings since it might have been
                    // corrupted.
                    Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
                            + mSettingsFilename);
                    mSettingsFilename.delete();
                }
            } catch (java.io.IOException e) {
                // We'll try for the normal settings file.
            }
        }

        mPendingPackages.clear();
        mPastSignatures.clear();
        mKeySetRefs.clear();
        mInstallerPackages.clear();

        try {
            if (str == null) {
                // 无备份且不存在 "packages.xml",即初始化状态,类似恢复出厂设置后首次开机,mFirstBoot 为 true
                if (!mSettingsFilename.exists()) {
                    mReadMessages.append("No settings file found\n");
                    PackageManagerService.reportSettingsProblem(Log.INFO,
                            "No settings file; creating initial state");
                    // It's enough to just touch version details to create them
                    // with default values
                    // 强制设置当前版本信息为系统默认版本
                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
                    return false;
                }
                str = new FileInputStream(mSettingsFilename);
            }
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(str, StandardCharsets.UTF_8.name());

            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
                ;
            }
            ...
            int outerDepth = parser.getDepth();
            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                    continue;
                }

                String tagName = parser.getName();
                // 解析 package 标签
                if (tagName.equals("package")) {
                    readPackageLPw(parser);
                // 解析 permissions 标签
                } else if (tagName.equals("permissions")) {
                    mPermissions.readPermissions(parser);
                } else if (tagName.equals("permission-trees")) {
                    mPermissions.readPermissionTrees(parser);
                // 解析 shared-user 标签
                } else if (tagName.equals("shared-user")) {
                    readSharedUserLPw(parser);
                } else if (tagName.equals("preferred-packages")) {
                    // no longer used.
                    ....
                }
        }

        ...

        if (mBackupStoppedPackagesFilename.exists()
                || mStoppedPackagesFilename.exists()) {
            // Read old file
            readStoppedLPw();
            mBackupStoppedPackagesFilename.delete();
            mStoppedPackagesFilename.delete();
            ...
        }

        ...

        return true;
    }
```

总结一下 PMS_START 阶段主要工作如下:

- 创建 `Settings` 类:这个是 Android 的全局管理者,用于协助 `PKMS` 保存所有的安装包信息;

- 获取系统配置信息:`SystemConfig` 构造方法中会通过 `readPermissions` 方法解析指定目录下的所有 XML 文件,然后把这些信息保存到 `SystemConfig` 中,其中动态库保存到 `mSharedLibraries`;

- 创建名为 `PackageManager` 的 handler 线程,建立 `PackageHandler` 消息循环,用于处理外部的安装请求等消息;

- 创建用户管理服务 `UserManagerService` 等其他对象;

## 4.2 PMS_SYSTEM_SCAN_START

`PKMS` 构造方法第二阶段的工作就是扫描系统中的 APK 了。由于需要逐个扫描文件,因此手机上装的程序越多,`PKMS` 的工作量就越大,系统启动速度也就越慢。接下来看 `PKMS` 第二阶段工作的核心内容,即扫描 Package,主要源码如下:

```java {.line-numbers}
    ...
    static final int SCAN_NO_DEX = 1<<0;
    ...
    static final int SCAN_BOOTING = 1<<4;
    ...
    static final int SCAN_INITIAL = 1<<9;
    ...
    static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1<<13;
    ...
    static final int SCAN_AS_SYSTEM = 1<<17;
    static final int SCAN_AS_PRIVILEGED = 1<<18;
    static final int SCAN_AS_OEM = 1<<19;
    static final int SCAN_AS_VENDOR = 1<<20;
    static final int SCAN_AS_PRODUCT = 1<<21;
    ...
    final boolean mIsUpgrade;
    final boolean mIsPreNUpgrade;
    final boolean mIsPreNMR1Upgrade;
    ...
    /**
     * Tracks existing system packages prior to receiving an OTA. Keys are package name.
     */
    final private ArraySet<String> mExistingSystemPackages = new ArraySet<>();
    /**
     * Whether or not system app permissions should be promoted from install to runtime.
     */
    boolean mPromoteSystemApps;
    ...
    private File mCacheDir;
    ...
    final ParallelPackageParserCallback mParallelPackageParserCallback =
            new ParallelPackageParserCallback();
    ...
    /**
     * Tracks new system packages [received in an OTA] that we expect to
     * find updated user-installed versions. Keys are package name, values
     * are package location.
     */
    final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
    ...
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...

        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            ...

            // 第二阶段开始,记录扫描开始时间
            long startTime = SystemClock.uptimeMillis();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
            ...
            // 检查系统环境变量,可通过 "adb shell env" 来查看系统所有的环境变量及相应值
            // BOOTCLASSPATH:常见包含 /system/framework 目录下的 framework.jar 等文件
            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            // SYSTEMSERVERCLASSPATH:主要包括 /system/framework 目录下 services.jar,
            // ethernet-service.jar,wifi-service.jar,com.android.location.provider.jar这4个文件
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");

            if (bootClassPath == null) {
                Slog.w(TAG, "No BOOTCLASSPATH found!");
            }

            if (systemServerClassPath == null) {
                Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
            }

            // 定义 frameworkDir 指向 /system/framework 目录
            File frameworkDir = new File(Environment.getRootDirectory(), "framework");

            // 根据 fingerprint 判断是否升级
            final VersionInfo ver = mSettings.getInternalVersion();
            mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
            ...

            // when upgrading from pre-M, promote system app permissions from install to runtime
            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

            // When upgrading from pre-N, we need to handle package extraction like first boot,
            // as there is no profiling data available.
            mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;

            mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;

            // save off the names of pre-existing system packages prior to scanning; we don't
            // want to automatically grant runtime permissions for new system apps
            // 是否需要提升系统应用的权限
            if (mPromoteSystemApps) {
                Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
                while (pkgSettingIter.hasNext()) {
                    PackageSetting ps = pkgSettingIter.next();
                    if (isSystemApp(ps)) {
                        // 遍历 Settings::mPackages 集合,将系统 APP 加入到 mExistingSystemPackages
                        mExistingSystemPackages.add(ps.name);
                    }
                }
            }

            // "/data/system/package_cache/1"
            mCacheDir = preparePackageParserCache(mIsUpgrade);

            // Set flag to monitor and not change apk file paths when
            // scanning install directories.
             // 定义扫描参数
            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;

            if (mIsUpgrade || mFirstBoot) {
                scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
            }

            // Collect vendor/product overlay packages. (Do this before scanning any apps.)
            // For security and version matching reason, only consider
            // overlay packages if they reside in the right directory.
            // 扫描 /vendor/overlay 目录
            scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);
            // 扫描 /product/overlay 目录
            scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT,
                    0);

            mParallelPackageParserCallback.findStaticOverlayPackages();

            // Find base frameworks (resource packages without code).
            // 扫描 /system/framework 目录
            scanDirTracedLI(frameworkDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_NO_DEX
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect privileged system packages.
            // 扫描 /system/priv-app 目录
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirTracedLI(privilegedAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary system packages.
            // 扫描 /system/app 目录
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirTracedLI(systemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM,
                    0);

            // Collect privileged vendor packages.
            File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
            try {
                privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /vendor/priv-app 目录
            scanDirTracedLI(privilegedVendorAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary vendor packages.
            File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
            try {
                vendorAppDir = vendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /vendor/app 目录
            scanDirTracedLI(vendorAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);

            // Collect privileged odm packages. /odm is another vendor partition
            // other than /vendor.
            File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
                        "priv-app");
            try {
                privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /odm/priv-app 目录
            scanDirTracedLI(privilegedOdmAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary odm packages. /odm is another vendor partition
            // other than /vendor.
            File odmAppDir = new File(Environment.getOdmDirectory(), "app");
            try {
                odmAppDir = odmAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /odm/app 目录
            scanDirTracedLI(odmAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);

            // Collect all OEM packages.
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            // 扫描 /oem/app 目录
            scanDirTracedLI(oemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_OEM,
                    0);

            // Collected privileged product packages.
            File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
            try {
                privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /product/priv-app 目录
            scanDirTracedLI(privilegedProductAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT
                    | SCAN_AS_PRIVILEGED,
                    0);

            // Collect ordinary product packages.
            File productAppDir = new File(Environment.getProductDirectory(), "app");
            try {
                productAppDir = productAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            // 扫描 /product/app 目录
            scanDirTracedLI(productAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT,
                    0);

            // Prune any system packages that no longer exist.
            // 清理不存在的系统包
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
            // Stub packages must either be replaced with full versions in the /data
            // partition or be disabled.
            final List<String> stubSystemApps = new ArrayList<>();
            if (!mOnlyCore) {
                ...
                final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();
                    ...
                    /*
                     * If this is not a system app, it can't be a
                     * disable system app.
                     */
                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                        ...
                        continue;
                    }

                    /*
                     * If the package is scanned, it's not erased.
                     */
                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                    if (scannedPkg != null) {
                        /*
                         * If the system app is both scanned and in the
                         * disabled packages list, then it must have been
                         * added via OTA. Remove it from the currently
                         * scanned package so the previously user-installed
                         * application can be scanned.
                         */
                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
                            ...
                            removePackageLI(scannedPkg, true);
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }

                        continue;
                    }

                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                        psit.remove();
                        logCriticalInfo(Log.WARN, "System package " + ps.name
                                + " no longer exists; it's data will be wiped");
                        // Actual deletion of code and data will be handled by later
                        // reconciliation step
                    } else {
                        // we still have a disabled system package, but, it still might have
                        // been removed. check the code path still exists and check there's
                        // still a package. the latter can happen if an OTA keeps the same
                        // code path, but, changes the package name.
                        final PackageSetting disabledPs =
                                mSettings.getDisabledSystemPkgLPr(ps.name);
                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()
                                || disabledPs.pkg == null) {
                            possiblyDeletedUpdatedSystemApps.add(ps.name);
                        }
                    }
                }
            }

            //delete tmp files
            deleteTempPackageFiles();

            final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();

            // Remove any shared userIDs that have no associated packages
            // 移除不相干包中的所有共享 userID
            mSettings.pruneSharedUsersLPw();
            // 计算扫描系统包所耗费的时间
            final long systemScanTime = SystemClock.uptimeMillis() - startTime;
            final int systemPackagesCount = mPackages.size();
            Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
                    + " ms, packageCount: " + systemPackagesCount
                    + " , timePerPackage: "
                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
                    + " , cached: " + cachedSystemApps);
            ...
            if (!mOnlyCore) {
                // 至此,第二阶段结束
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                ...
            }
            ...
        }
        }

        ...

    }

    ...
 

猜你喜欢

转载自blog.csdn.net/zhang_jun_xiang/article/details/90035919