PMS

PackageMangerService源码阅读总结

在这里插入图片描述

简介

PackageMangerService(简称PMS)是Android系统中负责管理所有Apk的安装、卸载更新等工作,其运行在SystemServer进程,维护管理系统中所有的Apk包管理,那它是如何管理众多APK的呢?它怎么知道系统中有哪些APK应用,这些Apk在什么时候安装、卸载和更新,PMS如何感知?就算它知道了系统中所有APK的存在,如何去建立一个维护呢?
带着以上诸多疑问,从源码的角度,来探寻这些问题,感悟PMS的精髓所在!


浅析自己设计一个PMS包管理

在这里插入图片描述

管理对象 — 静态存储在磁盘上的APK文件
管理者 — PMS管理器,系统中是唯一的
目标对象 — 活的应用程序,界面上能显示,能交互

首先,我作为PMS管理者,系统开机BOOT时,要扫描系统中的文件,寻找哪些是APK文件,然后针对处理,但是不可能扫描系统中所有的路径,肯定要事先做好约束,APK文件只能放在某些特定的目录下;还有一点,系统开机后,用户自行安装apk文件时,这个时候PMS不可能又去扫描一遍,最好的方法是提供一个命令接口,用户安装时通过调用这个命令接口,这样PMS就可以拿到用户安装的APK文件,然后分析之,以下几个路径是PMS开机启动时会扫描的几个路径:

system/app 系统自带的应用程序
data/app 用户程序安装的目录
vendor/app 厂商定制app目录
system/priv-app/ 系统私有App,其中service可以保活,一旦被杀死就会立即恢复,缺点无法正常升级

其次,PMS拿到APK文件后,要解析分享APK内容,拿到其中重要的东西,然后再保存到内存或数据库中,内存中用一系列赋值的数据模型建立好每个APK文件的映射,提供给Launcher程序,Launcher根据数据模型可以清楚指定有哪些应用,将其绘制到桌面上去,难点在于解析APK这块,你需要知道APK需要哪些权限、四大组件有哪些,lib动态库,使用的资源res文件等,解析后用内存数据模型维护好它们

实质上分析APK内容主要就是AndroidManifest.xml文件,然后把其中的内容映射到PackageParser.Package、PackageSetting中

最后,PMS在自己的进程里面,需要提供安装、卸载更新接口,其他用户进程通过binder跨进程来调用即可


PMS源码瞧瞧

按照我们上面的设计内容,从源码中去看看它是如何工作的?


PMS启动切入点

位于SystemServer的run方法中,其中调用startBootstrapServices,启动Boot相关的服务:

private void startBootstrapServices() {
	....
	mOnlyCore表示是加密参数,true表示只解析加密的APK
	mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
							mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
	....
}

进入main方法:

public static PackageManagerService main(Context context, Installer installer,                                                                                                                                                                                                       
        boolean factoryTest, boolean onlyCore) {
    // 自检初始化配置参数
    PackageManagerServiceCompilerMapping.checkProperties();
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    m.enableSystemUserPackages();
    把PMS加入到binder服务列表中去,名为package
    ServiceManager.addService("package", m);
    return m;
}

这里就正式进入PackageManager的领域了!


PMS构造方法

为什么要讲他的构造方法?首先,大量的工作内容都是在构造方法中完成的;其次,后续的安装、更新工作也是按照构造方法中这个套路的来执行的,所以它构造方法很重要!

构造方法中,有以下几个事件阶段,如下:

  • BOOT_PROGRESS_PMS_START
  • BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
  • BOOT_PROGRESS_PMS_DATA_SCAN_START
  • BOOT_PROGRESS_PMS_SCAN_END
  • BOOT_PROGRESS_PMS_READY
BOOT_PROGRESS_PMS_START

START阶段做了很多事情,依次阐述,源码如下,精简了部分:

Settings初始化工作,PMS中相关的配置工作类

