Android重学系列 PackageManagerService的启动与安装(上)

前言

PackageManagerService 是Android系统中对所有apk包的管理服务中心,之后我将成其为PMS。PMS除了管理所有已经安装好的apk包的数据,还包含了安装apk的服务,让我们一探究竟。

正文

PMS的启动

PMS的启动,从SystemServer开始,更加详细的原理可以去SystemServer到Home的启动下阅读:

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

    private void startBootstrapServices() {
...
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
...

    }

    private void startOtherServices() {
...
        if (!mOnlyCore) {
            try {
                mPackageManagerService.updatePackagesIfNeeded();
            } catch (Throwable e) {
                reportWtf("update packages", e);
            }
            traceEnd();
        }
...
        try {
            mPackageManagerService.performFstrimIfNeeded();
        } catch (Throwable e) {
            reportWtf("performing fstrim", e);
        }
...
        mPackageManagerService.systemReady();
...
        mActivityManagerService.systemReady(() -> {
...
            mPackageManagerService.waitForAppDataPrepared();
...
       }
    }

在SystemServer的启动依照如下顺序:

  • 1.PackageManagerService.main 将安装服务Intstaller传入,并实例化PMS
  • 2.mPackageManagerService.updatePackagesIfNeeded
  • 3.mPackageManagerService.performFstrimIfNeeded
  • 4.mPackageManagerService. systemReady
  • 5.mPackageManagerService. waitForAppDataPrepared

PackageManagerService.main

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

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

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

在PMS的构造函数中,完成了两个对象的实例化,并加入到ServiceManager中。

  • PackageManagerService
  • PackageManagerNative PackageManagerNative 是PMS的Binder接口对象,我们可以不用看,主要看看PMS本身的实例化都做了什么。

PackageManagerService的实例化

整个构造函数方法很长,我们拆分为几段和大家聊聊:

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);

        mContext = context;

        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
        mMetrics = new DisplayMetrics();
        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();
            mSettings = new Settings(mPermissionManager.getPermissionSettings(), 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);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

....

    }
  • 1.实例化DisplayMetrics对象,这个对象出现过很多次,里面包含了Display的屏幕信息

  • 2.实例化一个PackageManagerInternalImpl对象,这个对象将会作为本地的服务对外提供一些PMS的功能

  • 3.实例化UserManagerService 用户管理服务。在Android系统中是一个多用户系统,每一个应用就代表一个用户。而这个服务其实就是管理每一个应用用户相关的权限和信息。

  • 4.实例化PermissionManagerService 动态权限服务,所有的动态权限最终都会到这个服务下进行权限的设置操作,把权限相关的信息写入到一个名字为”package-perms-“+userId的文件中。

  • 5.实例化一个关键的对象,Settings对象。这个对象管理了开机时候需要读取的文件,如记录每一个安装的apk包中所有组件的packages.list,如记录每一个应用动态权限文件。

  • 6.Settings将会添加如下几个公共用户id:

    • SYSTEM_UID 系统
    • RADIO_UID 电话
    • LOG_UID 打印
    • NFC_UID NFC设备
    • BLUETOOTH_UID 蓝牙设备
    • SHELL_UID shell 命令
    • SE_UID selinux
    public static final int SYSTEM_UID = 1000;
    public static final int PHONE_UID = 1001;
    public static final int SHELL_UID = 2000;
    public static final int LOG_UID = 1007;
    public static final int NFC_UID = 1027;
    public static final int BLUETOOTH_UID = 1002;
    public static final int SE_UID = 1068;

我们来看看核心对象Settings是怎么实现的。至于UserManagerService,PermissionManagerService等暂时不再讨论范围内。

Settings 初始化

文件:/frameworks/base/services/core/java/com/android/server/pm/Settings.java

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

    Settings(File dataDir, PermissionSettings permission, Object lock) {
        mLock = lock;
        mPermissions = permission;
        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);

        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;

        // Deprecated: Needed for migration
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }
  • 1.整个PMS的Settings 也就是设置相关的文件都保存在根目录/data 文件夹下。

  • 2.接着在data文件夹下创建一个system文件夹’/data/system’,并为这个文件夹设置只有本进程用户能读写执行,同一个进程用户组也能读写执行,其他进程组只能读或者执行,定义如下:

      public static final int S_IRWXU = 00700;
      public static final int S_IRUSR = 00400;
      public static final int S_IWUSR = 00200;
      public static final int S_IXUSR = 00100;
    
      public static final int S_IRWXG = 00070;
      public static final int S_IRGRP = 00040;
      public static final int S_IWGRP = 00020;
      public static final int S_IXGRP = 00010;
    
      public static final int S_IRWXO = 00007;
      public static final int S_IROTH = 00004;
      public static final int S_IWOTH = 00002;
      public static final int S_IXOTH = 00001;
  • 3.在’/data/system’ 下创建一个配置文件packages.xml,以及一个备份的配置文件packages-backup.xml,该文件将会存储每一个apk包的java代码的文件夹以及so库的文件夹位置

  • 4.创建缓存所有应用相关信息的packages.list文件,并且设置当前的权限是0640,也就是本进程用户能读写,同一个进程(用户)组只能读,其他进程没有任何权限.

  • 5.判断/config/sdcardfs文件是否存在,存在则mKernelMappingFilename

  • 6.packages-stopped.xml维护的是被停掉的应用,packages-stopped-backup.xml则是它的备份信息。

本文关注的重点是关于包存储信息packages.list以及packages.xml,我们扒一扒这文件中存储的是什么东西?

packages.list文件内容

注意在Android 9.0中,已经没有权限打开这个权限。因此我将打开低版本Android 4.3中缓存的数据作为例子:
这里是packages.list文件内容:

com.google.android.location 10018 0 /data/data/com.google.android.location default
com.android.soundrecorder 10038 0 /data/data/com.android.soundrecorder release
com.android.sdksetup 10036 0 /data/data/com.android.sdksetup platform
com.android.defcontainer 10010 0 /data/data/com.android.defcontainer platform
com.android.launcher 10022 0 /data/data/com.android.launcher shared
com.android.smoketest 10047 0 /data/data/com.android.smoketest default
com.android.quicksearchbox 10035 0 /data/data/com.android.quicksearchbox shared
com.android.contacts 10000 0 /data/data/com.android.contacts shared
....

能看到在packages.list可以把这个数据分为如下几个部分:

  • com.google.android.location 包名
  • 10018 这个应用对应的userId,也正是因为记录当前的userId,所以每一次才能保证userId是一致的,保证了在Android系统中可以通过userId正确的找到应用
  • 0当前是否是debug模式,由AndroidManifest.xml中是否设置了android:debuggable
  • /data/data/com.google.android.location 确定了当前的应用存储数据的目录
  • default / release / platform / shared 这些字符串为在mac_permission.xml为每一个进程定义好的seinfo标签,seinfo不是描述文件的安全性,而是用来在seapp_contexts文件中查找对应的类型对象。

mac_permission.xml如下设置:

