Android Framework--PackageManagerService

概述

PackageManagerService负责Package的管理、应用程序的安装、卸载以及提供应用程序的信息查询

PKMS的启动过程

通过 Android Framework–启动流程 一文我们知道SystemServer阶段会启动各种服务,其中就包括PKMS
简单来讲,PackageManagerService启动过程主要做了这么几件事

  1. 读取/system/etc/permissions目录下的多个xml配置文件,得到设备对蓝牙、摄像头、gps、wifi、gsm等的支持,并保存起来,以供查询。
  2. 对/system/frameworks目录下framework-res.apk(系统资源)、/vendor/overlay(厂商替换系统默认应用)、/system/priv-app(系统核心应用)、/system/app(系统默认应用)、/vendor/app(厂商定制应用)、/oem/app等目录的apk文件进行扫描、信息抽取和解析并保存到起来,提供查询服务。
  3. 对/data/app下的非系统应用的apk文件进行扫描、信息抽取和解析,保存到packages.xml和packages.list中,提供查询服务。并且当程序有变动的时候及时更新。

详细流程如下:
在SystemServer.java的run中

private void run() {
	...
	// Start services.
    try {
       startBootstrapServices();
       startCoreServices();
       startOtherServices();
   } catch (Throwable ex) {
       Slog.e("System", "******************************************");
       Slog.e("System", "************ Failure starting system services", ex);
       throw ex;
   }
   ...
}

PKMS的启动代码就在startBootstrapServices中

private void startBootstrapServices() {
	...
// Start the package manager.
Slog.i(TAG, "Package Manager");
//①调用main启动
mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
	...
}

startOtherServices()中也有PKMS的相关设置

private void startOtherServices(){
	...
try {
//做dex优化
  mPackageManagerService.performBootDexOpt();
} catch (Throwable e) {
  reportWtf("performing boot dexopt", e);
}
	...
try {
//通知系统PKMS已经就绪
  mPackageManagerService.systemReady();
} catch (Throwable e) {
  reportWtf("making Package Manager Service ready", e);
}
	...
}

看①

public static final IPackageManager main(Context context,boolean factoryTest,boolean onlyCore){
	PackageManagerService m = new PackageManagerService(context,factoryTest,onlyCore);
	ServiceManager.addService("package",m);
	return m;
}

这边会构造一个PackageManagerService并把自己加载ServiceManager中。构造函数主要做了三阶段:扫描目标文件夹之前的准备工作、扫描目标文件夹、扫描后的工作。这部分非常耗时,也是Android启动很久的原因之一

public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {
	...
	//①-1 扫描前准备工作
	//保存屏幕相关信息
    this.mMetrics = new DisplayMetrics();
    //保存系统运行过程中的一些设置
    this.mSettings = new Settings(context);
    //①-1.1为进程添加权限 (字符串,用户id,标志系统package)
    this.mSettings.addSharedUserLPw("android.uid.system", 1000, 1073741825);
    this.mSettings.addSharedUserLPw("android.uid.phone", 1001, 1073741825);
    this.mSettings.addSharedUserLPw("android.uid.log", 1007, 1073741825);
    this.mSettings.addSharedUserLPw("android.uid.nfc", 1027, 1073741825);
    this.mSettings.addSharedUserLPw("android.uid.bluetooth", 1002, 1073741825);
    this.mSettings.addSharedUserLPw("android.uid.shell", 2000, 1073741825);	
	...
	//HandlerThread是一个带消息循环的线程,这个线程主要处理安装和卸载APK的任务
	this.mHandlerThread = new ServiceThread("PackageManager", 10, true);
    this.mHandlerThread.start();
    this.mHandler = new PackageManagerService.PackageHandler(this.mHandlerThread.getLooper());
    Watchdog.getInstance().addThread(this.mHandler, 600000L);
    //获取/data/目录下各目录对象
    File dataDir = Environment.getDataDirectory();
    this.mAppDataDir = new File(dataDir, "data");
    //指向 /data/app/ 下面的雷同
    this.mAppInstallDir = new File(dataDir, "app");
    this.mAppLib32InstallDir = new File(dataDir, "app-lib");
    this.mAsecInternalPath = (new File(dataDir, "app-asec")).getPath();
    this.mUserAppDataDir = new File(dataDir, "user");
    this.mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
    //UserManager 目前没用,预测支持Android多用户
    sUserManager = new UserManagerService(context, this, this.mInstallLock, this.mPackages);
    //①-1.2 读取并解析 /etc/permission/ 下的xml文件并保存起来,这些文件描述了设备支持的硬件特性
    readPermissions();
    //①-1.3 读取三个文件的数据状态(上次保存的取出来与最新的进行对比即可知道apk是否更新了等信息)
    this.mRestoredSettings = this.mSettings.readLPw();
}

①-1中有权限相关的知识
linux系统中每个进程都有一个UID(用户id)并且可以有多个GID(组id),linux的权限是根据组来的。比如进程1000需要读写SD卡权限,则需要把进程1000加到读写SD卡权限组(GID=1015)中即可
这部分定义在Process.java中
这里写图片描述
①-1.2中有关设备硬件特性的知识
简单来讲这部分有多个xml文件组成,每个文件描述一部分类型,例如设备对蓝牙的支持、对前置摄像头的支持、对gps、wifi、gsm等等的支持
这里写图片描述
①-1.3中读取的三个文件

  • packages.xml 扫描完目标文件夹后会创建该文件,当系统进行程序安装、卸载和更新等操作均会更新该文件
  • packages.list 描述系统中存在的所有非系统自带的APK信息。当这些程序有变动时,PKMS就会更新该文件
  • packages-stopped.xml 从系统自带的设置程序中进入应用程序页面,然后在选择强制停止某个应用时,系统会将该应用的相关信息记录到此文件中。