Settings是整个系统的APK配置管理类,里面维护了所有apk的内存存储模型,mPakcages作为参数传递
进去,mPackages类型为ArrayMap<String, PackageParser.Package>,key为packagename
mSettings = new Settings(mPackages);
添加sharedId内容,拥有相同sharedid可以共享资源,也可以在同一个进程运行,后面会讲到
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);
SystemConfig systemConfig = SystemConfig.getInstance();
解析相应文件(/system/etc/permission和(framework/base/data/etc/下的文件),
包括platform.xml和系统支持的各种硬件模块的feature,
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();

SystemConfig主要工作内容是:

  1. 建立底层的uid和 group id同行上层permission之间的映射,可以指定一个权限与几个id的对应。当一个APK被授予这个权限时,它也同时属于这几个组。

     例如:platform.xml文件内容有 
      <permission name="android.permission.BLUETOOTH" >
    	<group gid="net_bt" />
     </permission>
     即字符串蓝牙权限属于用户组net_bt,SystemConfig读取到蓝牙权限时,会用PermissionEntry实体保存,
     name为android.permission.BLUETOOTH,而gid是字符串,会用Process将gid字符串转换为整型数字保存在
     gids数组;也就是如下:
     public static final class PermissionEntry {                                                                                                                                                                                                                                          
     public final String name;
     public int[] gids;}
     当我们应用程序在AndroidManifest.xml声明了BLUETOOTH权限时,实质就是将我们加入到gids中某个用户
     组里面,这样就能够访问相应的文件
    
  2. 给一些底层用户分配权限,如给shell授予各种permission权限,把一个权限赋予uid,当进程只是用这个uid运行时,就具备了这个权限。

  3. libary,系统增加的一些应用需要link扩展的jar。

  4. feature,系统每增加一个硬件,都要添加相应的feature,将解析结果放入mSystemPermissions,mShareLibrariest,mSettings.mPermissions,mAvailableFeatures等几个集合供系统查询和权限配置使用。

然后看看Settings是个什么东西?其构造方法如下:

//传递进入mPackage当做一把锁
Settings(Object lock) {
	参数一返回的/data目录
    this(Environment.getDataDirectory(), lock);
}
Settings(File dataDir, Object lock) {
    mLock = lock;
    mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
    mSystemDir = new File(dataDir, "system");
    mSystemDir.mkdirs();
    FileUtils.setPermissions(mSystemDir.toString(),
            FileUtils.S_IRWXU|FileUtils.S_IRWXG
            |FileUtils.S_IROTH|FileUtils.S_IXOTH,
            -1, -1); 
    创建一些xml文件
    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");
} 

小结:
从以上源码看,settings主要在data目录下创建一些xml文件,并且后续扫描解析apk后,会把apk信息保存到这些文件中去,

packages.xml 记录了系统中所有安装的应用信息,包括基本信息、签名和权限
packages-backup.xml 对packages.xml文件的备份文件
packages.list 存储了系统中应用的安装信息,主要包含包名、安装位置、UID、Debuggable属性等
packages-stopped.xml 记录系统强行停止的应用信息
packages-stopped-backup.xml 顾名思义,对packages-stopped文件的备份 

插入一个SharedId知识点:
即AndroidManifest.xml里面的sharedUserId,两个应用声明相同的sharedUserId,就可以共享资源,如果签名相同还会运行在同一个进程下面;UID即用户User ID,Android中每个应用程序都有自己的一个UID,不同的UID应用程序之间是进程隔离的,但是如果我们在AndroidManifest.xml里面manifest标签里面声明相同的shareuserid,并且使用相同的签名,则两个应用程序是可以共享资源,并且运行在同一进程里面;

回到本文源码mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED),我们跟进去看看:

final ArrayMap<String, SharedUserSetting> mSharedUsers =                                                                                                                                                                                                                             
        new ArrayMap<String, SharedUserSetting>();
//存放大于10000小于19999的UID的sharedUserSetting
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
//同上,只是保存小于10000的UIDsharedUidSetting
private final SparseArray<Object> mOtherUserIds =
        new SparseArray<Object>();
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {  
	map获取value                                                                                                                                                                                      
    SharedUserSetting s = mSharedUsers.get(name);
    if (s != null) {
    	userId是SharedUserSetting成员,就是UID,这里指如果添加进来的UID和保存的相同就直接返回
        if (s.userId == uid) {
            return s;
        }
        PackageManagerService.reportSettingsProblem(Log.ERROR,
                "Adding duplicate shared user, keeping first: " + name);
        return null;
    }
    创建一个新的SharedUserSetting
    s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
    s.userId = uid;
   	并且把这个uid的应用添加到mOtherUserIds或mUserIds结构体中去
    if (addUserIdLPw(uid, s, name)) {
        mSharedUsers.put(name, s);
        return s;
    }
    return null;
}