<signer signature="@PLATFORM" >
  <seinfo value="platform" />
</signer>

<!-- Media key in AOSP -->
<signer signature="@MEDIA" >
  <seinfo value="media" />
</signer>

那么在seapp_contexts文件中有:
···java
user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
···

当 PackageManagerService 安装 App 的时候,它就会根据其签名或者包名查找到对应的 seinfo,并且将这个 seinfo 传递给另外一个守护进程 installed。

这部分属于SELinux的内容了,感兴趣的可以去阅读这一篇文章:SELinux的介绍。总之一句话就是,SELinux就是控制了不同权限的资源只能由对应的不同权限的进程才能访问。

packages.xml 内容
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
  <version sdkVersion="xx" databaseVersion="xx" fingerprint="xxx" />
  <version volumeUuid="xxx" sdkVersion="xx" databaseVersion="xx" fingerprint="xxx"/>
  <permission-trees>
    ...
  </permission-trees>
  <permissions>
     ...
  </permissions>
  <package ...>
    ...
  </package>

  <shared-user ...>
    ...
  </shared-user>

<packages>

在packages大标签中,分为如下几个部分:

  • permissions 里面包含如<item name="android.permission.ACCESS_NETWORK_STATE" package="android" /> 。permissions定义了所有在Android系统中当前的系统和App权限。可以分为两个两类:系统和App应用拥有的权限

  • package 代表了每一个安装在系统中App的应用。

     <package name="com.google.android.location" codePath="/system/app/NetworkLocation.apk" nativeLibraryPath="/data/app-lib/NetworkLocation" flags="4767301" ft="15b3647e0e0" it="15b3647e0e0" ut="15b3647e0e0" version="1110" sharedUserId="10018">
          <sigs count="1">
              <cert index="0" key="30820...." />
          </sigs>
      </package>

    该package标签包含了如下内容:

    • 1.name 包名
    • 2.codePath apk安装路径.主要是/system/app/data/app两种
    • 3.nativeLibraryPath 是so文件保存的位置
    • 4.userId 是当前应用的userId
    • 5.sigs 签名内容
  • shared-user标签包含如下内容

      <shared-user name="com.google.android.apps.maps" userId="10026">
          <sigs count="1">
              <cert index="0" />
          </sigs>
          <perms>
              <item name="android.permission.NFC" />
              <item name="android.permission.READ_EXTERNAL_STORAGE" />
              <item name="android.permission.USE_CREDENTIALS" />
              <item name="android.permission.WRITE_EXTERNAL_STORAGE" />
              <item name="android.permission.ACCESS_WIFI_STATE" />
              <item name="android.permission.ACCESS_COARSE_LOCATION" />
              <item name="android.permission.GET_ACCOUNTS" />
              <item name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
              <item name="android.permission.DISABLE_KEYGUARD" />
              <item name="android.permission.INTERNET" />
              <item name="android.permission.ACCESS_FINE_LOCATION" />
              <item name="android.permission.MANAGE_ACCOUNTS" />
              <item name="android.permission.VIBRATE" />
              <item name="android.permission.ACCESS_NETWORK_STATE" />
          </perms>
      </shared-user>

    shared-user这标签就是指能够访问共享的进程。 com.google.android.apps.maps就是这个共享进程的包名,userId 是指当前进程的userId,以及perms是指这个进程中的权限

PMS实例化第二段

        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
                installer, mInstallLock);
        mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock,
                dexManagerListener);
        mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

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

        getDefaultDisplayMetrics(context, mMetrics);


        SystemConfig systemConfig = SystemConfig.getInstance();
        mAvailableFeatures = systemConfig.getAvailableFeatures();


        mProtectedPackages = new ProtectedPackages(mContext);

        synchronized (mInstallLock) {

        synchronized (mPackages) {
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            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);
            }

            SELinuxMMAC.readInstallPolicy();


            FallbackCategoryProvider.loadFallbacks();


            mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));

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

            if (mFirstBoot) {
                requestCopyPreoptedFiles();
            }

....       
...
  • 1.构造了一个PackageDexOptimizer对象,这个对象将会操作Installer对象,对dex文件进行优化成odex文件。odex文件是经过dex文件的优化,进行一些提前的校验,切换18种指令为更加高效的指令,构建vtable 虚方法table等。之后有机会会解析dex2oat,实际上其实dex2oat 几乎也完成了dexopt的工作。

  • 2.构造了DexManager对象,用于控制PackageDexOptimizer对象,是Dex优化管理器。

  • 3.构建ArtManagerService对象,这是一个Binder对象。开放给其他服务,在运行时进行art编译处理。

  • 4.创建一个ServiceThread对象,这是一个HandlerThread对象。这就是一个带着Looper的线程,可以把Looper赋值给PackageHandler,创建PMS中的异步线程Handler对象。

  • 5.创建一个WatchDog,监听PMS的死锁等情况

  • 6.从系统配置systemConfig中,获取系统允许共享出来的共享库,保存在mSharedLibraries中。

  • 7.调用readLPw读取保存在系统中所有安装的packages.xml的包中所有的信息,通过返回值确定是否是第一次启动PMS。读取完所有的所有的包后,从Settings的包集合判断这些包中是否还包含代码路径,调用enableSystemPackageLPw方法,处理是否是保存在mDisabledSysPackages集合中,也就是禁止使用的系统应用,如果存在则重新添加,不存则返回。

  • 8.如果是第一次启动PMS,则调用requestCopyPreoptedFiles方法。

核心方法是readLPw。

Settings readLPw

