PackageManagerService 源码解析

一.SystemServer创建PackageManagerService

    省略

二.PackageManagerService 构造函数

 2.1 Settings

        mSettings = new Settings(mPackages);
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

    1.创建Settings 对象;

    2.调用mSettings.addSharedUserLPw添加 shared_user_id :

       6种系统的uid:system radio log nfc bluetooth shell。相同        shareUserId的包可以运行在一个进程中。

Settings类在data目录下创建了system目录,然后分别保存了下面文件的句柄(注意: Setting初始化时并未读取packages.xml 等文件中的信息)

1.packages.xml :记录系统中所有安装的应用的信息:

 <package name="com.android.providers.media" codePath="/system/priv-app/MediaProvider" nativeLibraryPath="/system/priv-app/MediaProvider/lib" publicFlags="944291397" privateFlags="8" ft="15659d595e8" it="15659d595e8" ut="15659d595e8" version="800" sharedUserId="10006">
     <sigs count="1">
         <cert index="2" key="308204a830820390a003020102020900f2b98e6123572c4e300d06092a864886f70d0101040500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f6964311
     </sigs>
     <perms>
         <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
         <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
         <item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" />
         <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
         <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
         <item name="android.permission.INTERNET" granted="true" flags="0" />
         <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />
         <item name="android.permission.ACCESS_ALL_DOWNLOADS" granted="true" flags="0" />
         <item name="android.permission.ACCESS_DOWNLOAD_MANAGER" granted="true" flags="0" />
         <item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
         <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
         <item name="android.permission.ACCESS_MTP" granted="true" flags="0" />
         <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
         <item name="android.permission.CLEAR_APP_CACHE" granted="true" flags="0" />
         <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" />
         <item name="android.permission.MODIFY_NETWORK_ACCOUNTING" granted="true" flags="0" />
         <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
         <item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" />
     </perms>
     <proper-signing-keyset identifier="4" />
 </package>

  1.packageName;

  2.codePath;

  3.nativeLibaryPath;

  4.此pkg 拥有的权限相关信息;

2.packages.list :保存普通应用的数据目录和uid信息(uid > 1000 的pkg):

......
com.android.managedprovisioning 10009 0 /data/data/com.android.managedprovisioning platform 3003
com.android.gifviewer 10042 0 /data/data/com.android.gifviewer default none
com.android.dreams.phototable 10054 0 /data/data/com.android.dreams.phototable default none
com.leadcore.telassistant 1000 0 /data/data/com.leadcore.telassistant platform 3002,1023,1015,3003,3001
com.android.noisefield 10049 0 /data/data/com.android.noisefield default none
com.android.smspush 10064 0 /data/data/com.android.smspush default none
com.leadcore.codescan 10029 0 /data/data/com.leadcore.codescan platform 3003
com.android.wallpaper.livepicker 10046 0 /data/data/com.android.wallpaper.livepicker platform none
jp.co.omronsoft.openwnn 10051 0 /data/data/jp.co.omronsoft.openwnn default none
com.android.settings 1000 0 /data/data/com.android.settings platform 3002,1023,1015,3003,3001

......

3.packages-backup.xml (packages.xml的备份) , packages-stopped.xml (被强行停止的PKG 信息) , packages-stopped-backup.xml (packages-stopped.xml 的备份);

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

    Settings(File dataDir, Object lock) {
... ...
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
... ...
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
        final File kernelDir = new File("/config/sdcardfs");
... ...

        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }

  Question: Setting 类是如何管理package 信息的?

  Setting 读取各种xml 文件,将pkg 信息放入 PackageSetting对象中,add 到 Settings.mPackages 这个成员数组中进行统一管理:

private void readPackageLPw(XmlPullParser parser) {

... ...

    packageSetting = new PackageSetting( ... ...

... ...

}

 2.2 SystemConfig

  

        SystemConfig systemConfig = SystemConfig.getInstance();
        mGlobalGids = systemConfig.getGlobalGids();
        mSystemPermissions = systemConfig.getSystemPermissions();
        mAvailableFeatures = systemConfig.getAvailableFeatures();

  PKMS 初始化时创建 systemConfig 对象:

    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), false);
        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), false);
        // Only read features from OEM config
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), true);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), true);
    }

 SystemConfig 的初始化函数基本全部调用readPermissions()读取system/etc/ 下的文件,处理permission 相关信息的录入,获取到的”permission“ 相关的信息都放在Setting.mPermissions 中。

例如/system/etc/permissin/platform.xml:


    <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" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />

 都是系统里permission_name 最原始的定义;

继续分析PKMS 构造函数:

PackageManagerService () {

... ...
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
 
            File dataDir = Environment.getDataDirectory();//存放应用数据目录
            mAppDataDir = new File(dataDir, "data");
            mAppInstallDir = new File(dataDir, "app");//放应用
            mAppLib32InstallDir = new File(dataDir, "app-lib");//native库
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mUserAppDataDir = new File(dataDir, "user");//存放用户数据
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
... ...

            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                    = systemConfig.getPermissions();
            for (int i=0; i<permConfig.size(); i++) {
                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
                BasePermission bp = mSettings.mPermissions.get(perm.name);
                if (bp == null) {
                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                    mSettings.mPermissions.put(perm.name, bp);
                }
                if (perm.gids != null) {
                    bp.gids = appendInts(bp.gids, perm.gids);
                }
            }
 
            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
            for (int i=0; i<libConfig.size(); i++) {
                mSharedLibraries.put(libConfig.keyAt(i),
                        new SharedLibraryEntry(libConfig.valueAt(i), null));
            }
}

  1.创建消息处理线程 PackageHandler;

  2.创建 ”/data/app“ , “/data/data” , “/data/app-lib” , “/data/user” 等目录;

  3.获取SystemConfig初始化时生成的mPermissions 系统全新相关信息,一一对应生成BasePermission 对象,放在Settings.mPermission 中 (此时,Setting 中包含PKMS 解析完成的 所有系统权限相关信息:Settings.mPermission

2.3 readLPw函数

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

Settings的readLPw函数的主要作用就是:检查packages-backup.xml有没有,有这个文件说明在写packages.xml的时候系统出问题了,所以在系统启动的时候就要读备份的想xml文件内容。如果没有这个备份文件再去看packages.xml, 然后再去解析xml文件,把解析出来的内容封装在各个对象中保存在mSettings中各个变量中(此时,Setting 中包含PKMS 解析完成的 所有pkg相关信息:Settings.XXXX

2.4 扫描文件

 2.4.1 dex

            long startTime = SystemClock.uptimeMillis();//记录开始扫描时间
 
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);
 
 ... ...

 
            final ArraySet<String> alreadyDexOpted = new ArraySet<String>();//已经优化的文件集合
 
... ...
            // BOOTCLASSPATH:自定义的一些JAR包 路径集合
            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            
            //SYSTEMSERVERCLASSPATH : framework 和 service 的一些jar 包的集合
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
 
            if (bootClassPath != null) {
                String[] bootClassPathElements = splitString(bootClassPath, ':');
                for (String element : bootClassPathElements) {
                    alreadyDexOpted.add(element);
                }
            } 
            
 
            if (systemServerClassPath != null) {
                String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
                for (String element : systemServerClassPathElements) {

                    alreadyDexOpted.add(element);
                }
            } 
... ...

   上面这段代码就是 把 一些中定义的JAR 包 和 framework 和service 的一些JAR 把 放入”已经dexopt“的数组中,避免再次dex

 

            if (mSharedLibraries.size() > 0) {
             // 处理sharedLibrary 的dex
... ...
                for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                    for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
                        final String lib = libEntry.path;
                        if (lib == null) {
                            continue;
                        }

                        try {
                            int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
                            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                                alreadyDexOpted.add(lib);
                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
                            }
                        } catch (FileNotFoundException e) {
                            Slog.w(TAG, "Library not found: " + lib);
                        } catch (IOException e) {
                            Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                                    + e.getMessage());
                        }
                    }
                }
            }

 上面这段代码就是处理一些sharedLib ,共享库的dex

 

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

...
            alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
...
            alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
...
            String[] frameworkFiles = frameworkDir.list();
            if (frameworkFiles != null) {
...
                for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                    for (int i=0; i<frameworkFiles.length; i++) {

                        File libPath = new File(frameworkDir, frameworkFiles[i]);
                        String path = libPath.getPath();

                        if (alreadyDexOpted.contains(path)) {
                            continue;
                        }

                        if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                            continue;
                        }
                        try {
                            int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
                            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                                mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded, false);
                            }
                        } 
...

上面这段代码: 处理/system/framework 下的dex,仅仅只处理apk 和 jar 文件。

以上,完成 1.中定义JAR 包;2. framework 和service 相关的一些JAR ;3 .sharedLib 库 ; 4./system/framework 下apk 和 jar 文件 的dex 。

 2.4.2 扫描APK 相关

            scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
 
            // Find base frameworks (resource packages without code).
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanFlags | SCAN_NO_DEX, 0);
 
            // Collected privileged system packages.
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
 
            // Collect ordinary system packages.
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            try {
                vendorAppDir = vendorAppDir.getCanonicalFile();
            } catch (IOException e) {
                // failed to look up canonical path, continue with original one
            }
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
            // Collect all OEM packages.
            final File oemAppDir = new File(Environment.getOemDirectory(), "app");
            scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

扫描完APK 后经过一系列处理,调用updatePermissionsLPw函数,更新了 mSettings.mPackages  变量。

2.5 赋予权限,写packages.xml

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            Slog.i(TAG, "Time to scan packages: "
                    + ((SystemClock.uptimeMillis()-startTime)/1000f)
                    + " seconds");
... ...
           updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                    | (regrantPermissions//是否要重新赋权限
                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)

... ...


            // All the changes are done during package scanning.
            mSettings.updateInternalDatabaseVersion();//更新数据库
 
            // can downgrade to reader
            mSettings.writeLPr();//把mSettings写入packages.xml

扫描完APK 后经过一系列处理,调用updatePermissionsLPw函数,更新了 mSettings.mPackages  变量,调用mSettings.writeLPr() 把新的mPackage 信息写入packages.xml 中。

packages.xml的更新

1 .PMS的构造函数先读取保存在packages.xml中的内容保存在mSettings中;

2. 构造函数中扫描设备中几个应用目录下的应用文件,并把扫描结果保存在PMS的mPackages成员变量中;

3. 通过对比mSettings内容是否有被升级包覆盖的系统应用,如果有从mPackages去除。这样mPackages和mSettings的记录一致了,最后将本次扫描内容(mPackages的内容)写到packages.xml文件中。

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