private boolean addUserIdLPw(int uid, Object obj, Object name) {
      last为19999    
      if (uid > Process.LAST_APPLICATION_UID) {
          return false;
      }
      first为10000 如果UID大于10000的应用,添加到mUserIds结构中
      if (uid >= Process.FIRST_APPLICATION_UID) {
          int N = mUserIds.size();
          final int index = uid - Process.FIRST_APPLICATION_UID;
          while (index >= N) {
              mUserIds.add(null);
              N++;
          }
          if (mUserIds.get(index) != null) {
              PackageManagerService.reportSettingsProblem(Log.ERROR,
                      "Adding duplicate user id: " + uid
                      + " name=" + name);
              return false;
          }
          mUserIds.set(index, obj);
      小于10000的属于系统应用,添加到mOtherUserIds结构体中去
      } else {
          if (mOtherUserIds.get(uid) != null) {
              PackageManagerService.reportSettingsProblem(Log.ERROR,
                      "Adding duplicate shared id: " + uid
                              + " name=" + name);
              return false;
          }
          mOtherUserIds.put(uid, obj);
      }
      return true;
  }

总结一下,Settings类里面有三个总要成员:
ArrayMap<String, SharedUserSetting> mSharedUsers:string保存了sharedId的描述符,SharedUserSetting保存应用信息
ArrayList<Object> mUserIds:保存UID大于10000的应用程序,通过源码得知,Object保存实质就是SharedUserSetting
SparseArray<Object> mOtherUserIds:保存UID小于10000的应用程序

我们用ps -ef查看我们应用进程时,其UID一般是Ux_axx的形式(如U0_a856),这是描述符,U表示userid,a表示appid,使用Process.myUID获取当前应用的UID,前者的x取值为UID/10000,后者为UID-10000

这里有一个问题?相同sharedID的不同应用在这三个数据模型内是如何保存的?答案就在SharedUserSetting
在这里插入图片描述
如上图,userId是用户ID值,name是其描述,ArraySet<PackageSetting>是拥有相同sharedId的应用,每个应用对应一个PackageSetting,这样系统就知道哪些app拥有相同的sharedId,综上在Settings里面的三个成员数据结构模型可归纳为下图:
在这里插入图片描述

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

扫描启动阶段,它会扫描某些特定目录,解析目录下存在的APK文件,并解析至,获得PackageParser.Package,每个APK对应一个PackageParser.Package,最后在转换生成PackageSetting存储在Settings里面去;过程就是这样,难点在于他如何去解析每一个APK,APK还会分单个APK和集群式APK,分别有不同的解析方法,解析完成后还涉及与旧版本APK对比更新等等!

扫描的路径主要是:

/vendor/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/data/app
/data/app-private
什么是集群式APK?

集群式APK其APK文件不止一个,一般由一个base.apk做基础,其他功能模块由不同split.apk组成,如Google提出App bundle技术就是这类型的apk,无论是单个Apk还是集群式Apk最终解析到内存数据模型都是为一个PackageParser.Package对象

APK解析过程parser

认清目标,解析对象是什么?最终的结果是什么?如下图:
在这里插入图片描述
源码中,解析流程主要是:

a. PackageManagerService.scanDirTracedLI() -> ParallelPackageParser.submit() -> PackageParser.parsePackage()
b. return PackageParser.Package到PackageManagerService
c. PackageManagerService.scanPackageLI(PackageParser.Package) 并得到PackageSetting
d. Settings保存PackageSetting

重点看一下a步骤里面PackageParser.parsePackage() 方法,方法内部又调用了PackageParser.parsePackage方法:

public Package parsePackage(File packageFile, int flags, boolean useCaches)                                                                                                                                                                                                          
        throws PackageParserException {
    有缓存就取缓存    
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }    
    如果file是一个目录,说明是集群式APP,使用parseClusterPackage去解析
    if (packageFile.isDirectory()) {
        parsed = parseClusterPackage(packageFile, flags);
    } else {
    单个App使用parseMonolithicPackage解析
        parsed = parseMonolithicPackage(packageFile, flags);
    }    
	缓存结果
    cacheResult(packageFile, flags, parsed);
    return parsed;
} 

这里我们看parseClusterPackage是如何实现的?

 private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
 解析APK精简内容,只解析apk的Androidmanifest中manifest、Application、
 use-split和package-verify标签下的属性
        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
        if (mOnlyCoreApps && !lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + packageDir);
        }

        // Build the split dependency tree.
        SparseArray<int[]> splitDependencies = null;
        final SplitAssetLoader assetLoader;
        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
            try {
                每个包apk依赖了其他另外的包,建立好他们的映射关系,即用到了use-split标签
                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
            }
        } else {
            assetLoader = new DefaultSplitAssetLoader(lite, flags);
        }

        try {
          //就是当前基础路径packageDir
            final File baseApk = new File(lite.baseCodePath);
            //解析baseApk中的AndroidManifest所有标签
            final Package pkg = parseBaseApk(baseApk, assets, flags);
            if (pkg == null) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                        "Failed to parse base APK: " + baseApk);
            }

            if (!ArrayUtils.isEmpty(lite.splitNames)) {
                final int num = lite.splitNames.length;
                pkg.splitNames = lite.splitNames;
                pkg.splitCodePaths = lite.splitCodePaths;
                pkg.splitRevisionCodes = lite.splitRevisionCodes;
                pkg.splitFlags = new int[num];
                pkg.splitPrivateFlags = new int[num];
                pkg.applicationInfo.splitNames = pkg.splitNames;
                pkg.applicationInfo.splitDependencies = splitDependencies;

                for (int i = 0; i < num; i++) {
                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
                    //依次解析每个分包apk的AndroidManiesf文件,split apk中主要解析四大组件,其他如权限方面在parseBaseApk中已经解析
                    //并且base apk和splitapk中组件都是放在Package的成员中,没有特地分开
                    parseSplitApk(pkg, i, splitAssets, flags);
                }
            }

            pkg.setCodePath(packageDir.getAbsolutePath());
            pkg.setUse32bitAbi(lite.use32bitAbi);
            return pkg;
        } finally {
            IoUtils.closeQuietly(assetLoader);
        }
    }

在看看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;

            //基础包base-apk的分包包名必须为空
            if (!TextUtils.isEmpty(splitName)) {
                outError[0] = "Expected base APK, but found split " + splitName;
                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
                return null;
            }
        } catch (PackageParserException e) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }

        if (mCallback != null) {
            //回调到PackageManagerService获取到overlay资源,加入到自己的资源路径,用于替换
            //overlay机制见https://blog.csdn.net/azhengye/article/details/49050631
            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);

        //解析基础包的manifest标签下的基本属性code和name等
        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        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);

        sa.recycle();
		一下方法主要是解析AndroidMainfest.xml中的四大组件权限等标签
        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);

上面mCallback.getOverlayPaths方法,主要是为了实现Android的Overlay机制,实现动态替换资源,具体Overlay简介请点击,最后parseBaseApkCommon就主要是机械AndroidMainfest中相通的一些内容,如四大组件以及权限等

小结:
从源码上来看解析集群式APK主要是解析每个APK的AndroidManifest.xml中的内容,并建立好各个模块APK之间的依赖关系,解析四大组件以及权限等、读取代码位置、安装路径等,最后存放到Package变量中去,所以,最终我们理解透这个Package中的内容即可,它属于PackageParser的内部类,如下