文件:/frameworks/base/services/core/java/com/android/server/pm/Settings.java

    boolean readLPw(@NonNull List<UserInfo> users) {
        FileInputStream str = null;
        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");
                if (mSettingsFilename.exists()) {

                    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) {
                if (!mSettingsFilename.exists()) {
                    mReadMessages.append("No settings file found\n");
                    PackageManagerService.reportSettingsProblem(Log.INFO,
                            "No settings file; creating initial state");

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

            if (type != XmlPullParser.START_TAG) {
...
                return false;
            }

            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();
                if (tagName.equals("package")) {
                    readPackageLPw(parser);
                } else if (tagName.equals("permissions")) {
...
                } else if (tagName.equals("permission-trees")) {
...
                } else if (tagName.equals("shared-user")) {
...
                } else if (tagName.equals("preferred-packages")) {
                } else if (tagName.equals("preferred-activities")) {

...
                } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
...
                } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
                 ...
                } else if (tagName.equals(TAG_DEFAULT_BROWSER)) {
...
                } else if (tagName.equals("updated-package")) {
...
                } else if (tagName.equals("cleaning-package")) {
....
                } else if (tagName.equals("renamed-package")) {
....
                } else if (tagName.equals("restored-ivi")) {
....
                } else if (tagName.equals("last-platform-version")) {
....
                } else if (tagName.equals("database-version")) {
....
                } else if (tagName.equals("verifier")) {
...
                } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
...
                } else if (tagName.equals("keyset-settings")) {
...
                } else if (TAG_VERSION.equals(tagName)) {
...
                } else {
 ...
                }
            }

            str.close();

        } catch (XmlPullParserException e) {
     ...
        } catch (java.io.IOException e) {
...
        }

        if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {
            final VersionInfo internal = getInternalVersion();
            if (!Build.FINGERPRINT.equals(internal.fingerprint)) {
                for (UserInfo user : users) {
                    mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id);
                }
            }
        }

        final int N = mPendingPackages.size();

        for (int i = 0; i < N; i++) {
            final PackageSetting p = mPendingPackages.get(i);
            final int sharedUserId = p.getSharedUserId();
            final Object idObj = getUserIdLPr(sharedUserId);
            if (idObj instanceof SharedUserSetting) {
                final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
                p.sharedUser = sharedUser;
                p.appId = sharedUser.userId;
                addPackageSettingLPw(p, sharedUser);
            } else if (idObj != null) {
         ...
            } else {
 ....
            }
        }
        mPendingPackages.clear();

        if (mBackupStoppedPackagesFilename.exists()
                || mStoppedPackagesFilename.exists()) {
            // Read old file
            readStoppedLPw();
            mBackupStoppedPackagesFilename.delete();
            mStoppedPackagesFilename.delete();
            // Migrate to new file format
            writePackageRestrictionsLPr(UserHandle.USER_SYSTEM);
        } else {
            for (UserInfo user : users) {
                readPackageRestrictionsLPr(user.id);
            }
        }

        for (UserInfo user : users) {
            mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
        }


        final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
        while (disabledIt.hasNext()) {
            final PackageSetting disabledPs = disabledIt.next();
            final Object id = getUserIdLPr(disabledPs.appId);
            if (id != null && id instanceof SharedUserSetting) {
                disabledPs.sharedUser = (SharedUserSetting) id;
            }
        }

        mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
                + mSharedUsers.size() + " shared uids\n");

        writeKernelMappingLPr();

        return true;
    }
  • 1.首先尝试的查找是否有备份的package.xml数据,存在则说明可能发生过错误,则读取备份文件中的FileStream。并删除了package.xml原来文件

  • 2.不存备份文件,则直接读取packages.xml的FileStream。

  • 3.在这个过程中,就能看到就是一个简单的解析xml文件的过程,每遇到一个标签就进行对应的解析行为。如package信息,权限信息等。

  • 解析完所有的信息后,并开始处理mPendingPackages数据。最后再检测是否存在备份文件或者packages-stopped.xml ,存在两者其一,则读取packages-stopped.xml中的数据,并把备份数据重新写入到新的packages.xml文件中。

值得注意的是解析标签package,在介些这个标签的时候执行了readPackageLPw方法对package标签进一步的解析:

 else if (userId > 0) {
                packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                        new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
                        secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
                        pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
                        null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
...
                } else {
                    packageSetting.setTimeStamp(timeStamp);
                    packageSetting.firstInstallTime = firstInstallTime;
                    packageSetting.lastUpdateTime = lastUpdateTime;
                }
            }

调用addPackageLPw添加到缓存中。

addPackageLPw
    PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
            String legacyNativeLibraryPathString, String primaryCpuAbiString,
            String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
            pkgFlags, int pkgPrivateFlags, String parentPackageName,
            List<String> childPackageNames, String[] usesStaticLibraries,
            long[] usesStaticLibraryNames) {
        PackageSetting p = mPackages.get(name);
        if (p != null) {
            if (p.appId == uid) {
                return p;
            }
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate package, keeping first: " + name);
            return null;
        }
        p = new PackageSetting(name, realName, codePath, resourcePath,
                legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
                childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
        p.appId = uid;
        if (addUserIdLPw(uid, p, name)) {
            mPackages.put(name, p);
            return p;
        }
        return null;
    }

很简单,就是根据当前的路径名,资源文件路径,代码文件路径,so库路径生成一个App应用PackageSetting的配置内存文件,保存到mPackages中。

PMS 实例化第三段

            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");


            File frameworkDir = new File(Environment.getRootDirectory(), "framework");

            final VersionInfo ver = mSettings.getInternalVersion();
            mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);


            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;


            mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;

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


            if (mPromoteSystemApps) {
...
            }

            mCacheDir = preparePackageParserCache(mIsUpgrade);


            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;

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


            scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);
            scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT,
                    0);

            mParallelPackageParserCallback.findStaticOverlayPackages();


            scanDirTracedLI(frameworkDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_NO_DEX
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRIVILEGED,
                    0);

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


            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirTracedLI(systemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM,
                    0);

            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
            }
            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
            }
            scanDirTracedLI(vendorAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR,
                    0);


            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
            }
            scanDirTracedLI(privilegedOdmAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_VENDOR
                    | SCAN_AS_PRIVILEGED,
                    0);


            File odmAppDir = new File(Environment.getOdmDirectory(), "app");
            try {
                odmAppDir = odmAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            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");
            scanDirTracedLI(oemAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_OEM,
                    0);

            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
            }
            scanDirTracedLI(privilegedProductAppDir,
                    mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM_DIR,
                    scanFlags
                    | SCAN_AS_SYSTEM
                    | SCAN_AS_PRODUCT
                    | SCAN_AS_PRIVILEGED,
                    0);


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

解析来这一段的工作实际上就是给第三方厂商的提供的包名应用,提供的服务通过scanDirTracedLI方法,把整个包的数据解析扫描到PMS的内存。

这里就有如下几个大目录:

  • 1.mCacheDir 首先通过preparePackageParserCache方法获取当前PMS下扫描结果的缓存目录:/data/system/package_cache/ 所有的包扫描的结果都会缓存到这里

  • 2./vendor/overlay

  • 3./product/overlay 第2和第3点都是第三方厂商提供的资源复写目录

  • 4./system/framework Android系统framework层内置提供的java的核心jar包,odex等

  • 5./system/priv-app,/system/app ,这里面提供了Android系统或者厂商默认的系统应用

  • 6./vendor/priv-app,/vendor/app 这是交给硬件厂商的目录,允许他们内置内置一些系统应用服务。我之前常说的hal层,就是在这个vendor目录安装提供的。

  • 7./odm/priv-app,/odm/app 可以看作是vendor目录的一种延伸。

原始设计制造商 (ODM) 能够为其特定设备(开发板)自定义系统芯片 (SoC) 供应商板级支持包 (BSP).这样,他们就可以为板级组件、板级守护进程或者其基于硬件抽象层 (HAL) 的自有功能实现内核模块。他们可能还需要替换或自定义 SoC 组件。

我们不是搞hal层的,没必要进一步探讨了。

  • 8./oem/app/product/priv-app,/product/app

OEM 会自定义 AOSP 系统映像,以实现自己的功能并满足运营商的要求

product分区则是从Android 9.0开始支持的分区。oem是老版本的product分区,product可以依赖oem分区。product可以多次刷新,oem不可刷新只能出厂一次。这两个分区就是支持自定义 AOSP 系统映像,product的分区出现能够更加灵活多语言多地区的系统映像。

