Android M PackageManagerService解析
16lz
2021-01-23
#############################################
本文为极度寒冰原创,转载请注明出处 #############################################1 PackageManager
/*常用命令*/adb shell dumpsys package (dump出系统中所有的application信息)
adb shell dumpsys package “com.android.contacts" p (dump出系统中特定包名的application信息)
/*常用命令*/
一: PackageManager服务
在Android系统中,和用户关系最密切的service应该是PackageManager了。
一般来说,用户想要在Android Phone上进行自己感兴趣的活动,都少不了apk的支持。
不论是打电话,上网,发短信还是玩一些自己喜欢的游戏,这些内容在android的世界里都是以apk的形式存在的。
所以,apk的安装,卸载是与每个用户息息相关的。
我们接下来会用一些文章去解析PackageManager的工作原理,apk的安装和卸载的过程。
在SystemServer中,经过前面的分析,我们知道了启动Android的系统关键服务的函数首先就是startBootstrapServices.
private void startBootstrapServices() { // Wait for installd to finish starting up so that it has a chance to // create critical directories such as /data/user with the appropriate // permissions. We need this to complete before we initialize other services. mInstaller = mSystemServiceManager.startService(Installer.class); // Install是pm路径下面的一个单独的类,主要用于通过InstallerConnection建立和installd的链接 // 然后Installd会进行创建一些系统关键目录的作用,所以我们要等待Installd的结束,才可以继续进行其它的创建..... // Only run "core" apps if we're encrypting the device. String cryptState = SystemProperties.get("vold.decrypt"); if (ENCRYPTING_STATE.equals(cryptState)) { Slog.w(TAG, "Detected encryption in progress - only parsing core apps"); mOnlyCore = true; } else if (ENCRYPTED_STATE.equals(cryptState)) { Slog.w(TAG, "Device encrypted - only parsing core apps"); mOnlyCore = true; } // Start the package manager. Slog.i(TAG, "Package Manager"); // mOnlyCore 用于判断是否只扫描系统的目录 mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); // mFactoryTestMode 用于判断是否为工厂测试,正常情况下mOnlyCore = false mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager();.....}在经过systemServer的启动后,我们知道了系统将会通过PackageManagerService的main函数进入到PackageManager的实际工作中。
PackageManagerService的main函数其实很简单,只有一点点的内容。
public static final PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); ServiceManager.addService("package", m); return m;}我们知道了PackageManager的main函数其实主要的实现是通过PackageManagerService的构造函数实现的。
那么构造函数的实现也是比较多的,做了很多耗时的工作。
其中,PackageManager的构造函数的主要功能为:
1. 扫描Android系统中几个目标文件夹中的apk,并且建立相应的数据结构去管理Package的信息,四大组件的信息,权限信息等内容
2. 解析物理的apk的文件,生成符合自己需求的数据结构。
所以,学习PackageManager最重要的就是学习保存各种信息的数据结构和他们之间的关系,以及控制的策略
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二: PackageManager的构造函数之前期准备
/*UID和GID*/
如果你是一个Linux的用户,那么一定会有如下的体验。当你的Linux的系统里面,存在多个用户的时候,你在登录了用户A之后,是没有权限去修改用户B的文件内容的。
再举个极端的例子,用普通用户的权限是没有办法进行Root用户所特有的操作的。
UID: Linux系统用于区别不同的用户,使用不同的用户名是一种方法。但是用户名只是一种让人方便读的字符串,对机器来讲是没有意义的。
为了方便机器的读取,Linux采用了一个32位的整数记录和区分不同的用户。这个用来区分不同用户的数字被称为User ID,简称UID。
Root的UID为0,普通的UID一般是从500开始。Android系统不同,后续会进行分析
GID: 除了用户的概念,Linux的系统还有用户组的概念。同一个用户组的用户之间具有相似的特征。
假如我们把某一个用户加入到root组,那么这个用户就可以浏览root用户组目录的文件。 如果root用户把某个文件的读写执行权限开放,root用户组的所有用户都可以修改该文件。
/*PackageManager 构造函数解析*/
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); if (mSdkVersion <= 0) { // 在最开始,我们会去检查sdk的version,因为如果检查不到sdk的话,apk就不知道运行在哪个版本上 Slog.w(TAG, "**** ro.build.version.sdk not set!"); } mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); //判断build的类型,一般为eng,user版本,eng的版本一般是不会进行odex的优化的 mMetrics = new DisplayMetrics(); // 初始化一个DisplayMetrics,用于保存屏幕像素的参数 mSettings = new Settings(context); // new了一个Settings的对象,这个settings是pm里面的,主要是保存系统apk的相关设置,互相之间关系等内容 --1 mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); // --2 mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; mSeparateProcesses = null; Slog.w(TAG, "Running with debug.separate_processes: * (ALL)"); } else { mDefParseFlags = 0; mSeparateProcesses = separateProcesses.split(","); Slog.w(TAG, "Running with debug.separate_processes: " + separateProcesses); } } else { mDefParseFlags = " + mDefParseFlags + " mSeparateProcesses " + mSeparateProcesses); } mInstaller = installer; getDefaultDisplayMetrics(context, mMetrics); SystemConfig systemConfig = SystemConfig.getInstance(); mGlobalGids = systemConfig.getGlobalGids(); mSystemPermissions = systemConfig.getSystemPermissions(); mAvailableFeatures = systemConfig.getAvailableFeatures();......}
/* --1 Settings的分析*/
/*
Settings的构造函数如下,在构造函数中主要是创建了/data/system的目录,并且创建了packages.xml, packages-backup.xml, packages.list, packages-stopped.xml等文件。
这些文件又都有一些具体的作用:
packages.xml: 是保存了系统所有的Package信息
packages-backup.xml: 是packages.xml的备份,防止在写packages.xml突然断电
packages.list: 保存了系统中已经安装的apk,以及对应的data/data/下面的对应关系
packages-stopped.xml: 用于记录系统中强制停止运行的Package信息
packages-stopped-backup.xml: 是packages-stopped.xml的备份,防止在写packages-stopped-backup的时候突然断电
*/
Settings(Context context) { this(context, Environment.getDataDirectory()); } Settings(Context context, File dataDir) { 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, 0660, SYSTEM_UID, PACKAGE_INFO_GID); // Deprecated: Needed for migration mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); }/*在初始化了Settings的文件之后,就进行了addSharedUserLPw的操作*/
/*
mSharedUsers是一个hashmap,HashMap<String, SharedUserSetting>,保存的是string和SharedUserSetting的对应
SharedUserSetting类继承自GrantedPermissions,包含了如下的五个内部变量:
final String name;
int userId;
// flags that are associated with this uid, regardless of any package flags
int uidFlags;
final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();
final PackageSignatures signatures = new PackageSignatures();
PackageSetting 继承自PackageSettingBase,主要有下面的三个成员变量:
int appId;
PackageParser.Package pkg;
SharedUserSetting sharedUser;
*/
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { SharedUserSetting s = mSharedUsers.get(name); // 从当前的mSharedUsers里面寻找对应名字的字符串是否已经有了对应 if (s != null) { // 如果已经存在的话,就返回对应的UID if (s.userId == uid) { return s; } // 如果从hashmap里面找到了这个String name,但是两个的uid却不一样的话,那么就会报错。因为一个shared user不可能有两个UID,只会保留第一个 PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared user, keeping first: " + name); return null; } s = new SharedUserSetting(name, pkgFlags); // 如果从mSharedUsers里面没有找到这个name的话,就会去新建一个 s.userId = uid; // 赋值UID if (addUserIdLPw(uid, s, name)) { // 将index和sharedUserSettings保存在mUserIds和mOtherUserIds mSharedUsers.put(name, s); // 讲name和sharedUserSettings保存在mSharedUsers return s; } return null; }/*接下来就会进入到addUserIdLPw的函数中*/
/*
UID前面讲了,是Linux的一种安全机制。
在android系统里面,继承并扩展了这种安全机制。
Android的系统,不同的应用程序之间原则上是不可以互相访问数据的。除非运行在同一个进程,或者通过contentProvider或其他的机制。
这个安全机制的主要原因,也就是android为每一个app都分配了一个UID,每个app从Linux的层面上面,都是一个独立的用户。
所以可以解释了这个安全机制的实现原理。
*/
private boolean addUserIdLPw(int uid, Object obj, Object name) { if (uid > Process.LAST_APPLICATION_UID) { // 系统为app分配的UID为10000 - 19999,如果超出了这个范围就会报错 return false; } /* mUserIds是一个ArrayList,也就是一个动态的数组 private final ArrayList<Object> mUserIds = new ArrayList<Object>(); 举个例子,如果是uid=10001的用户,说明是一个app,那么这个时候就会进入到app的相应处理中去 会去读去mUserIds的size. 在第一次读取的时候,N肯定是为0的。但是index肯定不会为0,因为在apk安装的时候就会为其分配一个UID 所以在第一次读取的时候,index一定是>=N的。 这个时候,会add null,其实就是什么也没有做,而只是对N进行++ 所以,N的数字,一定是比前面遍历过得apk的uid的index大1的 N只是用来统计当前的ArrayList里面的apk,index的数目 mOtherUserIds的定义如下:是一个SparseArray。可以通过put,append两种方式写入数据 private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>(); */ 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++; } /*如果通过index从mUserIds里面读取到了数据,那肯定是错误的。因为一个app只有一个index,且只能被赋值一次*/ if (mUserIds.get(index) != null) { PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate user id: " + uid + " name=" + name); return false; } mUserIds.set(index, obj); // 将当前的obj设置到ArrayList对应的index中 } 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; }/*
mUserIds是一个ArrayList,也就是一个动态的数组
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
举个例子,如果是uid=10001的用户,说明是一个app,那么这个时候就会进入到app的相应处理中去
会去读去mUserIds的size.
在第一次读取的时候,N肯定是为0的。但是index肯定不会为0,因为在apk安装的时候就会为其分配一个UID
所以在第一次读取的时候,index一定是>=N的。
这个时候,会add null,其实就是什么也没有做,而只是对N进行++
所以,N的数字,一定是比前面遍历过得apk的uid的index大1的
N只是用来统计当前的ArrayList里面的apk,index的数目
mOtherUserIds的定义如下:是一个SparseArray。可以通过put,append两种方式写入数据
private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>();
*/
/*
通过上述的分析,我们可以知道在PackageManager的构造函数中,先将系统的一些关键进程的UID进行了添加
为什么这样做呢?举个例子
在Settings里面,AndroidManifest里面有如下的声明:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settings"
coreApp="true"
android:sharedUserId="android.uid.system">
关注的焦点为sharedUserId为android.uid.system,设置这个可以使生成的apk能够获取system权限。
可以在任意system权限目录下面进行目录或者文件的创建,以及访问其他apk资源等
进行这个设置,也应该是让后面的app的uid在进行添加的时候,可以找到对应的android.uid.system的权限
然后在实际添加的过程中,调用了addSharedUserLPw函数,在这个函数中,我们会去检查是否当前的UID已经在mSharedUsers中存在,如果存在,就返回已经存在的对象。如果UID不一致,则会报错。
如果不存在的话,就会去new一个SharedUserSetting的对象。
在SharedUserSetting中,主要存在以下的对象。
final String name; --> name
int userId; --> 对应的UID
int uidFlags; --> 对应的FLAG
final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); --> 一个PackageSetting的对象,
final PackageSignatures signatures = new PackageSignatures(); --> 签名
在new出来SharedUserSetting的对象后,会将传递进来的UID进行赋值给userId,这样的话,结合构造函数,就完成了对
name, userId, uidFlags的赋值
然后执行addUserIdLPw函数,会将当前的SharedUserSetting对象和index进行关联,如果是app的话存入到了mUserIds的动态数组,如果是系统的userId的话,将对应的SharedUserSetting存入到了mOtherUserIds的hashmap中
*/
/*
在构造函数中,下面的code部分也是非常重要的
*/
SystemConfig systemConfig = SystemConfig.getInstance(); // 单例模式,获取SystemConfig的实例
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
/*
SystemConfig 的构造函数如下,可以看到,主要是去读取了一些系统的权限相关的信息
读取的路径是/ststem/etc/目录
*/
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); }/*
那么接下来去看一下readPermissions的实现
*/
void readPermissions(File libraryDir, boolean onlyFeatures) { // Read permissions from given directory. if (!libraryDir.exists() || !libraryDir.isDirectory()) { // 如果文件夹不存在,或者不为一个文件夹的时候 if (!onlyFeatures) { // 并且读取onlyFeatures的值为flase的时候 Slog.w(TAG, "No directory " + libraryDir + ", skipping"); } return; } // /system/etc/sysconfig 会进入到No directory skipping if (!libraryDir.canRead()) { // 如果文件夹不可读的话,会返回 Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return; } // Iterate over the files in the directory and scan .xml files for (File f : libraryDir.listFiles()) { // 遍历目录下面的文件夹 // We'll read platform.xml last if (f.getPath().endsWith("etc/permissions/platform.xml")) { // 最后再去处理platform.xml continue; } if (!f.getPath().endsWith(".xml")) { // 如果文件的路径不是xml结尾的话, 会继续读取其他的文件 Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue; } if (!f.canRead()) { // 如果文件不可读的话,会返回继续读取其它的文件 Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue; } readPermissionsFromXml(f, onlyFeatures); } // Read permissions from .../etc/permissions/platform.xml last so it will take precedence final File permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml"); readPermissionsFromXml(permFile, onlyFeatures); }然后最终会通过readPermissionsFromXml去进行文件的读取
/*
* 正如前面的文章分析init.rc时,我们知道针对这个文本的解析,都会有一个文本的解析器。
* 主要是对文本中的各个标签进行解析,并且对应成了相应的数据结构
* 解析android的system config的permission也是基于这个思想进行的解析
* 下面就来具体分析一下这个解析的过程和对应的数据结构
*/
private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {/* FileReader fr = new FileReader(String fileName);//使用带有指定文件的String参数的构造方法。创建该输入流对象。并关联源文件。 主要方法: int read(); // 读取单个字符。返回作为整数读取的字符,如果已达到流末尾,则返回 -1。 int read(char []cbuf);//将字符读入数组。返回读取的字符数。如果已经到达尾部,则返回-1。 void close();//关闭此流对象。释放与之关联的所有资源。 XmlPullParser: 常用的有: XmlPullParser.END_DOCUMENT XmlPullParser.START_DOCUMENT XmlPullParser.START_TAG XmlPullParser.END_TAG XmlPullParser.TEXT 分别代表着XML文档的结束,开始,标签的开始,标签的结束,内容 XmlPullParser.getEventType() : Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.) 【获取当前事件回调类型】 XmlPullParser.getName():For START_TAG or END_TAG events, the (local) name of the current element is returned when namespaces are enabled.【获取当前节点名字】 XmlPullParser.getAttributeValue(int index):Returns the given attributes value.【根据id获取节点属性值】 XmlPullParser.getAttributeValue(String namespace, String name):Returns the attributes value identified by namespace URI and namespace localName.【根据name获取节点属性值】 XmlPullParser.netxText(): If current event is START_TAG then if next element is TEXT then element content is returned or if next event is END_TAG then empty string is returned, otherwise exception is thrown.【回调节点START_TAG时,通过此方法获取节点内容】*/ FileReader permReader = null; try { permReader = new FileReader(permFile); } catch (FileNotFoundException e) { Slog.w(TAG, "Couldn't find or open permissions file " + permFile); return; } try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(permReader); int type; /* * parser.next() 指向下一个标签 */ while ((type=parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { ; } // 如果下一个标签不是START_TAG的话,会一直寻找到下一个标签 if (type != parser.START_TAG) { // 如果遍历完了,都没有遍历到一个标签的开始的话,会返回error throw new XmlPullParserException("No start tag found"); } /* * parser.getName() 获取标签的标题 */ if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {//如果获取到的标签的标题不为permissions,或者解析出来的标签的标题不为config的话,会报错/* 以android.hardware.nfc.hce.xml为例,该文件中的内容为: <permissions> <feature name="android.hardware.nfc.hce" /> </permissions>*/ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + ", expected 'permissions' or 'config'"); }/*具体的标签的属性如下:group: 安装到系统中的所有 APK 都具备的组 ID。permission: 可以指定一个权限与几个组 ID 对应。当一个 APK 被授予这个权限时,它也同时属于这几个组。assign-permission: 把一个权限赋予一个 UID,当进程使用这个 UID 运行时,就具备了这个权限。library: 为系统添加一些扩展库用的。对应的.jar 文件放在/system/framework/目录下。比如Google Map 相关的库。feature: 每添加一个硬件,都要增加对应的feature*/ while (true) { // 如果是permissions和config的话,会进入到这个while循环,用来解析这个大的标签,和配置文件 XmlUtils.nextElement(parser); // 读取permissions的大标签下面的每一个小标签 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { break; } String name = parser.getName();/* <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING"> <group gid="net_bw_acct" /> </permission>*/ if ("group".equals(name) && !onlyFeatures) { String gidStr = parser.getAttributeValue(null, "gid"); // 如果tag的name是group的话,根据gid去获取当前的属性 if (gidStr != null) { int gid = android.os.Process.getGidForName(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); // 将gid保存到mGlobalGids的数组中 } else { Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); } XmlUtils.skipCurrentTag(parser); continue; } else if ("permission".equals(name) && !onlyFeatures) { // 如果tag的name是permission的话,根据name去获取当前的属性 String perm = parser.getAttributeValue(null, "name"); if (perm == null) { Slog.w(TAG, "<permission> without name at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } perm = perm.intern(); // 返回string池中的perm,此处perm的值并没有改变 readPermission(parser, perm); // 调用readPermission进行perm的解析,该函数的解析我们放到后面进行分析 ----1/* <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" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />*/ } else if ("assign-permission".equals(name) && !onlyFeatures) { // 如果tag的name是assign-permission的话,会根据name, uid去获取相应的属性 String perm = parser.getAttributeValue(null, "name"); // 获取name if (perm == null) { Slog.w(TAG, "<assign-permission> without name at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } String uidStr = parser.getAttributeValue(null, "uid"); // 获取UID if (uidStr == null) { Slog.w(TAG, "<assign-permission> without uid at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } int uid = Process.getUidForName(uidStr); // 通过uid的string,获取到真正的UID if (uid < 0) { Slog.w(TAG, "<assign-permission> with unknown uid \"" + uidStr + "\" at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } perm = perm.intern(); // permission的name HashSet<String> perms = mSystemPermissions.get(uid); // 通过uid去perms的hashset中查询,看是否可以查询的到。 if (perms == null) { // 如果查询不到的话 perms = new HashSet<String>(); // 会新建一个perms mSystemPermissions.put(uid, perms); //然后把uid和对应的hashset放到mSystemPermissions的SparseArray中 } perms.add(perm); // 将新的permission放入到perms中 XmlUtils.skipCurrentTag(parser); // 通过这个操作,我们可以知道目前的数据结构是mSystemPermissions是一个SparseArray,然后是通过uid建立的映射。保存的是当前的UID所拥有的权限 } else if ("library".equals(name) && !onlyFeatures) {/*<library name="android.test.runner" file="/system/framework/android.test.runner.jar" /><library name="javax.obex" file="/system/framework/javax.obex.jar"/>*/ String lname = parser.getAttributeValue(null, "name"); // 通过name和file去获取对应的属性 String lfile = parser.getAttributeValue(null, "file"); if (lname == null) { Slog.w(TAG, "<library> without name at " + parser.getPositionDescription()); } else if (lfile == null) { Slog.w(TAG, "<library> without file at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got library " + lname + " in " + lfile); mSharedLibraries.put(lname, lfile); // 如果都有值的话,会把name和file都push到mSharedLibraries中 } XmlUtils.skipCurrentTag(parser); continue; } else if ("feature".equals(name)) {/*<permissions> <feature name="android.hardware.usb.accessory" /> <library name="com.android.future.usb.accessory" file="/system/framework/com.android.future.usb.accessory.jar" /></permissions>*/ String fname = parser.getAttributeValue(null, "name"); // 在feature的tag下,会获取feature的name,每增加一个硬件,都要增加对应的feature if (fname == null) { Slog.w(TAG, "<feature> without name at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got feature " + fname); FeatureInfo fi = new FeatureInfo(); fi.name = fname; mAvailableFeatures.put(fname, fi); // 将对应的feature name和其对应的FeatureInfo放入到了mAvailableFeatures中 } XmlUtils.skipCurrentTag(parser); continue; } else if ("allow-in-power-save".equals(name)) {/* allow-in-power-save, 应该是在省电模式下面允许该package获取网络的权限。 目前的话,只有platform.xml里面一处有这个标签的声明 <!-- These are the standard packages that are white-listed to always have internet access while in power save mode, even if they aren't in the foreground. --> <allow-in-power-save package="com.android.providers.downloads" />*/ String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<allow-in-power-save> without package at " + parser.getPositionDescription()); } else { mAllowInPowerSave.add(pkgname); // 将该pkgname保存到mAllowInPowerSave中 } XmlUtils.skipCurrentTag(parser); continue; } else if ("fixed-ime-app".equals(name)) { // 目前的permission中,并没有fixed-ime-app这个标签 String pkgname = parser.getAttributeValue(null, "package"); if (pkgname == null) { Slog.w(TAG, "<fixed-ime-app> without package at " + parser.getPositionDescription()); } else { mFixedImeApps.add(pkgname); } XmlUtils.skipCurrentTag(parser); continue; } else { XmlUtils.skipCurrentTag(parser); continue; } } permReader.close(); } catch (XmlPullParserException e) { Slog.w(TAG, "Got execption parsing permissions.", e); } catch (IOException e) { Slog.w(TAG, "Got execption parsing permissions.", e); }}在前面的解析中,我们留下了一个函数,是在permission的标签的时候,会用readPermission去进行解析
在这个函数中,我们会把permission和对应的gid进行保存。
void readPermission(XmlPullParser parser, String name) // name是permission的name,parser是读取到的数据流 throws IOException, XmlPullParserException { name = name.intern(); PermissionEntry perm = mPermissions.get(name); // 去mPermissions中查看是否已经有name对应的PermissionEntry if (perm == null) { perm = new PermissionEntry(name); mPermissions.put(name, perm); } // 如果没有的话,会new一个PermissionEntry int outerDepth = parser.getDepth(); int type; 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 ("group".equals(tagName)) { String gidStr = parser.getAttributeValue(null, "gid"); if (gidStr != null) { int gid = Process.getGidForName(gidStr); perm.gids = appendInt(perm.gids, gid); } else { Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); } } XmlUtils.skipCurrentTag(parser); }}分析到了现在,我们分析完了systemConfig的构造函数。
在systemConfig的阶段,我们会去读取/system/etc的permission内容,然后进行解析,保存在不同的数据结构中.
我们接着回到PackageManager的构造函数中:
/*
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids(); // 系统的etc的xml下面所声明的所有的gid
mSystemPermissions = systemConfig.getSystemPermissions(); // 系统中etc下xml声明的uid和其对应的权限
mAvailableFeatures = systemConfig.getAvailableFeatures(); // 系统中硬件所拥有的feature
*/
接下来我们继续分析PackageManager的构造函数
synchronized (mInstallLock) {// writersynchronized (mPackages) { mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); // 建立PackageHandler的消息循环,用于处理apk的安装请求 Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); File dataDir = Environment.getDataDirectory(); // 创建data的文件夹 mAppDataDir = new File(dataDir, "data"); // /data/data mAppInstallDir = new File(dataDir, "app"); // data/app mAppLib32InstallDir = new File(dataDir, "app-lib"); // /data/app-lib mAsecInternalPath = new File(dataDir, "app-asec").getPath(); // /data/app-ases mUserAppDataDir = new File(dataDir, "user"); // /data/user mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); // /data/app-private sUserManager = new UserManagerService(context, this, mInstallLock, mPackages);.... }}在进行完data的文件夹创建之后,会进入下面的一些操作:
ArrayMap<String, SystemConfig.PermissionEntry> permConfig = systemConfig.getPermissions(); // return 的是SystemConfig的mPermissions,存放的是permission的name和gid 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); } } // 该for循环的作用是将前面从/system/etc/permission里面读取到的permission的name和对应的gid放入到bp中,然后保存在mSettings的mPermissions中 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)); } // 该for循环的作用是将读取出来的SharedLibrary放入到mSharedLibraries中 mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();/*
这里首先调用Settings的readLPw函数去解析packages.xml和packages-backup.xml保存的安装列表信息
并把解析的pakcages信息添加到相应的数据结构中
这里我们先假设这是第一次开机,所有packages.xml和packages-backup.xml文件都还不存在。
所以Settings的readLPw函数会直接返回。
packages.xml里面保存了当前系统中保存的所有apk的详细信息
*/
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false), mSdkVersion, mOnlyCore); String customResolverActivity = Resources.getSystem().getString( R.string.config_customResolverActivity); if (TextUtils.isEmpty(customResolverActivity)) { customResolverActivity = null; } else { mCustomResolverComponentName = ComponentName.unflattenFromString( customResolverActivity); }
接下来会进行下面的处理:
long startTime = SystemClock.uptimeMillis(); // 获取当前系统的时间 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime); // Set flag to monitor and not change apk file paths when // scanning install directories. final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING; // 设置扫描模式 final HashSet<String> alreadyDexOpted = new HashSet<String>(); /** * Add everything in the in the boot class path to the * list of process files because dexopt will have been run * if necessary during zygote startup. */ final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); if(Debug_chao) {/* bootClassPath为 /system/framework/core-libart.jar: /system/framework/conscrypt.jar: /system/framework/okhttp.jar: /system/framework/core-junit.jar: /system/framework/bouncycastle.jar: /system/framework/ext.jar: /system/framework/framework.jar: /system/framework/telephony-common.jar: /system/framework/voip-common.jar: /system/framework/ims-common.jar: /system/framework/mms-common.jar: /system/framework/android.policy.jar: /system/framework/apache-xml.jar*/ Log.e(TAG,"bootClassPath = " + bootClassPath);/* /system/framework/services.jar: /system/framework/ethernet-service.jar: /system/framework/wifi-service.jar*/ Log.e(TAG,"systemServerClassPath = " + systemServerClassPath); } if (bootClassPath != null) { // 将bootClassPath的值,加入到alreadyDexOpted的hashset中/* BOOTCLASSPATH的赋值为: PRODUCT_BOOTCLASSPATH := $(subst $(space),:,$(foreach m,$(DEXPREOPT_BOOT_JARS_MODULES),/system/framework/$(m).jar)) DEXPREOPT_BOOT_JARS_MODULES := $(PRODUCT_BOOT_JARS) PRODUCT_BOOT_JARS := \ core-libart \ conscrypt \ okhttp \ core-junit \ bouncycastle \ ext \ framework \ telephony-common \ voip-common \ ims-common \ mms-common \ android.policy \ apache-xml \*/ String[] bootClassPathElements = splitString(bootClassPath, ':'); for (String element : bootClassPathElements) { alreadyDexOpted.add(element); } } else { Slog.w(TAG, "No BOOTCLASSPATH found!"); }/* # The order of PRODUCT_SYSTEM_SERVER_JARS matters. PRODUCT_SYSTEM_SERVER_JARS := \ services \ ethernet-service \ wifi-service*/ if (systemServerClassPath != null) { String[] systemServerClassPathElements = splitString(systemServerClassPath, ':'); for (String element : systemServerClassPathElements) { alreadyDexOpted.add(element); } } else { Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); } boolean didDexOptLibraryOrTool = false; final List<String> allInstructionSets = getAllInstructionSets(); ---1 final String[] dexCodeInstructionSets = // 将allInstructionSets转化为数组 getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()])); /** * Ensure all external libraries have had dexopt run on them. */ if (mSharedLibraries.size() > 0) { // 当知道了系统abi的架构后,会进行对sharedLibraries的处理,这边的mSharedLibraries,是jar // NOTE: For now, we're compiling these system "shared libraries" // (and framework jars) into all available architectures. It's possible // to compile them only when we come across an app that uses them (there's // already logic for that in scanPackageLI) but that adds some complexity. for (String dexCodeInstructionSet : dexCodeInstructionSets) { // 遍历所支持的平台,一般有arm,x86,arm64等平台 for (SharedLibraryEntry libEntry : mSharedLibraries.values()) { final String lib = libEntry.path; // 获取lib的path if (lib == null) { continue; } try { /* * 该函数为L上面新增的接口 * * Returns UP_TO_DATE if the VM believes that the apk/jar file * is up to date, PATCHOAT_NEEDED if it believes that the file is up * to date but it must be relocated to match the base address offset, * and DEXOPT_NEEDED if it believes that it is out of date and should * be passed through "dexopt" again. * * @param fileName the absolute path to the apk/jar file to examine. * @return DEXOPT_NEEDED if dexopt should be called on the file, * PATCHOAT_NEEDED if we need to run "patchoat" on it and * UP_TO_DATE otherwise. */ byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null, dexCodeInstructionSet, false); if (dexoptRequired != DexFile.UP_TO_DATE) { // 如果不是UP_TO_DATE alreadyDexOpted.add(lib); // 会将lib path加入到alreadyDexOpted // The list of "shared libraries" we have at this point is if (dexoptRequired == DexFile.DEXOPT_NEEDED) { // 如果是DEXOPT_NEEDED的话,灰烬行dexopt的优化 mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); } else { // 如果不是的话,那就是PATCHOAT_NEEDED,会进行oat的优化 mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); } didDexOptLibraryOrTool = true; // 只要进行了oat或者opt的优化的话,会将该变量置为true } } 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()); } } } }-------------------------------------------
1. getAllInstructionSets函数的实现:
private static List<String> getAllInstructionSets() { final String[] allAbis = Build.SUPPORTED_ABIS; // 获取当前手机所支持的硬件架构 final List<String> allInstructionSets = new ArrayList<String>(allAbis.length); // new一个List,长度即为获取出来的ABI的个数 for (String abi : allAbis) { // 从所有的支持的架构开始遍历,abi为armeabi,armeabi-v7a.... final String instructionSet = VMRuntime.getInstructionSet(abi); // 从以下的2,3可以看到,是从目前android系统支持的架构map中,去查找,查找出来一般为arm,mips,arm64... if (!allInstructionSets.contains(instructionSet)) { // 如果查询到的不包含在我们从Build的supported里面的话,把其加入到allInstructionSets里面 allInstructionSets.add(instructionSet); // 第一次的时候,因为allInstructionSets为一个刚new出来的对象,所以肯定是会加入到allInstructionSets里面 } } return allInstructionSets; }2. VMRuntime getInstructionSet的函数的实现:
public static String getInstructionSet(String abi) { final String instructionSet = ABI_TO_INSTRUCTION_SET_MAP.get(abi); if (instructionSet == null) { throw new IllegalArgumentException("Unsupported ABI: " + abi); } return instructionSet; }3. ABI_TO_INSTRUCTION_SET_MAP的声明:
private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP = new HashMap<String, String>(); static { ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm"); ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm"); ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips"); ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64"); ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86"); ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64"); ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64"); }--------------------------------------------------------------------------------- File frameworkDir = new File(Environment.getRootDirectory(), "framework"); // /system/framework // Gross hack for now: we know this file doesn't contain any // code, so don't dexopt it to avoid the resulting log spew. alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk"); // 将framework-res.apk加入到alreadyDexOpted中 // Gross hack for now: we know this file is only part of // the boot class path for art, so don't dexopt it to // avoid the resulting log spew. alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar"); /** * And there are a number of commands implemented in Java, which * we currently need to do the dexopt on so that they can be * run from a non-root shell. */ String[] frameworkFiles = frameworkDir.list(); // 或者/system/framework下的所有文件 if (frameworkFiles != null) { // TODO: We could compile these only for the most preferred ABI. We should // first double check that the dex files for these commands are not referenced // by other system apps. for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (int i=0; i<frameworkFiles.length; i++) { File libPath = new File(frameworkDir, frameworkFiles[i]); // 获取文件的绝对路径 String path = libPath.getPath(); // path是绝对路径 // Skip the file if we already did it. if (alreadyDexOpted.contains(path)) { // 如果该文件是已经包含在alreadyDexOpted中了,就忽略不去处理 continue; } // Skip the file if it is not a type we want to dexopt. if (!path.endsWith(".apk") && !path.endsWith(".jar")) { // 如果不是以jar和apk结尾的话,就忽略。因为odex优化针对的就是jar和apk continue; } try { byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null, dexCodeInstructionSet, false); // 判断文件是否需要优化 if (dexoptRequired == DexFile.DEXOPT_NEEDED) { // 如果需要dex的话,就会去进行Dex的优化 mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet); didDexOptLibraryOrTool = true; } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) { // 如果是需要oat优化的话,就会去进行oat的优化 mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet); didDexOptLibraryOrTool = true; } } catch (FileNotFoundException e) { Slog.w(TAG, "Jar not found: " + path); } catch (IOException e) { Slog.w(TAG, "Exception reading jar: " + path, e); } } } }// 在接下来的处理中,就会遇到packagemanager的一个重要的函数,scanDirLI,我们接下来继续分析。
// (Do this before scanning any apps.) // For security and version matching reason, only consider // overlay packages if they reside in VENDOR_OVERLAY_DIR. /* * private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; */ File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0); // 扫描该系统目录下的所有apk,进行安装 -1 // 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); if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); mInstaller.moveFiles();-------------------------------------------------------------------------------------------------------------------
/*
* scanDirLI的实现如下:
*/
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) { final File[] files = dir.listFiles(); // 会从传入的dir中,遍历所有的文件 if (ArrayUtils.isEmpty(files)) { // 如果该文件夹中一个文件都没有的话,会return Log.d(TAG, "No files in app dir " + dir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } for (File file : files) { // 如果有文件存在的话,就会进行遍历 final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); // 判断一个文件是否是一个apk的文件(以apk结尾),或者是一个文件并且文件夹满足isStageName的条件 if (!isPackage) { // Ignore entries which are not packages continue; } try { scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK, scanFlags, currentTime, null); // 会利用scanPackageLI进行接下来的解析 } catch (PackageManagerException e) { Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage()); // Delete invalid userdata apps if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 && e.error == PackageManager.INSTALL_FAILED_INVALID_APK) { // 只有非系统的apk扫描失败的时候,才会删除该apk。 logCriticalInfo(Log.WARN, "Deleting invalid package at " + file); if (file.isDirectory()) { FileUtils.deleteContents(file); } file.delete(); } } } }---------------------------------------------------------------------------------------------------------------------
public static boolean isStageName(String name) { final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); final boolean isLegacyContainer = name.startsWith("smdl2tmp"); return isFile || isContainer || isLegacyContainer; }---------------------------------------------------------------------------------------------------------------------
/* * Scan a package and return the newly parsed package. * Returns null in case of errors and the error code is stored in mLastScanError * 调用scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类package。 * 该类的实例代表一个apk文件,所以它就是和apk文件对应的数据结构 */ private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(); // new 了一个PackageParser的对象 pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) { parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; } /* * 各个参数的值如下: mSeparateProcesses = null,因为前面初始化值的时候separateProcesses为null mOnlyCore = false 正常情况下mOnlyCore为flase mMetrics 为前面获取到的手机的分辨率 mDefParseFlags 的值为0 parseFlags的值会随着scanFile的变化而变化 */ final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } PackageSetting ps = null; PackageSetting updatedPkg; // reader synchronized (mPackages) { // Look to see if we already know about this package. String oldName = mSettings.mRenamedPackages.get(pkg.packageName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) { // This package has been renamed to its original name. Let's // use that. ps = mSettings.peekPackageLPr(oldName); } // If there was no original package, see one for the real package name. if (ps == null) { ps = mSettings.peekPackageLPr(pkg.packageName); } // Check to see if this package could be hiding/updating a system // package. Must look for it either under the original or real // package name depending on our state. updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName); if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg); } boolean updatedPkgBetter = false; // First check if this is a system package that may involve an update if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { if (ps != null && !ps.codePath.equals(scanFile)) { // The path has changed from what was last scanned... check the // version of the new path against what we have stored to determine // what to do. if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath); if (pkg.mVersionCode < ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. logCriticalInfo(Log.INFO, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode + " better than this " + pkg.mVersionCode); if (!updatedPkg.codePath.equals(scanFile)) { Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : " + ps.name + " changing from " + updatedPkg.codePathString + " to " + scanFile); updatedPkg.codePath = scanFile; updatedPkg.codePathString = scanFile.toString(); // This is the point at which we know that the system-disk APK // for this package has moved during a reboot (e.g. due to an OTA), // so we need to reevaluate it for privilege policy. if (locationIsPrivileged(scanFile)) { updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED; } } updatedPkg.pkg = pkg; throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null); } else { // The current app on the system partition is better than // what we have updated to on the data partition; switch // back to the system partition version. // At this point, its safely assumed that package installation for // apps in system partition will go through. If not there won't be a working // version of the app // writer synchronized (mPackages) { // Just remove the loaded entries from package lists. mPackages.remove(ps.name); } logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile + " reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } synchronized (mPackages) { mSettings.enableSystemPackageLPw(ps.name); } updatedPkgBetter = true; } } } if (updatedPkg != null) { // An updated system app will not have the PARSE_IS_SYSTEM flag set // initially parseFlags |= PackageParser.PARSE_IS_SYSTEM; // An updated privileged app will not have the PARSE_IS_PRIVILEGED // flag set initially if ((updatedPkg.pkgFlags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { parseFlags |= PackageParser.PARSE_IS_PRIVILEGED; } } // Verify certificates against what was last scanned collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; if (updatedPkg == null && ps != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) { /* * Check to make sure the signatures match first. If they don't, * wipe the installed application and its data. */ if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but" + " signatures don't match existing userdata copy; removing"); deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false); ps = null; } else { /* * If the newly-added system app is an older version than the * already installed version, hide it. It will be scanned later * and re-added like an update. */ if (pkg.mVersionCode < ps.versionCode) { shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile + " but new version " + pkg.mVersionCode + " better than installed " + ps.versionCode + "; hiding system"); } else { /* * The newly found system app is a newer version that the * one previously installed. Simply remove the * already-installed application and replace it with our own * while keeping the application data. */ logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile + " reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } } } // The apk is forward locked (not public) if its code and resources // are kept in different files. (except for app in either system or // vendor path). // TODO grab this value from PackageSettings if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { if (ps != null && !ps.codePath.equals(ps.resourcePath)) { parseFlags |= PackageParser.PARSE_FORWARD_LOCK; } } // TODO: extend to support forward-locked splits String resourcePath = null; String baseResourcePath = null; if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) { if (ps != null && ps.resourcePathString != null) { resourcePath = ps.resourcePathString; baseResourcePath = ps.resourcePathString; } else { // Should not happen at all. Just log an error. Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); } } else { resourcePath = pkg.codePath; baseResourcePath = pkg.baseCodePath; } // Set application objects path explicitly. pkg.applicationInfo.setCodePath(pkg.codePath); pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); pkg.applicationInfo.setResourcePath(resourcePath); pkg.applicationInfo.setBaseResourcePath(baseResourcePath); pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); /* * If the system app should be overridden by a previously installed * data, hide the system app now and let the /data/app scan pick it up * again. */ if (shouldHideSystemApp) { synchronized (mPackages) { /* * We have to grant systems permissions before we hide, because * grantPermissions will assume the package update is trying to * expand its permissions. */ grantPermissionsLPw(pkg, true, pkg.packageName); mSettings.disableSystemPackageLPw(pkg.packageName); } } return scannedPkg; }---------------------------------------------------------------------------------------------------------------
在parsePackage的时候,会进行如下处理,如果是文件夹的话,会调用parseClusterPackage
如果不是文件夹的话,会进行parseMonolithicPackage的操作。
public Package parsePackage(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { return parseClusterPackage(packageFile, flags); } else { return parseMonolithicPackage(packageFile, flags); } }---------------------------------------------------------------------------------------------------------------
对于普通apk来说的话,是会进行parseMonolithicPackage的操作。然后我们来看一下这个函数的实现。
@Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { if (mOnlyCoreApps) { // mOnlyCoreApps的值是mOnlyCore,正常情况下也为false final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } } final AssetManager assets = new AssetManager(); // new了一个AssetManager的对象,AssetManager是资源管理框架 try { final Package pkg = parseBaseApk(apkFile, assets, flags); // 在parseBaseApk的时候,会把assets传人 pkg.codePath = apkFile.getAbsolutePath(); return pkg; } finally { IoUtils.closeQuietly(assets); } }----------------------------------------------------------------------------------------------------------------
private Package parseBaseApk(File apkFile, AssetManager assets, int flags) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); // 返回apkFile绝对路径名字字符串 mParseError = PackageManager.INSTALL_SUCCEEDED; // 在parse的开始,将mParseError的值设置为INSTALL_SUCCEEDED mArchiveSourcePath = apkFile.getAbsolutePath(); // 返回apkFile的绝对路径的字符串 if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); Resources res = null; XmlResourceParser parser = null; try { res = new Resources(assets, mMetrics, null); /* Resources类的成员函数updateConfiguration首先是根据参数config和metrics来更新设备的当前配置信息, 例如,屏幕大小和密码、国家地区和语言、键盘配置情况等等, 接着再调用成员变量mAssets所指向的一个Java层的AssetManager对象的成员函数setConfiguration来将这些配置信息设置到与之关联的C++层的AssetManager对象中去。 */ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final String[] outError = new String[1]; final Package pkg = parseBaseApk(res, parser, flags, outError); if (pkg == null) { throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); } pkg.baseCodePath = apkPath; pkg.mSignatures = null; return pkg; } catch (PackageParserException e) { throw e; } catch (Exception e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to read manifest from " + apkPath, e); } finally { IoUtils.closeQuietly(parser); } }---------------------------------------------------------------------------------------------------------------
private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) throws PackageParserException { if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Invalid package file: " + apkPath); } // The AssetManager guarantees uniqueness for asset paths, so if this asset path // already exists in the AssetManager, addAssetPath will only return the cookie // assigned to it. int cookie = assets.addAssetPath(apkPath); // addAssetPath 函数的作用是来将参数resDir所描述的Apk文件路径作为它的资源目录。 /* cookie有两种情况,分别为等不等于0. 如果等于0的话 AssetManager类的成员函数addAssetPath接着再检查在其成员变量mAssetPaths所描述的一个类型为asset_path的Vector中是否已经添加过参数path所描述的一个Apk文件路径了。 如果已经添加过了,那么AssetManager类的成员函数addAssetPath就不会再继续往下处理了,而是将与参数path所描述的一个Apk文件路径所对应的一个Cookie返回给调用者,即保存在输出参数cookie中, 前提是参数cookie的值不等于NULL。 一个Apk文件路径所对应的Cookie实际上只是一个整数,这个整数表示该Apk文件路径所对应的一个asset_path对象在成员变量mAssetPaths所描述的一个Vector中的索引再加上1。 */ if (cookie == 0) { // 等于0的时候,是添加vector失败的。所以会throw一个exception throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Failed adding asset path: " + apkPath); } return cookie; }
更多相关文章
- C语言函数以及函数的使用
- Android 获取本地音乐文件
- Android 4.0系统源码目录结构详解
- Android播放在线音乐文件
- Android应用程序调用系统解锁页面
- 文件读写
- Android读取Txt文件
- Android文件递归遍历
- android 不使用布局文件,完全由代码控制布局实例