public final static class Package {
        //表示包名
        public String packageName;
        // 表示"拆包"的包名,是个数组,每个元素代表一个"拆分"包名
        public String[] splitNames;
        //对应一个volume的uid
        public String volumeUuid;
        // 表示代码的路径,如果是单个包,则表示"base"的APK的路径,如果是"集群"包,则表示的"集群"包的目录。
        public String codePath;
        // "base APK"的路径
        public String baseCodePath;
       // "拆分 APK"的路径
        public String[] splitCodePaths;
        // "base APK"的调整版本号
        public int baseRevisionCode;
        //"拆分APK"的调整版本号
        public int[] splitRevisionCodes;
        // "拆分APK"的标志数组
        public int[] splitFlags;
        // "拆分APK"的私有标志数组
        public int[] splitPrivateFlags;
        // 是否支持硬件加速
        public boolean baseHardwareAccelerated;
        // 对应ApplicationInfo对象 对应AndroidManifest里面的Application
        public final ApplicationInfo applicationInfo = new ApplicationInfo();
        // APK安装包中 AndroidManifest里面的<Permission>
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
         // APK安装包中 AndroidManifest里面的<PermissionGroup>
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
        // APK安装包中 AndroidManifest里面的<Activity>,这里面的Activity是不是我们通常说的Activity,而是PackageParse的内部类Activity
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        // APK安装包中 AndroidManifest里面的<Receiver>,这里面的Activity是不是我们通常说的Activity,而是PackageParse的内部类Activity
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        // APK安装包中 AndroidManifest里面的<Provider>,这里面的Provider是不是我们通常说的Provider,而是PackageParse的内部类Provider
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
        // APK安装包中 AndroidManifest里面的<Service>,这里面的Service是不是我们通常说的Service,而是PackageParse的内部类Service
        public final ArrayList<Service> services = new ArrayList<Service>(0);
        // APK安装包中 AndroidManifest里面的<Instrumentation>,这里面的Instrumentation是不是我们通常说的Instrumentation,而是PackageParse的内部类Instrumentation
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
        // APK安装包中请求的权限
        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
        // APK安装包中 保内广播的Action
        public ArrayList<String> protectedBroadcasts;
        // APK安装包中 依赖库的名字
        public ArrayList<String> libraryNames = null;
        // APK安装包中 使用库的名字
        public ArrayList<String> usesLibraries = null;
        // APK安装包中 使用选项库的名字
        public ArrayList<String> usesOptionalLibraries = null;
        // APK安装包中 使用库的路径数组
        public String[] usesLibraryFiles = null;
        // APK安装包中 某个Activity信息的集合,在AndroidManifest里面的<preferred>标签(不在receiver里面)
        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
        // APK安装包中 AndroidManifest中对应"original-package"的集合
        public ArrayList<String> mOriginalPackages = null;
        // 是真实包名,通常和mOriginalPackages一起使用
        public String mRealPackage = null;
         // APK安装包中 AndroidManifest中对应"adopt-permissions"集合
        public ArrayList<String> mAdoptPermissions = null;
         // 我们独立的存储应用程序元数据,以避免多个不需要的引用
        public Bundle mAppMetaData = null;
        //  版本号
        public int mVersionCode;
        // 版本名
        public String mVersionName;
         // 共享id
        public String mSharedUserId;
        // 共享用户标签
        public int mSharedUserLabel;
         // 签名
        public Signature[] mSignatures;
        // 证书
        public Certificate[][] mCertificates;
        // dexopt的位置,以便PackageManagerService跟踪要执行dexopt的位置
        public int mPreferredOrder = 0;
         //  需要进行的dexopt的集合
        public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);
        // 最后一次使用pakcage的时间的
        public long mLastPackageUsageTimeInMills;
        // 附加数据 用于指向PackageSetting
        public Object mExtras;
         // 硬件配置信息,对一个AndroidManifest里面的<uses-configuration> 标签
        public ArrayList<ConfigurationInfo> configPreferences = null;
         //特性信息,对一个AndroidManifest里面的<uses-feature> 标签 
        public ArrayList<FeatureInfo> reqFeatures = null;
         //特性组信息,对一个AndroidManifest里面的<feature-group> 标签 
        public ArrayList<FeatureGroupInfo> featureGroups = null;
         // 安装的属性
        public int installLocation;
        // 是否是核心
        public boolean coreApp;
        // 是否是全局必要,所有用户都需要的应用程序,无法为用户卸载
        public boolean mRequiredForAllUsers;
        // 受限账户的 验证类型
        public String mRestrictedAccountType;
          //  账户的类型
        public String mRequiredAccountType;
        // 对应的AndroidManifest项目摘要清单
        public ManifestDigest manifestDigest;
        //AndroidManifest中 对应<overlay> 标签
        public String mOverlayTarget;
        //overlay对应的优先级
        public int mOverlayPriority;
         //  是否是受信任的Overlay
        public boolean mTrustedOverlay;
         // 下面是用来给KeySetManagerService的数据
        public ArraySet<PublicKey> mSigningKeys;  // 签名
        public ArraySet<String> mUpgradeKeySets;  //升级
        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;  //公钥
        // 如果有abi的话,abi覆盖
        public String cpuAbiOverride;