能发现每一个目录下,都调用了整个PMS最核心的方法scanDirTracedLI 对apk,jar包的解析方法。

scanDirTracedLI这个方法我们稍后再看,现在我们可以得知这个方法执行后,PMS就能知道安装apk包中具体的信息了,并把解析出来的PackageParser.Package对象保存在PMS全局变量mPackages中

PMS 第四段


            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();

            final List<String> stubSystemApps = new ArrayList<>();
            if (!mOnlyCore) {

                final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
                while (pkgIterator.hasNext()) {
                    final PackageParser.Package pkg = pkgIterator.next();
                    if (pkg.isStub) {
                        stubSystemApps.add(pkg.packageName);
                    }
                }

                final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
                while (psit.hasNext()) {
                    PackageSetting ps = psit.next();

                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                        continue;
                    }

                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
                    if (scannedPkg != null) {

                        if (mSettings.isDisabledSystemPackageLPr(ps.name)) {

                            removePackageLI(scannedPkg, true);
                            mExpectingBetter.put(ps.name, ps.codePath);
                        }

                        continue;
                    }

                    if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
                        psit.remove();


                    } else {

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

...
            if (!mOnlyCore) {

                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
                        | PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);

                for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
                    PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
                    mSettings.removeDisabledSystemPackageLPw(deletedAppName);
                    final String msg;
                    if (deletedPkg == null) {

                    } else {

                        msg = "Updated system package + " + deletedAppName
                                + " no longer exists; revoking system privileges";


                        final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
                        deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
                        deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
                    }
                    logCriticalInfo(Log.WARN, msg);
                }


                for (int i = 0; i < mExpectingBetter.size(); i++) {
                    final String packageName = mExpectingBetter.keyAt(i);
                    if (!mPackages.containsKey(packageName)) {
                        final File scanFile = mExpectingBetter.valueAt(i);


                        final @ParseFlags int reparseFlags;
                        final @ScanFlags int rescanFlags;
                        if (FileUtils.contains(privilegedAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(systemAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM;
                        } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
                                || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_VENDOR
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(vendorAppDir, scanFile)
                                || FileUtils.contains(odmAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_VENDOR;
                        } else if (FileUtils.contains(oemAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_OEM;
                        } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRODUCT
                                    | SCAN_AS_PRIVILEGED;
                        } else if (FileUtils.contains(productAppDir, scanFile)) {
                            reparseFlags =
                                    mDefParseFlags |
                                    PackageParser.PARSE_IS_SYSTEM_DIR;
                            rescanFlags =
                                    scanFlags
                                    | SCAN_AS_SYSTEM
                                    | SCAN_AS_PRODUCT;
                        } else {
                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                            continue;
                        }

                        mSettings.enableSystemPackageLPw(packageName);

                        try {
                            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
                        } catch (PackageManagerException e) {
                            Slog.e(TAG, "Failed to parse original system package: "
                                    + e.getMessage());
                        }
                    }
                }

                decompressSystemApplications(stubSystemApps, scanFlags);

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

                final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
                final int dataPackagesCount = mPackages.size() - systemPackagesCount;

                if (mIsUpgrade && dataPackagesCount > 0) {
                    MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
                            ((int) dataScanTime) / dataPackagesCount);
                }
            }
            mExpectingBetter.clear();


            mStorageManagerPackage = getStorageManagerPackageName();


            mSetupWizardPackage = getSetupWizardPackageName();
            if (mProtectedFilters.size() > 0) {

                for (ActivityIntentInfo filter : mProtectedFilters) {
                    if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {

                        continue;
                    }

                    filter.setPriority(0);
                }
            }

            mSystemTextClassifierPackage = getSystemTextClassifierPackageName();

            mDeferProtectedFilters = false;
            mProtectedFilters.clear();

            updateAllSharedLibrariesLPw(null);

....

            mPackageUsage.read(mPackages);
            mCompilerStats.read();

...

            mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> {
                TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
                        Trace.TRACE_TAG_PACKAGE_MANAGER);
                traceLog.traceBegin("AppDataFixup");
                try {
                    mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL,
                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                } catch (InstallerException e) {
                    Slog.w(TAG, "Trouble fixing GIDs", e);
                }
                traceLog.traceEnd();

                traceLog.traceBegin("AppDataPrepare");
                if (deferPackages == null || deferPackages.isEmpty()) {
                    return;
                }
                int count = 0;
                for (String pkgName : deferPackages) {
                    PackageParser.Package pkg = null;
                    synchronized (mPackages) {
                        PackageSetting ps = mSettings.getPackageLPr(pkgName);
                        if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
                            pkg = ps.pkg;
                        }
                    }
                    if (pkg != null) {
                        synchronized (mInstallLock) {
                            prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags,
                                    true /* maybeMigrateAppData */);
                        }
                        count++;
                    }
                }
                traceLog.traceEnd();
                Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages");
            }, "prepareAppData");

...
            mSettings.writeLPr();
...


            final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
            final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
            for (int userId : currentUserIds) {
                userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
            }
            mDexManager.load(userPackages);
            if (mIsUpgrade) {
                MetricsLogger.histogram(null, "ota_package_manager_init_time",
                        (int) (SystemClock.uptimeMillis() - startTime));
            }
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)


        Runtime.getRuntime().gc();

        mInstaller.setWarnIfHeld(mPackages);
  • 1.扫描所以在上面安装好系统apk等文件,查找哪些系统禁止的包名,则调用removePackageLI从缓存中移除。删除所有的临时包文件

  • 2.接下来扫描我们应用开发最重要的2个目录:/data/app,/data/app-private.

/data/app 是app安装的路径。所有的app都会安装到这个目录下,可以进进一步的通过对应的包名找到我们的apk应用中的代码等数据。/data/app-private这是每一个应用存储除了代码和资源的其他私密数据。

  • 3.在扫描app安装目录之后,遍历possiblyDeletedUpdatedSystemApps,看看有没有那个apk是需要删除,则调用removeDisabledSystemPackageLPw 从系统配置中移除。这个possiblyDeletedUpdatedSystemApps集合就是系统设置的禁用包集合

  • 4.扫描mExpectingBetter集合中保存的apk包。这个集合说明的是app中有更加新的版本,期望进行更新,所以会进行扫描替换原来的app应用

  • 5.调用Settings的writeLPr方法。更新package.list中的安装包数据列表。

  • 6.通过SystemServerInitThreadPool启动一个特殊的线程池,赋值为mPrepareAppDataFuture对象。执行了如下内容:

    • 1.调用了Installd的fixupAppData方法,创建一个/data/user/用户id/data/data目录,这个目录由StoreManagerService进行管理。这里要和每一个应用的userId要区分开,其实是指登陆Android不同的用户。也就是我们常见的/data/user/0./data/user/用户id/data/data的软链接。不同的用户id只能访问到不同userid对应的app安装内容。可以认为其实每一个app安装的实际路径是/data/user/用户ID/包名/。而我们常见到的/data/data/包名其实是他的软连接。

    • 2.调用prepareAppDataAndMigrateLIF方法,准备应用数据。最终会调用到prepareAppDataLeafLIF方法中:

  private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
