深入Android系统(十)PMS-1-服务初始化

本文详细解析了Android系统PackageManagerService(PMS)的初始化过程,包括构造方法、重要成员变量、设置、系统应用扫描、数据扫描、权限处理等步骤。通过对PMS的深入理解,有助于更好地掌握Android应用的管理和运行机制。

导读

PMS笔记大小写到106kb时,就知大事不妙,导读叒叒叒来了

不得已,将原本计划一篇结束的文章分为了如下三部分:

前两篇主要梳理了PMS初始化的流程和初始化的一些细节;最后一篇对应用的安装过程进行了简单梳理。

有没有注意梳理字眼,关于想要深入全面学习PMS的同学只能非常抱歉,本系列文章也仅仅是起到的主要流程的梳理作用。

我们先从整体上掌握,等后面遇到相关需求再来仔细研究吧(这波安慰很及时。。。。)

整个模块学习下来,简单对PackageManagerService吐槽一下:

  • PMS中涉及的类比较多,真正掌握需要一番时间和精力
    • 相关的类像:Package相关、Session相关、Settings等等
  • 阅读PMS的方法有点拆俄罗斯套娃的感觉,一层又一层。。。。
  • 业务逻辑复杂,几乎每个方法的执行会夹杂着各种权限用户的逻辑判断

有了上面的预期,欢迎来到PackageManagerService的世界

了解PackageManagerService

PackageManagerService代码行数在24000行。。。。。

Android的应用管理主要是通过PackageManagerService来完成的。PackageManagerService负责各种APK包的安装、卸载、优化和查询。

Android中的应用可以简单分为两大类:系统应用普通应用

  • 系统应用是指位于/system/app/system/priv-app目录下的应用
    • /system/priv-app是从Android 4.4开始出现的目录,存放的是一些系统底层的应用,如:SettingsSystemUI
    • /system/app存放的则是一些系统级的应用,如CalendarContacts
  • 普通应用是用户安装的应用,位于目录/data/app

PackageManagerService在启动时会扫描所有APK文件和Jar包,然后把它们的信息读取到内存中,这样系统在运行时就能迅速找到各种应用和组件信息。

  • 扫描过程如果遇到没有优化的文件,还要执行优化操作。

    Android 5.0开始引入了ARTART 使用预先 (AOT) 编译,并且从 Android 7.0开始结合使用 AOT即时 (JIT) 编译配置文件引导型编译来优化应用的启动和执行效率

不着急直接分析源代码,我们先从使用的角度简单了解下

在应用中,如果要使用PackageManagerService服务,通常是调用ContextgetPackageManager()方法,内容如下:

    public PackageManager getPackageManager() {
   
   
        if (mPackageManager != null) {
   
   
            return mPackageManager;
        }
        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
   
   
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }

调用到了ActivityThread.getPackageManager()的方法:

    public static IPackageManager getPackageManager() {
   
   
        if (sPackageManager != null) {
   
   
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;
    }

从这两个getPackageManager()方法的代码中可以看到:

  • 返回的是ApplicationPackageManager对象,这个对象创建时使用了IPackageManager对象作为参数

    • IPackageManager对象是PackageManagerServiceBinder代理对象
  • ApplicationPackageManager继承自PackageManager,在PackageManager中定义了应用可以访问PackageManagerService的所有接口

PackageManager关系图如下:

image

而对于PackageManagerService类来说,有两个重要成员变量:

  • mInstallerService:类PackageInstallerService的实例对象。Android通过PackageInstallerService来管理应用的安装过程。

    • PackageInstallerService也是一个Binder服务,对应的代理对象是PackageInstaller
  • mInstaller:类Installer的实例对象。类结构相对简单,有一个IInstalld mInstalld变量,通过Binder调用来和installd进程通信

    • 实际上系统中进行apk文件格式转换、建立数据目录等工作最后都是由installd进程来完成的

上述这几个对象之间的关系图如下:

image

Settings类和packages.xml

在开始分析PackageManagerService前,我们要先看下Settings类,这个类用来保存和PackageManagerService相关的一些设置,它保存的内容在解析应用时会用到。

先看看com.android.server.pm.Settings类的构造方法:

    Settings(File dataDir, PermissionSettings permission, Object lock) {
   
   
        // 省略权限相关的处理
        ......
        // 在data目录下创建system目录
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
        // 设置目录的属性为0775
        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");
    }

Settings类的构造方法会在/data目录下创建system目录用来保存一些系统配置文件。创建了5个位于目录/data/systemFile对象:

  • packages.xml:记录系统中所有安装的应用信息,包括基本信息、签名和权限。
  • packages-backup.xmlpackages.xml文件的备份
  • packages.list:保存普通应用的数据目录和uid等信息
  • packages-stopped.xml:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会将应用的信息记录到文件中
  • packages-stopped-backup.xmlpackages-stopped.xml文件的备份

packages-backup.xmlpackages-stopped-backup.xml是备份文件。关于备份文件的逻辑是:

  • 当Android对文件packages.xmlpackages-stopped.xml写之前,会把他们备份
  • 如果文件写成功了,再把备份文件删除掉
  • 如果写的时候系统出了问题,重启后再需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为原文件可能已经损坏了。

packages.xmlPackageManagerService启动时需要用到的文件,我们先看看文件的格式:

    <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="28" sharedUserId="1001" isOrphaned="true">
        <sigs count="1" schemeVersion="3">
            <cert index="0" />
        </sigs>
        <perms>
            <item name="android.permission.USE_RESERVED_DISK" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS_FULL" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
            <item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>

上面是文件中的一个<package/>标签,表示一个应用的基本信息、签名和声明的权限,从内容来看基本都是解析AndroidManifest.xml的内容。

我们看下主要属性和标签:

  • name表示应用的包名
  • codePath表示apk文件的位置
  • nativeLibraryPath表示应用的native库的储存路径
  • it表示应用安装时间
  • ut表示最后一次修改的时间
  • version表示应用的版本号
  • sharedUserId表示应用用来共享的用户ID
  • userId表示应用所属的用户ID
  • <sign/>表示应用的签名
    • 属性count表示标签中包含有多少个证书
    • 标签<cert/>表示具体证书的值
  • <perms/>表示应用声明使用的权限
    • 每个子标签<item/>代表一项权限

除了<package/>标签,packages.xml中还可能存在一下标签:

  • <updated-package/>:记录系统应用升级的情况。当安装了一个包名相同、版本号更高的应用后,Android 会通过此标签记录被覆盖的系统应用的信息。
  • <renamed-package/>:记录变更应用包名的情况。
    • 应用的包名通常是在AndroidManifest.xml中使用属性package来指定,同时在AndroidManifest.xml中还可以用<original-package>来指定原始包名,对于这种情况:
      • 当设备上存在低版本且包名与原始包名相同的应用时,会升级覆盖原始应用。
      • 最后运行的是新安装的应用,但是运行时应用的包名还是原始的包名。Android会通过<renamed-package/>标签来记录这种改名的情况。
  • <cleaning-package/>:用来记录那些已经删除,但是数据目录还暂时保留的应用的信息

对于<package/><updated-package/>标签,解析后的数据将保存在PackageSetting的对象中。PackageSetting类的继承关系图如下:

image

  • PackageSetting继承了PackageSettingBase类,PackageSettingBase又继承了SettingBase
    • 应用的基本信息保存在PackageSettingBase类的成员变量中
    • 申明的权限保存在SettingBasemPermissionState
  • 签名信息保存在SharedUserSetting类的成员变量signatures
  • SharedUserSetting用来描述具有相同sharedUserId的应用信息
    • 它的成员变量packages是用来保存所有具有相同sharedUserId应用对象的集合
    • 这些应用的签名是相同的,因此只需要通过一个成员变量signatures保存即可

代表标签<package/>PackageSetting对象都会保存在Settings的成员变量mPackages中,定义如下:

final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();

代表标签<updated-package/>PackageSetting对象都会保存在Settings的成员变量mDisabledSysPackages中,定义如下:

private final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<String, PackageSetting>();

代表标签<cleaning-package/>PackageSetting对象都会保存在Settings的成员变量mPackagesToBeCleaned中,定义如下:

final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();

代表标签<renamed-package/>PackageSetting对象都会保存在Settings的成员变量mRenamedPackages中,定义如下:

private final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();

这四个对象在PackageManagerService中会经常遇到,为了方便后面的理解,请记一下哈

PackageManagerService的初始化

有了前面的铺垫,我们来看下PackageManagerService的初始化过程。

PackageManagerService是在SystemServer中初始化的,分别在startBootstrapServices()startOtherServices()中都进行了一些处理。相关代码如下:

private void startBootstrapServices() {
   
   
    ......
    // 启动 installd,PMS依赖此服务
    Installer installer = mSystemServiceManager.startService(Installer.class);
    ......
    // 判断vold.decrypt是否被设定为加密状态
    // mOnlyCore=true表示加密状态,只能处理系统应用
    // 一般情况mOnlyCore为false
    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;
    }
    // 调用main()初始化PMS
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值