上诉代码中提到的Activity、Service等并不是我们开发中使用到的,而是PackageParser的内部类,想想也可以理解,这里如Activity需要保存Activity的所有信息,如名字、启动模式、Intent过滤等以及和ApplicationInfo之间的关系,这些都不是一个Activity所能保存的;
在这里插入图片描述
在重点看一下cd步骤,如何处理解析后的Package变量? 解析内容太过于复杂,重点参考这篇文章,会先判断apk的签名,是否已经安装等,最后在保存到Settings实例中去,保存到Settings主要是一个PackageSetting对象,主要涉及文章前面提到的mPackages、mSharedUser、mOtherUserIds和mUserIds这三个成员中,调用最终逻辑在:
PackageManagerService.commitPackageSettings() -> Settings.insertPackageSettingLPw() -> Settings.addPackageSettingLPw()将把此次APK的PackageSetting添加到Settings的mPackages里面去,同时根据sharedId描述符添加到对应的mSharedUser里面去,最后根据当前APK的appId大小,将SharedUserSetting替换对应的mOtherUserIds和mUserIds成员中去

这里也是重点看一下PackageSetting是一个什么样的对象:
在这里插入图片描述

 PackageSetting(String name, String realName, File codePath, File resourcePath,
        String legacyNativeLibraryPathString, String primaryCpuAbiString,
        String secondaryCpuAbiString, String cpuAbiOverrideString,
        int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
        List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
        int[] usesStaticLibrariesVersions) {
    super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
            primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
            pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
            usesStaticLibraries, usesStaticLibrariesVersions);
    this.sharedUserId = sharedUserId;
}

从它的构造方法可以看出来,主要包含了一个APP的基本信息,如安装位置,lib位置、资源位置、版本信息、包名等


BOOT_PROGRESS_PMS_DATA_SCAN_START

这个步骤和上一个步骤类似,扫描/data/app和/data/app-private目录下的APK


BOOT_PROGRESS_PMS_SCAN_END

扫描结束,当前系统下所有APK文件已经写入到内存结构模型中去了,主要在Settings的mPackages、mSharedUser、mUserId和mOtherUserId几个成员,现在将要把相关信息写入到文档packages.xml中持久化,所以在PackageManagerService的BOOT_PROGRESS_PMS_SCAN_END步骤会有如下:

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,                                                                                                                                                                                                                 
                    SystemClock.uptimeMillis());
                    
writeLPr将会写入信息到文档packages.xml
mSettings.writeLPr();

void writeLPr() {
....
 try {
 	mSettingsFilename变量是Settings构造方法创建的package.xml文件
      FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
      BufferedOutputStream str = new BufferedOutputStream(fstr);
      //XmlSerializer serializer = XmlUtils.serializerInstance();
      XmlSerializer serializer = new FastXmlSerializer();
      serializer.setOutput(str, StandardCharsets.UTF_8.name());
      serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            serializer.startTag(null, "packages");
            xml序列化所有PackageSetting
 for (final PackageSetting pkg : mPackages.values()) {
                writePackageLPr(serializer, pkg);
            }
            for (final PackageSetting pkg : mDisabledSysPackages.values()) {
                writeDisabledSysPackageLPr(serializer, pkg);
            }
            for (final SharedUserSetting usr : mSharedUsers.values()) {
                serializer.startTag(null, "shared-user");
                serializer.attribute(null, ATTR_NAME, usr.name);
                serializer.attribute(null, "userId",
                        Integer.toString(usr.userId));
                usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
                writePermissionsLPr(serializer, usr.getPermissionsState()
                        .getInstallPermissionStates());
                serializer.endTag(null, "shared-user");
            }
str.flush();
            FileUtils.sync(fstr);
            str.close();

            // New settings successfully written, old ones are no longer
            // needed.
            mBackupSettingsFilename.delete();
            FileUtils.setPermissions(mSettingsFilename.toString(),
                    FileUtils.S_IRUSR|FileUtils.S_IWUSR
                    |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
                    -1, -1);
            return;

        } catch(XmlPullParserException e) {
            Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
                    + "current changes will be lost at reboot", e);
        } catch(java.io.IOException e) {
            Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
                    + "current changes will be lost at reboot", e);
        }
}