...
        try {
            ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
                    appId, seInfo, app.targetSdkVersion);
        } catch (InstallerException e) {
        ....
        }

        if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
            mArtManagerService.prepareAppProfiles(pkg, userId);
        }

...

        prepareAppDataContentsLeafLIF(pkg, userId, flags);
    }
  • 1.Installer的createAppData 实际上就是遍历所有的包名,为每一个包名创建一个cache以及code_cache的目录,用于缓存编译优化后的结果。

    • 2.prepareAppProfiles,这个方法最后调用了IInstalld的prepareAppProfile方法,并且调用保存在/system/bin/profman 这个程序,在程序目录下生成一个.prof文件,这个文件可以加速dex2oat编译优化的速度。
  • 3.prepareAppDataContentsLeafLIF 核心就是调用了IInstalld的linkNativeLibraryDirectory。其实就是把app的安装so库的目录/data/data/包名/lib/data/user/用户id/包名/lib链接上。
  • 7.初始化数据存储服务,InstantApp的扫描,以及让DexManager检查持有每一个分配了用户id的应用的代码路径,保存在PackageDexUsage中。

到这里PMS的实例化,大体上都过了一遍,能看到实际上PMS就是在引导时候,把所有之后Android需要使用的代码包都进行了扫描,并且加载了所有包的配置等文件。其中扫描最为重要,扫描核心方法就是scanDirTracedLI。

暂且放一放,我们继续走PMS初始化流程,我们最后回头看看这个方法都做了什么?

PMS updatePackagesIfNeeded

    public void updatePackagesIfNeeded() {
        enforceSystemOrRoot("Only the system can request package update");


        boolean causeUpgrade = isUpgrade();


        boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;


        boolean causePrunedCache = VMRuntime.didPruneDalvikCache();

        if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
            return;
        }

        List<PackageParser.Package> pkgs;
        synchronized (mPackages) {
            pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
        }

        final long startTime = System.nanoTime();
        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                    false /* bootComplete */);
...
    }

这里面做了两件事情:

  • 1.PackageManagerServiceUtils的getPackagesForDexopt方法,对需要在开机时候进行dexopt优化的apk包进行优化。此时会对PMS有一个dex优化的优先级顺序,其顺序依次为:

    • 1.coreApp 也就是系统核心app服务的包最早进行优化
    • 2.其次是哪些需要接受Intent.ACTION_PRE_BOOT_COMPLETED的广播接受者对应的apk包
    • 3.还有被前两种apk依赖的代码 apk包
  • 2.最终循环调用performDexOptUpgrade。其中PackageDexOptimizer对象的performDexOpt方法。这个方法最终会调用Installer的dexopt方法,通知Intsalld服务,也是一个Binder对象,跨进程通信到Intsalld服务执行dexopt,对dex文件进行优化

更加详细的超出本文讨论范围,之后有空在聊dex2oat的时候一起聊了。

PMS performFstrimIfNeeded

    public void performFstrimIfNeeded() {
        enforceSystemOrRoot("Only the system can request fstrim");

        try {
            IStorageManager sm = PackageHelper.getStorageManager();
            if (sm != null) {
                boolean doTrim = false;
                final long interval = android.provider.Settings.Global.getLong(
                        mContext.getContentResolver(),
                        android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                        DEFAULT_MANDATORY_FSTRIM_INTERVAL);
                if (interval > 0) {
                    final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
                    if (timeSinceLast > interval) {
                        doTrim = true;
                    }
                }
                if (doTrim) {
                    final boolean dexOptDialogShown;
                    synchronized (mPackages) {
                        dexOptDialogShown = mDexOptDialogShown;
                    }
                    if (!isFirstBoot() && dexOptDialogShown) {
                        try {
                            ActivityManager.getService().showBootMessage(
                                    mContext.getResources().getString(
                                            R.string.android_upgrading_fstrim), true);
                        } catch (RemoteException e) {
                        }
                    }
                    sm.runMaintenance();
                }
            } else {

            }
        } catch (RemoteException e) {

        }
    }

这里只做了一件事情:获取StorageManagerService对象,判断此时的时间和上一次操作Android系统的存储磁盘最晚的时间差是多少。默认是超过了3天,则调用StorageManagerService的runMaintenance方法,删除哪些不再有效的数据(注意在操作系统中,文件删除不是立即从磁盘中删除,而是把磁盘中的block中的数据,打上一个标记允许其他数据覆盖)。 之后有机会,会在Linux内核中和大家聊聊整个Linux如何管理磁盘的。

PMS systemReady

    public void systemReady() {
        enforceSystemOrRoot("Only the system can claim the system is ready");

        mSystemReady = true;
        final ContentResolver resolver = mContext.getContentResolver();
        ContentObserver co = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange) {
                mWebInstantAppsDisabled =
                        (Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
                                (Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
            }
        };
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
                        .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
                false, co, UserHandle.USER_SYSTEM);
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
                        .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
        co.onChange(true);
...
        sUserManager.systemReady();
        // If we upgraded grant all default permissions before kicking off.
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }

        if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
        }


        synchronized (mPackages) {
            mPermissionManager.updateAllPermissions(
                    StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
                    mPermissionCallback);
        }

        // Kick off any messages waiting for system ready
        if (mPostSystemReadyMessages != null) {
            for (Message msg : mPostSystemReadyMessages) {
                msg.sendToTarget();
            }
            mPostSystemReadyMessages = null;
        }

        // Watch for external volumes that come and go over time
        final StorageManager storage = mContext.getSystemService(StorageManager.class);
        storage.registerListener(mStorageListener);

        mInstallerService.systemReady();
        mDexManager.systemReady();
        mPackageDexOptimizer.systemReady();

        StorageManagerInternal StorageManagerInternal = LocalServices.getService(
                StorageManagerInternal.class);
        StorageManagerInternal.addExternalStoragePolicy(
                new StorageManagerInternal.ExternalStorageMountPolicy() {
            @Override
            public int getMountMode(int uid, String packageName) {
                if (Process.isIsolated(uid)) {
                    return Zygote.MOUNT_EXTERNAL_NONE;
                }
                if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                    return Zygote.MOUNT_EXTERNAL_DEFAULT;
                }
                if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                    return Zygote.MOUNT_EXTERNAL_READ;
                }
                return Zygote.MOUNT_EXTERNAL_WRITE;
            }

            @Override
            public boolean hasExternalStorage(int uid, String packageName) {
                return true;
            }
        });

        // Now that we're mostly running, clean up stale users and apps
        sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);

        mPermissionManager.systemReady();

        if (mInstantAppResolverConnection != null) {
            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    mInstantAppResolverConnection.optimisticBind();
                    mContext.unregisterReceiver(this);
                }
            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
        }
    }
  • 1.设置了两个两个CP组件的数据变化监听者,分别是Global.ENABLE_EPHEMERAL_FEATURE以及Secure.INSTANT_APPS_ENABLED
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature"
public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled"