//接上面的构造函数
	...
	//①-2扫描任务
	//①-2.1 对多个目录的APK进行扫描
	File frameworkDir = new File(Environment.getRootDirectory(), "framework");
	this.scanDirLI(frameworkDir, 193, 418, 0L);
	File vendorOverlayDir = new File("/vendor/overlay");
    this.scanDirLI(vendorOverlayDir, 65, 928, 0L);
    File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
    this.scanDirLI(privilegedAppDir, 193, 416, 0L);
    File systemAppDir = new File(Environment.getRootDirectory(), "app");
    this.scanDirLI(systemAppDir, 65, 416, 0L);
    File vendorAppDir = new File("/vendor/app");
    this.scanDirLI(vendorAppDir, 65, 416, 0L);
    File oemAppDir = new File(Environment.getOemDirectory(), "app");
    //①-2.2 扫描apk
    this.scanDirLI(oemAppDir, 65, 416, 0L);

①-2.1 这边可以看到用scanDirLi对多个目录进行扫描,这些目录分别是

  • /system/frameworks 该目录下的文件都是系统库,framework.jar、services.jar、framework-res.apk;不过scanDirLi只扫描apk,所以这里只会扫描framework-res.apk
  • /vendor/overlay 厂商用来替换系统默认应用(比如时钟等,但很多厂商没有放这里)
  • /system/priv-app 4.4之后增加,放系统核心应用(Launcher,systemui, settingsprovider),拥有系统级权限
  • /system/app 该目录下的都是系统默认应用,Browser.apk等
  • /vendor/app 该目录下的都是厂商定制的应用
  • /oem/app
    ①-2.2中对apk进行了扫描,其主要做了以下几件事
  • 利用PackageParser解析AndroidManifest.xml文件获取包信息保存在Package对象中
  • 根据AndroidManifest.xml提供的信息处理APK的permission、service、activity、provider等
//接上面的构造函数
	...
	//①-3 扫尾工作,将上面拿到的信息整合下保存下来
	//汇总并更新和Permission相关的信息
	this.updatePermissionsLPw((String)null, (Package)null, 1 | (regrantPermissions?6:0));
    //将信息写到package.xml package.list 及 package-stopped.xml中
    this.mSettings.writeLPr();
    this.mRequiredVerifierPackage = this.getRequiredVerifierLPr();

APK Install

Android每次启动的时候,都会使用PKMS,把Android系统中的所有Apk都安装一遍,一共4个步骤,如下所示

这里写图片描述

而Android系统会保存所有的apk而不解压,每次开机的时候重新安装一次,读取各种信息,是因为不解压apk节省空间,而每次读取资源则只需要解析apk中的Resource.arsc文件,该文件存储着资源的所有信息,包括资源所在apk中的地址、大小等,需要资源的时候先去这里查询即可快速找到。

我们手动安装的时候会弹出一个安装界面,这个界面就是PackageInstallerActivity,最终会调用PKMS的installPackageAsUser
流程大致如下

  1. 将apk文件复制到/data/app目录
  2. 解析apk信息,并且校验签名信息,四大组件的解析注册
  3. dex优化(dalvik虚拟机执行dexopt,优化后路径在data/dalvik-cache;art虚拟机执行dex2oat)
  4. 更新权限信息
  5. 完成安装,发送Intent.ACTION_PACKAGE_ADDED广播

Android Apk安装过程分析

queryIntentActivities

PKMS提供Activity、Service、BroadcastReceiver等通过IntentFilter的查询功能

public List<ResolveInfo> queryIntentActivities(Intent intent,
            String resolvedType, int flags, int userId) {
        if (!sUserManager.exists(userId)) return Collections.emptyList();
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector(); 
                comp = intent.getComponent();
            }
        }
		//第一种:根据ComponentName获取ActivityInfo返回查询结果
        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                final ResolveInfo ri = new ResolveInfo();
                //ResolveInfo的activityInfo指向查询到的ActivityInfo
                ri.activityInfo = ai;
                list.add(ri);
            }
            return list;
        }

        //
        synchronized (mPackages) {
            final String pkgName = intent.getPackage();
            //第二种情况,如果package也没有信息,则进行全系统范围内匹配
            if (pkgName == null) {
                List<CrossProfileIntentFilter> matchingFilters =
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
                // Check for results that need to skip the current profile.
                ResolveInfo resolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,
                        resolvedType, flags, userId);
                if (resolveInfo != null) {
                    List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
                    result.add(resolveInfo);
                    return result;
                }
                // Check for cross profile results.
                resolveInfo = queryCrossProfileIntents(
                        matchingFilters, intent, resolvedType, flags, userId);

                // Check for results in the current profile.
                //进行全系统范围内匹配
                List<ResolveInfo> result = mActivities.queryIntent(
                        intent, resolvedType, flags, userId);
                if (resolveInfo != null) {
                    result.add(resolveInfo);
                    Collections.sort(result, mResolvePrioritySorter);
                }
                return result;
            }
            //第三种:有package信息,根据package进行查询
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg != null) {
                return mActivities.queryIntentForPackage(intent, resolvedType, flags,
                        pkg.activities, userId);
            }
            return new ArrayList<ResolveInfo>();
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值