以上就把所有apk配置信息写入到文件中去了,后续其他应用需要包信息都可以从这里找到,也可以从Settings的成员变量中找到


BOOT_PROGRESS_PMS_READY

创建安装服务PackageInstallerService,接收来自安装程序如adb、packageinstaller等安装请求


PMS安装APK

什么是安装?

第一步:拷贝文件到指定的目录:
在Android系统中,apk安装文件是会被保存起来的,默认情况下,用户安装的apk首先会被拷贝到/data/app目录下,/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统出场的apk文件则被放到了/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,该分区只有ROOT权限的用户才能访问,这也就是为什么在没有Root手机之前,我们没法删除系统出场的app的原因了。
第二步:解压缩apk,宝贝文件,创建应用的数据目录
为了加快app的启动速度,apk在安装的时候,会首先将app的可执行文件dex拷贝到/data/dalvik-cache目录,缓存起来。然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放在应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等。
第三步:解析apk的AndroidManifest.xml文件

Android系统中,也有一个类似注册表的东西,用来记录当前所有安装的应用的基本信息,每次系统安装或者卸载了任何apk文件,
都会更新这个文件。这个文件位于如下目录:/data/system/packages.xml。系统在安装这个apk的过程中,会解析apk的
AndroidManifest.xml文件,提取出这个apk的重要信息写入到packages.xml文件中,这些信息包括:权限、应用包名、
APK的安装位置、版本、userID等等。由此,我们就知道了为什么一些应用市场和软件管理类的app能够很清楚地知道当前手机
所安装的所有app,以及这些app的详细信息了。另外一件事就是Linux的用户Id和用户组Id,以便他们可以获得合适的运行权限。
以上都是由PackageServcieManager完成的,后面我们会重点介绍PackageServiceManager。

第四步:显示快捷方式
如果这些应用程序在PackageManagerService服务注册好了,如果我们想要在Android桌米上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。在Android系统中,负责把系统中已经安装的应用程序在桌面中展现出来的Home应用就是Launcher了。

PackageHandler

PackageHandler属于PMS的内部类,是一个Handler,主要用于接收binder客户端,如PackageInstaller或adb的安装程序,发起跨进程调用;对于PMS暴露出来的安装接口,如:

public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, int userId) {
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final VerificationInfo verificationInfo = new VerificationInfo(
            null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
    final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
            installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
            null /*packageAbiOverride*/, null /*grantedPermissions*/,
            null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
    params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;

    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
            System.identityHashCode(msg.obj));
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
            System.identityHashCode(msg.obj));
	发送给PackageHandler
    mHandler.sendMessage(msg);
}

PackageHandler属于PMS的子线程,专门处理安装程序!PMS内部的安装流程就不在分析!

备注一个重要信息,如果我们编译了系统应用apk后,希望我们的apk能默认获取一些权限,而不是安装时,或者运行弹出权限提醒,人工点击允许的话!可以在以下这个地方添加默认权限
在PMS启动完成后,会由Zygote调用他的systemRead()方法,在此方法内部会调用mDefaultPermissionPolicy.grantDefaultPermissions(userId),也就是给系统app添加默认权限的,进入这个类:

private void grantDefaultSystemHandlerPermissions(int userId) {
	// Camera
	Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
	获取apk对应的解析包Package
	PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr(
	        cameraIntent, userId);
	if (cameraPackage != null
	        && doesPackageSupportRuntimePermissions(cameraPackage)) {
	        赋予权限
	    grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
	    grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
	    grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
	}
}

本文博客参考很多博主隔壁老李头博客,更多细节可以参考其博客!感谢!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值