这两个标志位共同决定了mWebInstantAppsDisabled 也就是Web的InstantApp是否可以生效。

  • 2.调用UserManager的systemReady

  • 3.注册了StorageManager的监听

  • 4.PackageInstallerService 的systemReady

  • 5.DexManager的systemReady

  • 6.PackageDexOptimizer的systemReady

  • 7.PermissionManagerService的systemReady

  • 8.注册一个Intent.ACTION_BOOT_COMPLETED 系统系统启动完成的广播

PMS waitForAppDataPrepared

当AMS调用了systemReady之后,说明Android系统其实可以启动第一个App也就是桌面应用了,但是此时会调用waitForAppDataPrepared等待PMS的一些事务完成。

    public void waitForAppDataPrepared() {
        if (mPrepareAppDataFuture == null) {
            return;
        }
        ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
        mPrepareAppDataFuture = null;
    }

其实就是等待mPrepareAppDataFuture的完成。而这个对象在PMS的实例化小结聊过,其实就是为每一个应用包创建对应的软连接和目录。

PMS scanDirTracedLI 扫描应用的原理

对于PMS的启动有了一个总体的概括之后,我们来看看PMS中最为的核心方法没有之一的scanDirTracedLI中做了什么?怎么解析apk包的。

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = scanDir.listFiles();

        try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback)) {
            // Submit files for parsing in parallel
            int fileCount = 0;
            for (File file : files) {
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                if (!isPackage) {
                    // Ignore entries which are not packages
                    continue;
                }
                parallelPackageParser.submit(file, parseFlags);
                fileCount++;
            }

            for (; fileCount > 0; fileCount--) {
                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
                Throwable throwable = parseResult.throwable;
                int errorCode = PackageManager.INSTALL_SUCCEEDED;

                if (throwable == null) {

                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                        renameStaticSharedLibraryPackage(parseResult.pkg);
                    }
                    try {
                        if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
                            scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
                                    currentTime, null);
                        }
                    } catch (PackageManagerException e) {
...
                    }
                } else if (throwable instanceof PackageParser.PackageParserException) {
                    PackageParser.PackageParserException e = (PackageParser.PackageParserException)
                            throwable;
                    errorCode = e.error;

                } else {
...
                }

                // Delete invalid userdata apps
                if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
                        errorCode != PackageManager.INSTALL_SUCCEEDED) {
                    removeCodePathLI(parseResult.scanFile);
                }
            }
        }
    }
  • 把mCacheDir作为参数,构造了一个ParallelPackageParser 并行执行的包解析器。

    class ParallelPackageParser implements AutoCloseable {
    
      private static final int QUEUE_CAPACITY = 10;
      private static final int MAX_THREADS = 4;
    
      private final String[] mSeparateProcesses;
      private final boolean mOnlyCore;
      private final DisplayMetrics mMetrics;
      private final File mCacheDir;
      private final PackageParser.Callback mPackageParserCallback;
      private volatile String mInterruptedInThread;
    
      private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
    
      private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
              "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
    
      ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
              DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
          mSeparateProcesses = separateProcesses;
          mOnlyCore = onlyCoreApps;
          mMetrics = metrics;
          mCacheDir = cacheDir;
          mPackageParserCallback = callback;
      }

    能看到在ParallelPackageParser中存在一个名字为package-parsing-thread的线程池,而这个线程吃最大并行数量为4.以及一个ArrayBlockingQueue,这个同步阻塞队列大小为10.

  • 2.遍历该目录所有的文件,如果判断是可以进行解析的apk包,则调用submit方法,为ParallelPackageParser提交一个解析任务。

      public void submit(File scanFile, int parseFlags) {
          mService.submit(() -> {
              ParseResult pr = new ParseResult();
              try {
                  PackageParser pp = new PackageParser();
                  pp.setSeparateProcesses(mSeparateProcesses);
                  pp.setOnlyCoreApps(mOnlyCore);
                  pp.setDisplayMetrics(mMetrics);
                  pp.setCacheDir(mCacheDir);
                  pp.setCallback(mPackageParserCallback);
                  pr.scanFile = scanFile;
                  pr.pkg = parsePackage(pp, scanFile, parseFlags);
              } catch (Throwable e) {
                  ...
              } finally {
                 ...
              }
              try {
                  mQueue.put(pr);
              } catch (InterruptedException e) {
    ...
              }
          });
      }

    当在线程中开始执行的时候,就会实例化一个新的PackageParser对象,并且调用parsePackage方法执行解析,最后把解析的结果放在ArrayBlockingQueue中,等待获取。ArrayBlockingQueue这个队列很简单,就是一个生产者消费者模式,当没数据想取出的时候会被阻塞,等到数据加入后唤醒。当想要放入任务进行消费,但是满了,就会阻塞不允许放入任务。

而parsePackage方法会调用PackageParser.parsePackage.

PackageParser parsePackage

/frameworks/base/core/java/android/content/pm/PackageParser.java

    public Package parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {
        Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
        if (parsed != null) {
            return parsed;
        }

        if (packageFile.isDirectory()) {
            parsed = parseClusterPackage(packageFile, flags);
        } else {
            parsed = parseMonolithicPackage(packageFile, flags);
        }

        long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
        cacheResult(packageFile, flags, parsed);
...
        }
        return parsed;
    }
  • 1.首先尝试的通过getCachedResult获取是否已经有解析好的缓存数据,有则直接返回Package
  • 2.没有缓存,则判断当前的packageFile是文件夹还是文件:
    • 1.是文件夹则调用parseClusterPackage方法
    • 2.是文件则调用parseMonolithicPackage方法
  • 3.最后通过cacheResult 缓存下来。

我们先跳过缓存的逻辑看看PackageParser是怎么解析的。由于一般存在data/app下都是一个apk文件,所以我们以parseMonolithicPackage为例子

PackageParser parseMonolithicPackage
    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
....

        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
        try {
            final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
            pkg.setCodePath(apkFile.getCanonicalPath());
            pkg.setUse32bitAbi(lite.use32bitAbi);
            return pkg;
        } catch (IOException e) {
...
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
    }

先构造一个DefaultSplitAssetLoader对象,这个对象实际上就是通过ApkAssets进行解析,并获取AssetsManager对象。关于AssetsManager相关的原理可以阅读 资源管理系统系列文章

调用parseBaseApk解析apk.

PackageParser parseBaseApk
  public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        String volumeUuid = null;
        if (apkPath.startsWith(MNT_EXPAND)) {
            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
        }

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = apkFile.getAbsolutePath();


        XmlResourceParser parser = null;
        try {
            final int cookie = assets.findCookieForPath(apkPath);
...
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
            final Resources res = new Resources(assets, mMetrics, null);

            final String[] outError = new String[1];
            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
...

            pkg.setVolumeUuid(volumeUuid);
            pkg.setApplicationVolumeUuid(volumeUuid);
            pkg.setBaseCodePath(apkPath);
            pkg.setSigningDetails(SigningDetails.UNKNOWN);

            return pkg;

        } catch (PackageParserException e) {
...
        } catch (Exception e) {
...
        } finally {
            IoUtils.closeQuietly(parser);
        }
    }

很简单,就是通过findCookieForPath找到apk缓存对应的cookieId,并以此为索引,调用AssetManager.openXmlResourceParser方法,打开AndroidManifest.xml的流,等待后面parseBaseApk的解析

parseBaseApk
    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
        final String splitName;
        final String pkgName;

        try {
            Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
            pkgName = packageSplit.first;
            splitName = packageSplit.second;

            if (!TextUtils.isEmpty(splitName)) {
...
                return null;
            }
        } catch (PackageParserException e) {
...
            return null;
        }

        if (mCallback != null) {
            String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
            if (overlayPaths != null && overlayPaths.length > 0) {
                for (String overlayPath : overlayPaths) {
                    res.getAssets().addOverlayPath(overlayPath);
                }
            }
        }

        final Package pkg = new Package(pkgName);

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);

        pkg.mVersionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        pkg.mVersionCodeMajor = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
        pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
        pkg.baseRevisionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
        pkg.mVersionName = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_versionName, 0);
        if (pkg.mVersionName != null) {
            pkg.mVersionName = pkg.mVersionName.intern();
        }

        pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

        pkg.mCompileSdkVersion = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
        pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
        pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
        if (pkg.mCompileSdkVersionCodename != null) {
            pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
        }
        pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;

        sa.recycle();

        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
    }
  • 1.生成Package对象,获取包名以及 标签做的AndroidManifest分割部分(这种很少用,其实就是bundle模块,允许动态切割资源和包,分配提交市场)。

  • 2.从AndroidManifest解析出版本名,版本号等常用参数设置到App应用中。

  • 3.parseBaseApkCommon进行解析Application标签,uses-permission等同等级数据。

              if (tagName.equals(TAG_APPLICATION)) {
                  if (foundApp) {
                      if (RIGID_PARSER) {
                          outError[0] = "<manifest> has more than one <application>";
                          mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                          return null;
                      } else {
                          Slog.w(TAG, "<manifest> has more than one <application>");
                          XmlUtils.skipCurrentTag(parser);
                          continue;
                      }
                  }
    
                  foundApp = true;
                  if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                      return null;
                  }
              } 

    能看到这里进行了Application数目的校验,只允许一个存在。接着开始解析parseBaseApplication中的组件信息。

parseBaseApplication

这个方法很长,解析所有在Application标签的参数,我们重点关注四大组件是如何解析的

    private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestApplication);

...

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

            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                hasActivityOrder |= (a.order != 0);
                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                        true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                hasReceiverOrder |= (a.order != 0);
                owner.receivers.add(a);

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                hasServiceOrder |= (s.order != 0);
                owner.services.add(s);

            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.providers.add(p);

            }
}

...

}
  • 1.解析activity标签:parseActivity解析出了一个Activity对象,保存到Package的activities集合中
  • 2.解析receiver标签: 还是调用parseActivity方法,解析了一个Activity,保存到Package的receivers集合中
  • 3.解析service标签: 调用了parseService方法,解析出了Service,保存到Package的services集合中
  • 4.解析provider标签: 调用了parseProvider方法,解析出了Provider,保存到Package的providers集合中

这四个方法没什么好说的,都是很简单的解析xml中的内容,接着设置到了对应的结构对象中并返回。

PackageParser缓存原理

每一次解析完成之前都会有进行缓存校验和获取。因为每一次解析apk包中的 AndroidManifest.xml确实比较耗时。

获取和存储分别由两个方法完成:

Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
cacheResult(packageFile, flags, parsed);

我们先来看看cacheResult是怎么存储的。

PackageParser cacheResult

    private String getCacheKey(File packageFile, int flags) {
        StringBuilder sb = new StringBuilder(packageFile.getName());
        sb.append('-');
        sb.append(flags);

        return sb.toString();
    }


    private void cacheResult(File packageFile, int flags, Package parsed) {
        if (mCacheDir == null) {
            return;
        }

        try {
            final String cacheKey = getCacheKey(packageFile, flags);
            final File cacheFile = new File(mCacheDir, cacheKey);

            if (cacheFile.exists()) {
                if (!cacheFile.delete()) {

                }
            }

            final byte[] cacheEntry = toCacheEntry(parsed);

            if (cacheEntry == null) {
                return;
            }

            try (FileOutputStream fos = new FileOutputStream(cacheFile)) {
                fos.write(cacheEntry);
            } catch (IOException ioe) {
                cacheFile.delete();
            }
        } catch (Throwable e) {
            Slog.w(TAG, "Error saving package cache.", e);
        }
    }
  • 通过getCacheKey构建出来的key为包名_flag,也就是包名_0,并在/data/system/package_cache/生成一个对应的文件,也就是/data/system/package_cache/包名_0.

  • 通过toCacheEntry方法,获取获取Package所有的内容,如果为空则返回。

  • 不为空,则通过FileOutputStream把Package存储好的二进制数据全部写到/data/system/package_cache/包名_0文件中。

toCacheEntry系统如何快速获取该文件中所有的内容呢?

toCacheEntry
    protected byte[] toCacheEntry(Package pkg) {
        return toCacheEntryStatic(pkg);

    }

    /** static version of {@link #toCacheEntry} for unit tests. */
    @VisibleForTesting
    public static byte[] toCacheEntryStatic(Package pkg) {
        final Parcel p = Parcel.obtain();
        final WriteHelper helper = new WriteHelper(p);

        pkg.writeToParcel(p, 0 /* flags */);

        helper.finishAndUninstall();

        byte[] serialized = p.marshall();
        p.recycle();

        return serialized;
    }

很有趣:

  • 1.就是通过Parcel,把package写入到Parcel中。

  • 2.调用了WriteHelper的finishAndUninstall方法。

      public static class WriteHelper extends Parcel.ReadWriteHelper {
          private final ArrayList<String> mStrings = new ArrayList<>();
    
          private final HashMap<String, Integer> mIndexes = new HashMap<>();
    
          private final Parcel mParcel;
          private final int mStartPos;
    
          public WriteHelper(Parcel p) {
              mParcel = p;
              mStartPos = p.dataPosition();
              mParcel.writeInt(0); // We come back later here and write the pool position.
    
              mParcel.setReadWriteHelper(this);
          }
    
          @Override
          public void writeString(Parcel p, String s) {
              final Integer cur = mIndexes.get(s);
              if (cur != null) {
                  // String already in the pool. Just write the index.
                  p.writeInt(cur); // Already in the pool.
                  if (DEBUG) {
                      Log.i(TAG, "Duplicate '" + s + "' at " + cur);
                  }
              } else {
                  final int index = mStrings.size();
                  mIndexes.put(s, index);
                  mStrings.add(s);
    
                  p.writeInt(index);
              }
          }
    
          public void finishAndUninstall() {
              // Uninstall first, so that writeStringList() uses the native writeString.
              mParcel.setReadWriteHelper(null);
    
              final int poolPosition = mParcel.dataPosition();
              mParcel.writeStringList(mStrings);
    
              mParcel.setDataPosition(mStartPos);
              mParcel.writeInt(poolPosition);
    
              mParcel.setDataPosition(mParcel.dataSize());
              if (DEBUG) {
                  Log.i(TAG, "Wrote " + mStrings.size() + " strings");
              }
          }
      }

注意因为这里设置了setReadWriteHelper。在Package对象中调用writeToParcel全是writeString,此时调用的是WriteHelper的writeString。所有的数据都将写入到mStrings中。此时调用finishAndUninstall,把list中所有的String统一写入到Parcel中。

  • 3.调用了Parcel的marshall方法,核心方法如下

    static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
    {
      Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
      if (parcel == NULL) {
         return NULL;
      }
    ...
      jbyteArray ret = env->NewByteArray(parcel->dataSize());
    
      if (ret != NULL)
      {
          jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
          if (array != NULL)
          {
              memcpy(array, parcel->data(), parcel->dataSize());
              env->ReleasePrimitiveArrayCritical(ret, array, 0);
          }
      }
    
      return ret;
    }

    就是在jni中通过memcpy,把数据全部拷贝到java层并返回。

同理,我们来看看PackageParser是怎么读取数据的。

PackageParser getCachedResult

    private Package getCachedResult(File packageFile, int flags) {
        if (mCacheDir == null) {
            return null;
        }

        final String cacheKey = getCacheKey(packageFile, flags);
        final File cacheFile = new File(mCacheDir, cacheKey);

        try {
            if (!isCacheUpToDate(packageFile, cacheFile)) {
                return null;
            }

            final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
            Package p = fromCacheEntry(bytes);
            if (mCallback != null) {
                String[] overlayApks = mCallback.getOverlayApks(p.packageName);
                if (overlayApks != null && overlayApks.length > 0) {
                    for (String overlayApk : overlayApks) {
                        // If a static RRO is updated, return null.
                        if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
                            return null;
                        }
                    }
                }
            }
            return p;
        } catch (Throwable e) {
...
            cacheFile.delete();
            return null;
        }
    }
  • 1.能看到同样是构造了/data/system/package_cache/包名_0文件

  • 2.isCacheUpToDate 判断当前的缓存文件是否过期了。

      private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
          try {
    
              final StructStat pkg = android.system.Os.stat(packageFile.getAbsolutePath());
              final StructStat cache = android.system.Os.stat(cacheFile.getAbsolutePath());
              return pkg.st_mtime < cache.st_mtime;
          } catch (ErrnoException ee) {
              if (ee.errno != OsConstants.ENOENT) {
                  Slog.w("Error while stating package cache : ", ee);
              }
    
              return false;
          }
      }

很简单,就是检查apk本身安装的时间,和缓存本身的时间哪个更加老。如果缓存更新。,说明当前的缓存有效。更老了,说明apk版本安装更新了,需要重新读取了

  • 3.readFileAsByteArray 读取文件中所有的内容,并调用fromCacheEntry方法逆向转化为package对象。接着检查overlayapk的的时效和当前的缓存相比较。

总结

到这里就把PMS的启动原理大体上和大家聊完了。老规矩,先上时序图
PMS启动流程.png

在PMS初始化中做了如下的事情:

  • 1.实例化Settings PMS的配置对象,内含packages.listpackages.xml.
    • 1.1.packages.list记录了Android系统中安装的应用列表以及对应的userID
    • 2.2.packages.xml记录了每一个安装包的名字,代码路径,so路径,签名等信息
  • 2.对整个Android系统进行分区,扫描分区中所有的尝试提供的服务以及App安装的包,分为如下区域

  • 2.1./data/system/package_cache/ 所有的包扫描的结果都会缓存到这里,每一个结果的缓存为/data/system/package_cache/包名_0

    • 2.2./vendor/overlay

    • 2.3./product/overlay 第2和第3点都是第三方厂商提供的资源复写目录

    • 2.4./system/framework Android系统framework层内置提供的java的核心jar包,odex等

    • 2.5./system/priv-app,/system/app ,这里面提供了Android系统或者厂商默认的系统应用

    • 2.6./vendor/priv-app,/vendor/app 这是交给硬件厂商的目录,允许他们内置内置一些系统应用服务。我之前常说的hal层,就是在这个vendor目录安装提供的。

    • 2.7./odm/priv-app,/odm/app 可以看作是vendor目录的一种延伸。

    • 2.8./oem/app/product/priv-app,/product/app

    • 2.9./data/app,/data/app-private.所有的应用apk对象都会拷贝到/data/app

    • 2.10.通过Installd. fixupAppData 在/data/user/用户ID/包名/下构造每一个安装后真正的数据保存路径,并把这个目录链接到/data/data/包名

    • 2.11.Installd.createAppData 为每一个包名下创建/data/user/用户ID/包名/cache/data/user/用户ID/包名/code_cache.用于缓存安装过程中编译优化好的文件,如art,odex,vdex,dex等等

    • 2.12. prepareAppDataContentsLeafLIF 会调用Installd. linkNativeLibraryDirectory创建每一个应用so库的扫描目录/data/user/用户id/包名/lib。并把这个目录链接到/data/data/包名/lib

    • 2.13.还会调用/system/bin/profman 程序生成每一个应用包名的/data/data/包名/包名.prof文件用于加速后面的dex2oat的编译优化。

  • 3.在扫描的过程中,就以我们开发安装的应用微粒子。会从/data/app目录下取出apk文件,拿到其中的AndroidManifest.xml,解析里面所有的组件和标签并保存到package对象。并把这个package对象通过Parcel进行序列化,保存到data/system/package_cache/包名_0中。

下一次就会优先从这个缓存中获取,直到出现缓存的文件的修改日期时间比/data/app中保存的apk文件修改时间早,说明apk发生了版本更新,才会重新从/data/app中读取。


 上一篇
Android重学系列 PackageManagerService的启动与安装(下) Android重学系列 PackageManagerService的启动与安装(下)
前言 之前和大家聊完了PMS的启动原理,本文继续介绍当进行安装的时候,PMS做了什么事情。 正文先来看看在Android 中如何吊起安装界面: public static void installApk(Context context,
2020-08-31
下一篇 
Android重学系列 ContentProvider 启动原理 Android重学系列 ContentProvider 启动原理
前言终于来到了四大组件的最后一个了,ContentProvider(之后简称CP)开发中用的不是很多,但这不代表这不重要。很多开源库不少巧妙的思路就是借用CP巧妙实现的,如头条的AutoSize是如何自动获取Context的,360的ReP
  目录