《深入理解Android(卷2)》笔记 7.第四章 深入理解PackageManagerService
2013-06-05 21:27 ...平..淡... 阅读(3081) 评论(0) 编辑 收藏 举报知识点1:PKMS启动流程
SystemServer.java::ServerThread::run--->PKMS::main--->PKMS::构造函数
如下,在PKMS的main函数中将PKMS服务添加到到ServiceManager中。
public static final IPackageManager main(Context context, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore); ServiceManager.addService("package", m); return m; }
接下来对构造函数进行分析,这个是重点,将其分为3个大部分讨论。
知识点2:PKMS之构造函数分析
PKMS构造函数的主要功能是:扫描Android系统中几个目标文件夹中的APK,从而建立合适的数据结构以管理诸如package信息、四大组件信息、权限信息等。
第1大部分:构造函数分析之前期准备
将前期准备分为2个小部分,Settings类分析和xml文件扫描。先来看Settings类分析。
第1大部分之第1小部分:Settings类分析
public PackageManagerService(Context context, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); //mSdkVersion的值取自系统属性ro.build.version.sdk,即编译的SDK版本。如果没有定义,则APK就不知道自己运行在Android哪个版本上。 if (mSdkVersion <= 0) { Slog.w(TAG, "**** ro.build.version.sdk not set!"); } mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; //如果系统是eng版,则扫描package后,不对package做dex优化 mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); //mMetrics用于存储与显示屏相关的一些属性,比如屏幕宽/高/分辨率等信息 mMetrics = new DisplayMetrics(); //1.Settings是一个非常重要的类,该类用于存储系统运行过程中的一些设置 mSettings = new Settings(); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.phone", MULTIPLE_APPLICATION_UIDS ? RADIO_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.log", MULTIPLE_APPLICATION_UIDS ? LOG_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.nfc", MULTIPLE_APPLICATION_UIDS ? NFC_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.fm_radio", MULTIPLE_APPLICATION_UIDS ? FM_RADIO_UID : FIRST_APPLICATION_UID, ApplicationInfo.FLAG_SYSTEM); //-----------------第1大部分之第1小部分代码结束-----------------------
第1大部分之第1小部分的代码主要是了解Settings类及其addSharedUserLPw函数。
1.初识Settings
先来看PKMS的构造函数中,使用Settings类的地方:
mSettings.addSharedUserLPw("android.uid.system", //字符串 Process.SYSTEM_UID, //系统进程使用的用户id,值为1000 ApplicationInfo.FLAG_SYSTEM); //标识系统package
接下来分析上述语句,两个知识点:(1)UID/GID的介绍;(2) addSharedUserLPw函数
(1) UID/GID的介绍
UID/GID和进程权限有关,系统定义的UID/GID在Process.java中。
/** * Defines the UID/GID under which system code runs. */ public static final int SYSTEM_UID = 1000; /** * Defines the UID/GID under which the telephony code runs. */ public static final int PHONE_UID = 1001; /** * Defines the UID/GID under which the bluetooth code runs. * {@hide} */ public static final int BLUETOOTH_UID = 1002; /** * Defines the UID/GID for the user shell. * @hide */ public static final int SHELL_UID = 2000; /** * Defines the UID/GID for the log group. * @hide */ public static final int LOG_UID = 1007; /** * Defines the UID/GID for the WIFI supplicant process. * @hide */ public static final int WIFI_UID = 1010; /** * Defines the UID/GID for the mediaserver process. * @hide */ public static final int MEDIA_UID = 1013; /** * Defines the GID for the group that allows write access to the SD card. * @hide */ public static final int SDCARD_RW_GID = 1015; /** * Defines the UID/GID for the FM process. * @hide */ public static final int FM_RADIO_UID = 1028; /** * Defines the UID/GID for the NFC service process. * @hide */ public static final int NFC_UID = 1027; /** * Defines the GID for the group that allows write access to the internal media storage. * @hide */ public static final int MEDIA_RW_GID = 1023; /** * Defines the start of a range of UIDs (and GIDs), going from this * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning * to applications. */ public static final int FIRST_APPLICATION_UID = 10000; /** * Last of application-specific UIDs starting at * {@link #FIRST_APPLICATION_UID}. */ public static final int LAST_APPLICATION_UID = 99999; /** * Defines a secondary group id for access to the bluetooth hardware. */ public static final int BLUETOOTH_GID = 2000;
(2) addSharedUserLPw函数的分析
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { //(2.1)mSharedUsers是一个HashMap,key为字符串,值为SharedUserSetting对象 SharedUserSetting s = mSharedUsers.get(name); if (s != null) { if (s.userId == uid) { return s; } PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared user, keeping first: " + name); return null; } //创建一个新的SharedUserSetting对象,并设置userId为uid s = new SharedUserSetting(name, pkgFlags); s.userId = uid; //(2.2)分析addUserIdLPw if (addUserIdLPw(uid, s, name)) { mSharedUsers.put(name, s); //将name与s键值对添加到mSharedUsers中 return s; } return null; }
addSharedUserLPw函数中也有两个知识点:(2.1) SharedUserSetting类的分析;(2.2) addUserIdLPw函数的分析
(2.1) SharedUserSetting类的分析
该类包含了3个数据成员,
final String name; int userId; final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();
这里,通过一个例子来分析SharedUserSetting的数据成员的作用。该例子来源于SystemUI的AndroidManifest.xml,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.systemui" android:sharedUserId="android.uid.system" android:process="system" >
在xml文件中声明了一个android:sharedUserId属性,值为"android.uid.system"。该属性有两个作用:
* 两个或多个声明了同一种sharedUserId的APK可以共享彼此的数据,并可运行在同一进程中。
* 声明特定的sharedUserId之后,该APK所在进程将被赋予指定的UID(即用户权限)。
ps:要想实现”赋予指定的UID”,还需要在其Android.mk文件中声明额外的LOCAL_CERTIFICATE:=platform。
通过以上分析,可以获得以下3个关键点,即三个数据成员的作用:
* name(String):xml中sharedUserId属性指定了一个字符串,它是UID的字符串描述,故对应数据结构中也应该有一个字符串,这样就把代码和xml中的属性联系起来了。
* userId(int): linux系统中uid是一个整数,所以该数据结构中必然有一个整型变量。
* packages(HashSet<PackageSetting>):多个Package可声明同一个sharedUserId,故该数据结构必然会保存那些声明了相同sharedUserId的package的某些信息。
了解了3个关键点后,再看看Android系统如何设计相应数据结构的,SharedUserSetting类的关系图如下:
分析关系图中的各个类之间的关系:
* Settings类定义了一个mSharedUsers成员变量,它是一个HashMap,以字符串 (android.uid.system)为key,对应的value是一个SharedUserSetting对象。
* SharedUserSetting继承自GrantedPermission,定义了一个packages成员变量,类型为HashSet,用于保存声明了相同SharedUserId的Package的权限设置信息。
* 每个Package都有自己的权限设置。权限的概念由PackageSetting类表达。
* Settings中还有两个成员变量。mUserIds和mOtherUserIds变量,类型分别为ArrayList和SparseArray。目的是以UID为索引,得到对应的SharedUserSetting对象。
* 一般情况下,”以索引获取数组元素的速度”比”以key获取HashMap中的元素的速度”快得多。由此可知,这是以空间换取时间的做法。
(2.2) addUserIdLPw函数的分析
该函数的功能是将SharedUserSetting对象保存到对应的数组中。代码如下:
private boolean addUserIdLPw(int uid, Object obj, Object name) { //Android中,应用apk所在进程的uid从10000开始,系统apk所在进程的uid小于1000 if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) { return false; } if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { int N = mUserIds.size(); //计算索引,其值是uid和FIRST_APPLICATION_UID的差 final int index = uid - PackageManagerService.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); // mUserIds保存应用Package的uid } else { if (mOtherUserIds.get(uid) != null) { PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared id: " + uid + " name=" + name); return false; } mOtherUserIds.put(uid, obj); // mOtherUserIds保存系统Package的uid } return true; }
Setting的作用是管理Android系统运行过程中的一些设置信息。分析完毕。
第1大部分之第2小部分:xml文件扫描
1. xml文件扫描
继续分析PKMS的构造函数:
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 = 0; mSeparateProcesses = null; } //创建一个Installer对象,该对象和Native进程installd交互 mInstaller = new Installer(); //得到一个WindowManager对象 WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); d.getMetrics(mMetrics); //获取当前设备的显示屏信息 synchronized (mInstallLock) { // writer synchronized (mPackages) { //该线程的工作是:程序的安装和卸载等 mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); File dataDir = Environment.getDataDirectory(); // mAppDataDir指向/data/data目录 mAppDataDir = new File(dataDir, "data"); // mUserAppDataDir指向/data/user目录 mUserAppDataDir = new File(dataDir, "user"); // mDrmAppPrivateInstallDir指向/data/app-private目录 mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); //创建一个UserManager对象 mUserManager = new UserManager(mInstaller, mUserAppDataDir); //(a)从文件中读权限 readPermissions(); //(b) readLPw分析 mRestoredSettings = mSettings.readLPw(); long startTime = SystemClock.uptimeMillis(); //---------------------第1大部分之第2小部分代码结束--------------- …...…... }
接下来对代码中的(a)(b)进行分析。
(a) readPermissions函数分析
代码如下:
void readPermissions() { //从"etc/permissions"读取权限,该目录中存储了和设备相关的一些权限信息。 File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions"); if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.w(TAG, "No directory " + libraryDir + ", skipping"); return; } 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 //先处理非platform.xml文件 if (f.getPath().endsWith("etc/permissions/platform.xml")) { continue; } if (!f.getPath().endsWith(".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函数解析此xml文件 readPermissionsFromXml(f); } // Read permissions from .../etc/permissions/platform.xml last so it will take precedence final File permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml"); //解析platform.xml文件,该文件优先级最高 readPermissionsFromXml(permFile); }
可见,readPermissions函数就是调用readPermissionXml函数解析/system/etc/permissions目录下的文件。两个知识点:/system/etc/permissions下的xml文件和readPermissionXml函数。
(a.1)先来分析手机上/system/etc/permissions目录下的platform.xml文件:
<permissions> <!—建立权限名和gid的映射关系--> <permission name="android.permission.BLUETOOTH_ADMIN" > <group gid="net_bt_admin" /> </permission> <permission name="android.permission.BLUETOOTH" > <group gid="net_bt" /> </permission> <permission name="android.permission.INTERNET" > <group gid="inet" /> </permission> <permission name="android.permission.CAMERA" > <group gid="camera" /> </permission> <permission name="android.permission.READ_LOGS" > <group gid="log" /> </permission> <permission name="android.permission.WRITE_EXTERNAL_STORAGE" > <group gid="sdcard_rw" /> </permission> <!-- The group that /cache belongs to, linked to the permission set on the applications that can access /cache --> <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" > <group gid="cache" /> </permission> <!-- RW permissions to any system resources owned by group 'diag'. This is for carrier and manufacture diagnostics tools that must be installable from the framework. Be careful. --> <permission name="android.permission.DIAGNOSTIC" > <group gid="input" /> <group gid="diag" /> </permission> <!-- ================================= --> <!—赋予uid(shell所在线程)对应的权限--> <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" /> <assign-permission name="android.permission.SEND_SMS" uid="shell" /> <assign-permission name="android.permission.CALL_PHONE" uid="shell" /> <assign-permission name="android.permission.READ_CONTACTS" uid="shell" /> <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" /> <assign-permission name="android.permission.READ_CALENDAR" uid="shell" /> <assign-permission name="android.permission.WRITE_CALENDAR" uid="shell" /> <assign-permission name="android.permission.READ_USER_DICTIONARY" uid="shell" /> <assign-permission name="android.permission.WRITE_USER_DICTIONARY" uid="shell" /> <assign-permission name="android.permission.ACCESS_FINE_LOCATION" uid="shell" /> <assign-permission name="android.permission.ACCESS_COARSE_LOCATION" uid="shell" /> <assign-permission name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" uid="shell" /> <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" /> <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" /> <assign-permission name="android.permission.BLUETOOTH" uid="shell" /> <!-- System tool permissions granted to the shell. --> <assign-permission name="android.permission.GET_TASKS" uid="shell" /> <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" /> <assign-permission name="android.permission.REORDER_TASKS" uid="shell" /> <assign-permission name="android.permission.SET_ANIMATION_SCALE" uid="shell" /> <assign-permission name="android.permission.SET_PREFERRED_APPLICATIONS" uid="shell" /> <assign-permission name="android.permission.WRITE_SETTINGS" uid="shell" /> <assign-permission name="android.permission.WRITE_SECURE_SETTINGS" uid="shell" /> <assign-permission name="android.permission.BROADCAST_STICKY" uid="shell" /> <!-- Development tool permissions granted to the shell. --> <assign-permission name="android.permission.SET_DEBUG_APP" uid="shell" /> <assign-permission name="android.permission.SET_PROCESS_LIMIT" uid="shell" /> <assign-permission name="android.permission.SET_ALWAYS_FINISH" uid="shell" /> <assign-permission name="android.permission.DUMP" uid="shell" /> <assign-permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES" uid="shell" /> <!-- Internal permissions granted to the shell. --> <assign-permission name="android.permission.FORCE_BACK" uid="shell" /> <assign-permission name="android.permission.BATTERY_STATS" uid="shell" /> <assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" /> <assign-permission name="android.permission.INJECT_EVENTS" uid="shell" /> <assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" /> <assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" /> <assign-permission name="android.permission.SET_ORIENTATION" uid="shell" /> <assign-permission name="android.permission.INSTALL_PACKAGES" uid="shell" /> <assign-permission name="android.permission.CLEAR_APP_USER_DATA" uid="shell" /> <assign-permission name="android.permission.DELETE_CACHE_FILES" uid="shell" /> <assign-permission name="android.permission.DELETE_PACKAGES" uid="shell" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="shell" /> <assign-permission name="android.permission.READ_FRAME_BUFFER" uid="shell" /> <assign-permission name="android.permission.DEVICE_POWER" uid="shell" /> <assign-permission name="android.permission.INSTALL_LOCATION_PROVIDER" uid="shell" /> <assign-permission name="android.permission.BACKUP" uid="shell" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" /> <assign-permission name="android.permission.ACCESS_DRM" uid="media" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> <!—系统提供的Java库,应用程序运行时必须要链接这些库,该工作由系统自动完成--> <library name="android.test.runner" file="/system/framework/android.test.runner.jar" /> <library name="javax.obex" file="/system/framework/javax.obex.jar"/> </permissions>
platform.xml文件中主要由4个标签:
*permission和group:用于建立Linux层和Android层permission之间的映射关系。
*assign-permission:用于向指定的uid赋予相应的权限。
*library:用于指定系统库。
另外/system/etc/permissions目录下还有其他xml文件,包含feature标签。该标签的作用是用来描述一个手持终端应该支持的硬件特性。比如支持camera、支持蓝牙等。
真实设备上/system/etc/permissions目录中的文件来源:在编译时阶段由不同硬件平台根据自己的配置信息复制相关文件(路径是/frameworks/base/data/etc/)到/system/etc/permissions目录中。编译完成后,将生成system镜像,最终烧到手机里,这就是来源。
(a.2) readPermissionsFromXml分析
作用:将xml文件中的标签以及它们之间的关系转换成代码中的相应的数据结构。
private void readPermissionsFromXml(File permFile) { 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); XmlUtils.beginDocument(parser, "permissions"); while (true) { XmlUtils.nextElement(parser); if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { break; } String name = parser.getName(); //解析group标签 if ("group".equals(name)) { String gidStr = parser.getAttributeValue(null, "gid"); if (gidStr != null) { //转换xml中的gid字符串为整型,并保存到mGlobalGids中 int gid = Integer.parseInt(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); } else { Slog.w(TAG, "<group> without gid at " + parser.getPositionDescription()); } XmlUtils.skipCurrentTag(parser); continue; } else if ("permission".equals(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(); //调用readPermission处理 readPermission(parser, perm); //解析assign-permission标签 } else if ("assign-permission".equals(name)) { String perm = parser.getAttributeValue(null, "name"); if (perm == null) { Slog.w(TAG, "<assign-permission> without name at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } String uidStr = parser.getAttributeValue(null, "uid"); if (uidStr == null) { Slog.w(TAG, "<assign-permission> without uid at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } //根据字符串uidstr,然后获得uid值 int uid = Process.getUidForName(uidStr); if (uid < 0) { Slog.w(TAG, "<assign-permission> with unknown uid \"" + uidStr + "\" at " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } perm = perm.intern(); //和assign相关的信息保存在mSystemPermissions中 HashSet<String> perms = mSystemPermissions.get(uid); if (perms == null) { perms = new HashSet<String>(); mSystemPermissions.put(uid, perms); } perms.add(perm); XmlUtils.skipCurrentTag(parser); } else if ("library".equals(name)) { //解析library String lname = parser.getAttributeValue(null, "name"); 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); //将xml中的name和library保存到mSharedLibraries中 } XmlUtils.skipCurrentTag(parser); continue; } else if ("feature".equals(name)) { //解析feature标签 String fname = parser.getAttributeValue(null, "name"); if (fname == null) { Slog.w(TAG, "<feature> without name at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got feature " + fname); //在xml中定义的feature由FeatureInfo表达 FeatureInfo fi = new FeatureInfo(); fi.name = fname; mAvailableFeatures.put(fname, fi); //保存到mAvailableFeatures中 } 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); } }
总结:主要还是了解各标签的作用;对于数据结构,知道就好~~
(b) readLPw函数分析
readLPw函数的功能也是解析文件,不过这些文件的内容却是在PKMS正常启动后生成的。
这里介绍几个文件,具体位置在Settings构造函数中指明。代码如下:
Settings() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); systemDir.mkdirs(); FileUtils.setPermissions(systemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1); /* packages.xml和packages-backup.xml为一组,用于描述系统中安装的Package的信息,其中backup为临时文件。 packages-stopped.xml和packages-stopped-backup.xml为一组,用于描述系统中强制停止运行的Package的信息。 package.list列出当前系统中应用级(uid>10000)Package的信息 */ mSettingsFilename = new File(systemDir, "packages.xml"); mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); mPackageListFilename = new File(systemDir, "packages.list"); mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml"); }
文件的来历:
*package.xml:PKMS扫描完目标文件后会创建该文件。当系统进行安装、卸载和更新等操作时,均会更新该文件。下次开机会直接从里面读取相关信息添加到内存相关列表中。
*package-stopped.xml:该文件保存系统中被用户强制停止的Package的信息。
*package.list:描述系统中存在的非系统自带的APK的信息。
readLPw函数的功能就是解析这些xml文件的内容,然后建立并更新对应的数据结构。
总结:”第1大部分:构造函数分析之前期准备”这一阶段的工作就到此为止了。在这个阶段的工作就是扫描并解析xml文件,将其中的信息保存到特定的数据结构中。
这个阶段扫描的xml文件与权限以及上一次扫描得到的Package信息有关,它为PKMS下一阶段的工作提供了重要的参考信息。
第2大部分:构造函数分析之扫描Package
PKMS构造函数第二阶段的工作就是扫描系统中的APK了。由于需要逐个扫描文件,因此手机上装的APK越多,PKMS工作量就越大,系统启动速度就越慢。
将扫描Package分为3个小部分,系统库的dex优化、扫描系统package、扫描非系统package。
第2大部分之第1小部分:系统库的dex优化
dex优化的主要工作是:检查 BOOTCLASSPATH,mSharedLibraries 及 /system/framework 下的jar是否需要dexopt,需要的话,则通过 dexopt 进行优化。
接上一阶段的PKMS的构造函数,代码如下:
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. int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX; //定义扫描参数 if (mNoDexOpt) { Slog.w(TAG, "Running ENG build: no pre-dexopt!"); scanMode |= SCAN_NO_DEX; //在控制扫描过程中是否对APK文件进行dex优化 } final HashSet<String> libFiles = new HashSet<String>(); // mFrameworkDir指向/system/framework目录 mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); // mDalvikCacheDir指向/data/dalvik-cache目录 mDalvikCacheDir = new File(dataDir, "dalvik-cache"); boolean didDexOpt = false; /* Out of paranoia, ensure that everything in the boot class path has been dexed. 获取Java启动类库的路径,在init.rc文件中通过BOOTCLASSPATH环境变量输出,该变量指明了framwork所有核心库及文件位置。该值如下(init.rc中可找到): /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar:/system/framework/com.qrd.plugin.feature_query.jar:/system/framework/com.qrdinside.impl.jar */ String bootClassPath = System.getProperty("java.boot.class.path"); if (bootClassPath != null) { String[] paths = splitString(bootClassPath, ':'); for (int i=0; i<paths.length; i++) { try { //判断该Jar包是否需要重新做dex优化 if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { //将该Jar包文件路径保存到libFiles中,然后通过mInstall对象发送命令给installd,让其对该Jar包进行dex优化 libFiles.add(paths[i]); mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); didDexOpt = true; } } catch (FileNotFoundException e) { Slog.w(TAG, "Boot class path not found: " + paths[i]); } catch (IOException e) { Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? " + e.getMessage()); } } } else { Slog.w(TAG, "No BOOTCLASSPATH found!"); } //判断系统库是否需要做dex优化。处理方式同上 if (mSharedLibraries.size() > 0) { Iterator<String> libs = mSharedLibraries.values().iterator(); while (libs.hasNext()) { String lib = libs.next(); try { if (dalvik.system.DexFile.isDexOptNeeded(lib)) { libFiles.add(lib); mInstaller.dexopt(lib, Process.SYSTEM_UID, true); didDexOpt = 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()); } } } // Gross hack for now: we know this file doesn't contain any // code, so don't dexopt it to avoid the resulting log spew. // framework-res.apk定义了系统常用的资源,还有几个重要的Activity,如长按power键后弹出的选择框 libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); /** * 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. */ //列举/system/framework目录中的文件 String[] frameworkFiles = mFrameworkDir.list(); if (frameworkFiles != null) { //判断该目录下的APK或Jar文件是否需要做dex优化。处理方式同上 for (int i=0; i<frameworkFiles.length; i++) { File libPath = new File(mFrameworkDir, frameworkFiles[i]); String path = libPath.getPath(); // Skip the file if we alrady did it. if (libFiles.contains(path)) { continue; } // Skip the file if it is not a type we want to dexopt. if (!path.endsWith(".apk") && !path.endsWith(".jar")) { continue; } try { if (dalvik.system.DexFile.isDexOptNeeded(path)) { mInstaller.dexopt(path, Process.SYSTEM_UID, true); didDexOpt = true; } } catch (FileNotFoundException e) { Slog.w(TAG, "Jar not found: " + path); } catch (IOException e) { Slog.w(TAG, "Exception reading jar: " + path, e); } } } /* 上面代码对系统库(包括BOOTCLASSPATH指定的、platform.xml定义的、/system/framework目录下的Jar包与APK文件)进行一次仔细检查,对该优化的进行优化。如果优化期间,对任何一个文件都进行了优化,则设置didDexOpt为true */ if (didDexOpt) { // If we had to do a dexopt of one of the previous // things, then something on the system has changed. // Consider this significant, and wipe away all other // existing dexopt files to ensure we don't leave any // dangling around. String[] files = mDalvikCacheDir.list(); if (files != null) { /*如果前面对任意一个系统库重新做过dex优化,就需要删除cache文件。从删除cache的操作来看,这些cache文件应该是使用了dex优化后的系统库,当系统库重新做dex优化后,这些cache就失效了,需要删除。 */ for (int i=0; i<files.length; i++) { String fn = files[i]; if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) { Slog.i(TAG, "Pruning dalvik file: " + fn); (new File(mDalvikCacheDir, fn)).delete(); } } } } ............ //-------------------系统库的dex优化到此为止----------------------
第2大部分之第2小部分:扫描系统Package
清空cache文件后,PKMS进入了这个阶段的核心内容,即扫描Package,代码如下:
// Find base frameworks (resource packages without code). //创建文件夹监控对象,监视/system/frameworks目录。利用了Linux平台的tify机制 mFrameworkInstallObserver = new AppDirObserver( mFrameworkDir.getPath(), OBSERVER_EVENTS, true); mFrameworkInstallObserver.startWatching(); //调用scanDirLI函数扫描/system/framework目录 scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX, 0); // Collect all system packages. //创建文件夹监控对象,监视/system/app目录 mSystemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( mSystemAppDir.getPath(), OBSERVER_EVENTS, true); mSystemInstallObserver.startWatching(); //扫描/system/app目录 scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); // Collect all vendor packages. //监视并扫描/vendor/app目录 mVendorAppDir = new File("/vendor/app"); mVendorInstallObserver = new AppDirObserver( mVendorAppDir.getPath(), OBSERVER_EVENTS, true); mVendorInstallObserver.startWatching(); scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); //和installd交互 mInstaller.moveFiles();
由以上代码可知,PKMS将扫描以下几个目录:
* /system/framework:该目录中的文件都是系统库,例如framework.jar、services.jar、framework-res.apk。不过scanDirLI只扫描APK文件,所以framework-res.apk是该目录中唯一”受宠”的文件。 * /system/app:该目录下全是默认的系统应用,如Browser.apk、Settings.apk等。 * /vendor/app:该目录中的文件由厂商提供。
PKMS调用scanDirLI函数进行扫描,接下来分析该函数。
1.scanDirLI函数分析
代码如下:
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) { String[] files = dir.list(); //列举该目录下的文件 if (files == null) { Log.d(TAG, "No files in app dir " + dir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + dir); } int i; for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); if (!isPackageFilename(files[i])) { // Ignore entries which are not apk's,即这里只扫描APK文件 continue; } /* 调用scanPackageLI函数扫描一个特定的文件,返回值是PackageParser的内部类Package,该类的实例代表一个APK文件,所以它就是APK文件对应的数据结构 */ PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); // Don't mess around with apps in system partition. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { //注意flag的作用,只有非系统Package扫描失败,才会删除该文件 // Delete the apk Slog.w(TAG, "Cleaning up failed install of " + file); file.delete(); } } }
接着分析scanPackageLI函数。PKMS中有两个同名的scanPackageLI函数。先看初遇的scanPackageLI函数。
2.初会scanPackageLI函数[-->PKMS:: scanPackageLI]
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanMode, long currentTime) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; //默认的扫描标志,正常情况下为0 //创建一个PackageParser对象 PackageParser pp = new PackageParser(scanPath); pp.setSeparateProcesses(mSeparateProcesses); // mSeparateProcesses为空 pp.setOnlyCoreApps(mOnlyCore); //mOnlyCore为false //调用parsePackage函数解析APK文件。注意,这里把mMetric对象也作为参数传递进去了 final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags); if (pkg == null) { mLastScanError = pp.getParseError(); return null; } PackageSetting ps = null; PackageSetting updatedPkg; //--------BEGIN: 接下来主要是关于Package升级方面的工作----------- 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.mDisabledSysPackages.get( ps != null ? ps.name : pkg.packageName); } // 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 (pkg.mVersionCode < ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. Log.i(TAG, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode + " better than this " + pkg.mVersionCode); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } else { // The current app on the system partion 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); } Slog.w(TAG, "Package " + ps.name + " at " + scanFile + "reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = new FileInstallArgs(ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString); args.cleanUpResourcesLI(); mSettings.enableSystemPackageLPw(ps.name); } } } if (updatedPkg != null) { // An updated system app will not have the PARSE_IS_SYSTEM flag set initially parseFlags |= PackageParser.PARSE_IS_SYSTEM; } //--------END: 关于Package升级方面的工作结束----------- // Verify certificates against what was last scanned if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) { Slog.w(TAG, "Failed verifying certificates for package:" + pkg.packageName); return null; } // The apk is forward locked (not public) if its code and resources are kept in different files.目前只有/vendor/app目录下的扫描会使用该PARSE_FORWARD_LOCK标志。 // TODO grab this value from PackageSettings if (ps != null && !ps.codePath.equals(ps.resourcePath)) { parseFlags |= PackageParser.PARSE_FORWARD_LOCK; } String codePath = null; String resPath = null; if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0) { if (ps != null && ps.resourcePathString != null) { resPath = ps.resourcePathString; } else { // Should not happen at all. Just log an error. Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); } } else { resPath = pkg.mScanPath; } codePath = pkg.mScanPath; //mScanPath指向该APK文件所在的位置 // Set application objects path explicitly.其中codePath和resPath都指向APK文件所在位置 setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application //调用第二个scanPackageLI函数 return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime); }
scanPackageLI函数首先调用PackageParser类的相关函数(parsePackage函数)对APK文件进行解析。PackageParser类完成了从物理文件到对应数据结构的转换。下面来分析它。
3.PackageParser分析
PackageParser主要负责APK文件的解析,即解析APK文件中的AndroidManifest.xml文件。代码如下:[-->PackageParser.java::parsePackage]
public Package parsePackage(File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) { mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = sourceFile.getPath(); if (!sourceFile.isFile()) { Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (!isPackageFilename(sourceFile.getName()) && (flags&PARSE_MUST_BE_APK) != 0) { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, // so don't warn about them. Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); } mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (DEBUG_JAR) Slog.d(TAG, "Scanning package: " + mArchiveSourcePath); //检查是否为APK文件 XmlResourceParser parser = null; AssetManager assmgr = null; Resources res = null; boolean assetError = true; try { assmgr = new AssetManager(); int cookie = assmgr.addAssetPath(mArchiveSourcePath); if (cookie != 0) { res = new Resources(assmgr, metrics, null); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); //获得一个xml资源解析对象,该对象解析的是APK中的AndroidManifest.xml parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); assetError = false; } else { Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); } } catch (Exception e) { Slog.w(TAG, "Unable to read AndroidManifest.xml of " + mArchiveSourcePath, e); } if (assetError) { if (assmgr != null) assmgr.close(); mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; return null; } //出错处理 String[] errorText = new String[1]; Package pkg = null; Exception errorException = null; try { // 调用另外一个parsePackage函数 pkg = parsePackage(res, parser, flags, errorText); } catch (Exception e) { errorException = e; mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; } if (pkg == null) { // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED // just means to skip this app so don't make a fuss about it. if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) { if (errorException != null) { Slog.w(TAG, mArchiveSourcePath, errorException); } else { Slog.w(TAG, mArchiveSourcePath + " (at " + parser.getPositionDescription() + "): " + errorText[0]); } if (mParseError == PackageManager.INSTALL_SUCCEEDED) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; } } parser.close(); assmgr.close(); return null; } parser.close(); assmgr.close(); // 设置代码和资源路径,都指向APK文件所在的位置 pkg.mPath = destCodePath; pkg.mScanPath = mArchiveSourcePath; pkg.mSignatures = null; return pkg; }
该函数中调用了另一个同名的parsePackage函数。这个同名函数的作用单一,就是解析AndroidManifest.xml中的各种标签。分析其中的关键代码:
private Package parsePackage( Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { AttributeSet attrs = parser; mParseInstrumentationArgs = null; mParseActivityArgs = null; mParseServiceArgs = null; mParseProviderArgs = null; //得到Package的名字,其实就是得到AndroidManifest.xml中的Package属性的值,每个APK都必须定义该属性 .......... int type; .......... //以pkgName名字为参数,创建一个Package对象,后面的工作就是解析xml并填充该Package信息 final Package pkg = new Package(pkgName); boolean foundApp = false; TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); pkg.mVersionName = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_versionName, 0); if (pkg.mVersionName != null) { pkg.mVersionName = pkg.mVersionName.intern(); } String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); if (str != null && str.length() > 0) { String nameError = validateName(str, true); if (nameError != null && !"android".equals(pkgName)) { outError[0] = "<manifest> specifies bad sharedUserId name \"" + str + "\": " + nameError; mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; return null; } pkg.mSharedUserId = str.intern(); pkg.mSharedUserLabel = sa.getResourceId( com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); } sa.recycle(); pkg.installLocation = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_installLocation, PARSE_DEFAULT_INSTALL_LOCATION); pkg.applicationInfo.installLocation = pkg.installLocation; // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; int supportsNormalScreens = 1; int supportsLargeScreens = 1; int supportsXLargeScreens = 1; int resizeable = 1; int anyDensity = 1; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); //得到标签名 if (tagName.equals("application")) { if (foundApp) { if (RIGID_PARSER) { outError[0] = "<manifest> has more than one <application>"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, "<manifest> has more than one <application>"); XmlUtils.skipCurrentTag(parser); continue; } } foundApp = true; //解析application标签 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) { return null; } } else if (tagName.equals("permission-group")) { //解析permission-group标签 if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("permission")) { //解析permission标签 if (parsePermission(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("permission-tree")) { if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("uses-permission")) { //从xml文件中获取uses-permission标签的属性 sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission); // Note: don't allow this value to be a reference to a resource // that may change. //取出属性值,也就是对应的权限使用声明 String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); sa.recycle(); if (name != null && !pkg.requestedPermissions.contains(name)) { //添加到Package的requestedPermissions数组 pkg.requestedPermissions.add(name.intern()); } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-configuration")) { ConfigurationInfo cPref = new ConfigurationInfo(); sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesConfiguration); cPref.reqTouchScreen = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, Configuration.TOUCHSCREEN_UNDEFINED); cPref.reqKeyboardType = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, Configuration.KEYBOARD_UNDEFINED); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, false)) { cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; } cPref.reqNavigation = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation, Configuration.NAVIGATION_UNDEFINED); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, false)) { cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; } sa.recycle(); pkg.configPreferences.add(cPref);//保存到configPreference数组 XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-feature")) { FeatureInfo fi = new FeatureInfo(); sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesFeature); // Note: don't allow this value to be a reference to a resource // that may change. fi.name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesFeature_name); if (fi.name == null) { fi.reqGlEsVersion = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion, FeatureInfo.GL_ES_VERSION_UNDEFINED); } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) { fi.flags |= FeatureInfo.FLAG_REQUIRED; } sa.recycle(); if (pkg.reqFeatures == null) { pkg.reqFeatures = new ArrayList<FeatureInfo>(); } pkg.reqFeatures.add(fi); if (fi.name == null) { ConfigurationInfo cPref = new ConfigurationInfo(); cPref.reqGlEsVersion = fi.reqGlEsVersion; pkg.configPreferences.add(cPref); } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-sdk")) { if (SDK_VERSION > 0) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesSdk); int minVers = 0; String minCode = null; int targetVers = 0; String targetCode = null; TypedValue val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion); if (val != null) { if (val.type == TypedValue.TYPE_STRING && val.string != null) { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. targetVers = minVers = val.data; } } val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion); if (val != null) { if (val.type == TypedValue.TYPE_STRING && val.string != null) { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. targetVers = val.data; } } sa.recycle(); if (minCode != null) { if (!minCode.equals(SDK_CODENAME)) { if (SDK_CODENAME != null) { outError[0] = "Requires development platform " + minCode + " (current platform is " + SDK_CODENAME + ")"; } else { outError[0] = "Requires development platform " + minCode + " but this is a release platform."; } mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; } } else if (minVers > SDK_VERSION) { outError[0] = "Requires newer sdk version #" + minVers + " (current version is #" + SDK_VERSION + ")"; mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; } if (targetCode != null) { if (!targetCode.equals(SDK_CODENAME)) { if (SDK_CODENAME != null) { outError[0] = "Requires development platform " + targetCode + " (current platform is " + SDK_CODENAME + ")"; } else { outError[0] = "Requires development platform " + targetCode + " but this is a release platform."; } mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; } // If the code matches, it definitely targets this SDK. pkg.applicationInfo.targetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; } else { pkg.applicationInfo.targetSdkVersion = targetVers; } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("supports-screens")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestSupportsScreens); pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, 0); pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, 0); pkg.applicationInfo.largestWidthLimitDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, 0); // This is a trick to get a boolean and still able to detect // if a value was actually set. supportsSmallScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens, supportsSmallScreens); supportsNormalScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens, supportsNormalScreens); supportsLargeScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens, supportsLargeScreens); supportsXLargeScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens, supportsXLargeScreens); resizeable = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable, resizeable); anyDensity = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity, anyDensity); sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("protected-broadcast")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestProtectedBroadcast); // Note: don't allow this value to be a reference to a resource // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); sa.recycle(); if (name != null && (flags&PARSE_IS_SYSTEM) != 0) { if (pkg.protectedBroadcasts == null) { pkg.protectedBroadcasts = new ArrayList<String>(); } if (!pkg.protectedBroadcasts.contains(name)) { pkg.protectedBroadcasts.add(name.intern()); } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("instrumentation")) { if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) { return null; } } else if (tagName.equals("original-package")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); String orig =sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); if (!pkg.packageName.equals(orig)) { if (pkg.mOriginalPackages == null) { pkg.mOriginalPackages = new ArrayList<String>(); pkg.mRealPackage = pkg.packageName; } pkg.mOriginalPackages.add(orig); } sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("adopt-permissions")) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); String name = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); sa.recycle(); if (name != null) { if (pkg.mAdoptPermissions == null) { pkg.mAdoptPermissions = new ArrayList<String>(); } pkg.mAdoptPermissions.add(name); } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-gl-texture")) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals("compatible-screens")) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals("eat-comment")) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (RIGID_PARSER) { outError[0] = "Bad element under <manifest>: " + parser.getName(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } } if (!foundApp && pkg.instrumentation.size() == 0) { outError[0] = "<manifest> does not contain an <application> or <instrumentation>"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; } final int NP = PackageParser.NEW_PERMISSIONS.length; StringBuilder implicitPerms = null; for (int ip=0; ip<NP; ip++) { final PackageParser.NewPermissionInfo npi = PackageParser.NEW_PERMISSIONS[ip]; if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) { break; } if (!pkg.requestedPermissions.contains(npi.name)) { if (implicitPerms == null) { implicitPerms = new StringBuilder(128); implicitPerms.append(pkg.packageName); implicitPerms.append(": compat added "); } else { implicitPerms.append(' '); } implicitPerms.append(npi.name); pkg.requestedPermissions.add(npi.name); } } if (implicitPerms != null) { Slog.i(TAG, implicitPerms.toString()); } if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; } if (supportsNormalScreens != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; } if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; } if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.GINGERBREAD)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS; } if (resizeable < 0 || (resizeable > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS; } if (anyDensity < 0 || (anyDensity > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; } return pkg; }
上面介绍了PackageParser类对APK的解析流程,即对AndroidManifest.xml文件的解析流程。
现在来看一下PackageParser类内部的重要信息:
* 这些内部类主要是用来保存对应的信息。解析AndroidManifest.xml文件得到的信息由Package类保存。四大组件的信息也由对应的类保存。因为一个APK可声明多个组件,所以activities和services等都可以声明为ArrayList。
* 各种组件类还需要创造出XXIntentInfo类(最终继承自IntentFilter类)而不是直接使用XXInfo类,主要原因是为了支持Intent匹配查询。该类会判断自己是否满足某Intent中的Action等的要求,若满足,则返回对应的ActivityInfo。
* PackageLite仅用来存储Package的一些简单信息。在安装Package时,会用到该类。
4.再会scanPackageLI(函数重载,不是之前遇到的那个函数)
当PackageParse扫描完一个APK后,此时系统已经根据该APK中的AndroidManifest.xml创建了一个完整的Package对象,下一步就是将该Package对象加入到系统中。此时调用的函数就是另一个scanPackageLI,代码如下:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime) { File scanFile = new File(pkg.mScanPath); ...... mScanningPath = scanFile; //设置Package对象中的applicationInfo的flags标签,用于标示该Package为系统Package。 if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; } //单独判断包名是否为”android”,很重要!!! if (pkg.packageName.equals("android")) { synchronized (mPackages) { if (mAndroidApplication != null) { Slog.w(TAG, ...... // mPlatformPackage成员用于保存包名为”android”的Package信息。 mPlatformPackage = pkg; pkg.mVersionCode = mSdkVersion; // mAndroidApplication保存此Package中的ApplicationInfo。 mAndroidApplication = pkg.applicationInfo; // mResolveActivity指向表示ChooseActivity信息的ActivityInfo。 mResolveActivity.applicationInfo = mAndroidApplication; mResolveActivity.name = ResolverActivity.class.getName(); mResolveActivity.packageName = mAndroidApplication.packageName; mResolveActivity.processName = mAndroidApplication.processName; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; // mResolveInfo的ActivityInfo成员指向mResolveActivity。它用于存储系统解析Intent(经IntentFilter过滤)后得到的结果信息。比如满足某个Intent的Activity信息 mResolveInfo.activityInfo = mResolveActivity; mResolveInfo.priority = 0; mResolveInfo.preferredOrder = 0; mResolveInfo.match = 0; mResolveComponentName = new ComponentName( mAndroidApplication.packageName, mResolveActivity.name); } }
这里先分析下上述if (pkg.packageName.equals("android")) 这个判断条件,即单独判断PackageName为”android”的Package。与该Package对应的是framework-res.apk。
实际上,framework-res.apk还包含几个常用的Activity。
*ChooserActivity:当多个Activity符合某个Intent时,系统会弹出该Activity,由用户选择。
*RingtonePickerActivity:铃声选择Activity。
*ShutdownActivity:关机前弹出的选择对话框。
此处保存这些信息,主要是为了提高运行过程中的效率。
ps:从PKMS中查询满足某个Intent的Activity时,返回的就是ResolveInfo,再根据ResolveInfo的信息得到具体的Activity。
继续分析scanPackageLI函数。
//mPackage用于保存系统内的所有Package,以packageName为key if (mPackages.containsKey(pkg.packageName) || mSharedLibraries.containsKey(pkg.packageName)) { Slog.w(TAG, "Application package " + pkg.packageName + " already installed. Skipping duplicate."); mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } // Initialize package source and resource directories File destCodeFile = new File(pkg.applicationInfo.sourceDir); File destResourceFile = new File(pkg.applicationInfo.publicSourceDir); SharedUserSetting suid = null; PackageSetting pkgSetting = null; if (!isSystemApp(pkg)) { // Only system apps can use these features. pkg.mOriginalPackages = null; pkg.mRealPackage = null; pkg.mAdoptPermissions = null; } // writer synchronized (mPackages) { /*此处代码很长,概括其工作内容如下: 1.如果该Package声明了useslibraries,那么系统要判断library是否在mShareLibraries中 2.如果package声明了SharedUser,则需要处理SharedUserSettings相关内容,由Settings的getSharedUserLPw函数处理 3.处理pkgSetting,通过调用Settings的getPackageLPw函数完成 4.调用verifySignaturesLP函数,检查该Package的signature */ if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { if (mTmpSharedLibraries == null || mTmpSharedLibraries.length < mSharedLibraries.size()) { mTmpSharedLibraries = new String[mSharedLibraries.size()]; } int num = 0; int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0; for (int i=0; i<N; i++) { final String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); if (file == null) { Slog.e(TAG, "Package " + pkg.packageName + " requires unavailable shared library " + pkg.usesLibraries.get(i) + "; failing!"); mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; return null; } mTmpSharedLibraries[num] = file; num++; } N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0; for (int i=0; i<N; i++) { final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i)); if (file == null) { Slog.w(TAG, "Package " + pkg.packageName + " desires unavailable shared library " + pkg.usesOptionalLibraries.get(i) + "; ignoring!"); } else { mTmpSharedLibraries[num] = file; num++; } } if (num > 0) { pkg.usesLibraryFiles = new String[num]; System.arraycopy(mTmpSharedLibraries, 0, pkg.usesLibraryFiles, 0, num); } } if (pkg.mSharedUserId != null) { suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, pkg.applicationInfo.flags, true); if (suid == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " for shared user failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId + "): packages=" + suid.packages); } } // Check if we are renaming from an original package name. PackageSetting origPackage = null; String realName = null; if (pkg.mOriginalPackages != null) { // This package may need to be renamed to a previously // installed name. Let's check on that... final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage); if (pkg.mOriginalPackages.contains(renamed)) { // This package had originally been installed as the // original name, and we have already taken care of // transitioning to the new one. Just update the new // one to continue using the old name. realName = pkg.mRealPackage; if (!pkg.packageName.equals(renamed)) { // Callers into this function may have already taken // care of renaming the package; only do it here if // it is not already done. pkg.setPackageName(renamed); } } else { for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) { if ((origPackage = mSettings.peekPackageLPr( pkg.mOriginalPackages.get(i))) != null) { // We do have the package already installed under its // original name... should we use it? if (!verifyPackageUpdateLPr(origPackage, pkg)) { // New package is not compatible with original. origPackage = null; continue; } else if (origPackage.sharedUser != null) { // Make sure uid is compatible between packages. if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) { Slog.w(TAG, "Unable to migrate data from " + origPackage.name + " to " + pkg.packageName + ": old uid " + origPackage.sharedUser.name + " differs from " + pkg.mSharedUserId); origPackage = null; continue; } } else { if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package " + pkg.packageName + " to old name " + origPackage.name); } break; } } } } if (mTransferedPackages.contains(pkg.packageName)) { Slog.w(TAG, "Package " + pkg.packageName + " was transferred to another, but its .apk remains"); } // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.flags, true, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } if (pkgSetting.origPackage != null) { // If we are first transitioning from an original package, // fix up the new package's name now. We need to do this after // looking up the package under its new name, so getPackageLP // can take care of fiddling things correctly. pkg.setPackageName(origPackage.name); // File a report about this. String msg = "New package " + pkgSetting.realName + " renamed to replace old package " + pkgSetting.name; reportSettingsProblem(Log.WARN, msg); // Make a note of it. mTransferedPackages.add(origPackage.name); // No longer need to retain this. pkgSetting.origPackage = null; } if (realName != null) { // Make a note of it. mTransferedPackages.add(pkg.packageName); } if (mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } pkg.applicationInfo.uid = pkgSetting.userId; pkg.mExtras = pkgSetting; if (!verifySignaturesLP(pkgSetting, pkg)) { if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { return null; } // The signature has changed, but this package is in the system // image... let's recover! pkgSetting.signatures.mSignatures = pkg.mSignatures; // However... if this package is part of a shared user, but it // doesn't match the signature of the shared user, let's fail. // What this means is that you can't change the signatures // associated with an overall shared user, which doesn't seem all // that unreasonable. if (pkgSetting.sharedUser != null) { if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser); mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return null; } } // File a report about this. String msg = "System package " + pkg.packageName + " signature changed; retaining data."; reportSettingsProblem(Log.WARN, msg); } // Verify that this new package doesn't have any content providers // that conflict with existing packages. Only do this if the // package isn't already installed, since we don't want to break // things that are installed. if ((scanMode&SCAN_NEW_INSTALL) != 0) { final int N = pkg.providers.size(); int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); if (p.info.authority != null) { String names[] = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { if (mProviders.containsKey(names[j])) { PackageParser.Provider other = mProviders.get(names[j]); Slog.w(TAG, "Can't install because provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + ") is already used by " + ((other != null && other.getComponentName() != null) ? other.getComponentName().getPackageName() : "?")); mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; return null; } } } } } if (pkg.mAdoptPermissions != null) { // This package wants to adopt ownership of permissions from // another package. for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) { final String origName = pkg.mAdoptPermissions.get(i); final PackageSetting orig = mSettings.peekPackageLPr(origName); if (orig != null) { if (verifyPackageUpdateLPr(orig, pkg)) { Slog.i(TAG, "Adopting permissions from " + origName + " to " + pkg.packageName); mSettings.transferPermissionsLPw(origName, pkg.packageName); } } } } } final String pkgName = pkg.packageName; final long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; //确定运行该package的进程的进程名,一般用packageName作为进程名 pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, pkg.applicationInfo.uid); File dataPath; if (mPlatformPackage == pkg) { // The system package is special. dataPath = new File (Environment.getDataDirectory(), "system"); pkg.applicationInfo.dataDir = dataPath.getPath(); } else { // 返回该package的目录,一般是/data/data/packageName/ dataPath = getDataPathForPackage(pkg.packageName, 0); boolean uidError = false; if (dataPath.exists()) { //如果该目录已存在,则要处理uid的问题 mOutPermissions[1] = 0; FileUtils.getPermissions(dataPath.getPath(), mOutPermissions); // If we have mismatched owners for the data path, we have a problem. if (mOutPermissions[1] != pkg.applicationInfo.uid) { boolean recovered = false; if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { // If this is a system app, we can at least delete its // current data so the application will still work. int ret = mInstaller.remove(pkgName, 0); if (ret >= 0) { // TODO: Kill the processes first // Remove the data directories for all users mUserManager.removePackageForAllUsers(pkgName); // Old data gone! String msg = "System package " + pkg.packageName + " has changed from uid: " + mOutPermissions[1] + " to " + pkg.applicationInfo.uid + "; old data erased"; reportSettingsProblem(Log.WARN, msg); recovered = true; // And now re-install the app. ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! msg = "System package " + pkg.packageName + " could not have data directory re-created after delete."; reportSettingsProblem(Log.WARN, msg); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } // Create data directories for all users mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); } if (!recovered) { mHasSystemUidErrors = true; } } if (!recovered) { pkg.applicationInfo.dataDir = "/mismatched_uid/settings_" + pkg.applicationInfo.uid + "/fs_" + mOutPermissions[1]; pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir; String msg = "Package " + pkg.packageName + " has mismatched uid: " + mOutPermissions[1] + " on disk, " + pkg.applicationInfo.uid + " in settings"; // writer synchronized (mPackages) { mSettings.mReadMessages.append(msg); mSettings.mReadMessages.append('\n'); uidError = true; if (!pkgSetting.uidError) { reportSettingsProblem(Log.ERROR, msg); } } } } pkg.applicationInfo.dataDir = dataPath.getPath(); } else { //向installd发送install命令,实际上就是在/data/data目录下建立packageName目录。 if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } // 为系统所有user安装此程序 mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { Slog.w(TAG, "Unable to create data directory: " + dataPath); pkg.applicationInfo.dataDir = null; } } /* * Set the data dir to the default "/data/data/<package name>/lib" * if we got here without anyone telling us different (e.g., apps * stored on SD card have their native libraries stored in the ASEC * container with the APK). * * This happens during an upgrade from a package settings file that * doesn't have a native library path attribute at all. */ if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) { //为该Package确定native library所在目录,一般是/data/data/packageName/lib if (pkgSetting.nativeLibraryPathString == null) { final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath(); pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath; pkgSetting.nativeLibraryPathString = nativeLibraryPath; } else { pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString; } } pkgSetting.uidError = uidError; } String path = scanFile.getPath(); /* Note: We don't want to unpack the native binaries for * system applications, unless they have been updated * (the binaries are already under /system/lib). * Also, don't unpack libs for apps on the external card * since they should have their libraries in the ASEC * container already. * * In other words, we're going to unpack the binaries * only for non-system apps and system app upgrades. */ //如果该APK包含了native动态库,则需要将它们从APK文件中解压并复制到对应目录中 if (pkg.applicationInfo.nativeLibraryDir != null) { try { final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); //从Android 2.3开始,系统package的native库统一放在/system/lib下。所以系统不会提取系统Package目录下的APK包中的native库 if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { /* * Upgrading from a previous version of the OS sometimes * leaves native libraries in the /data/data/<app>/lib * directory for system apps even when they shouldn't be. * Recent changes in the JNI library search path * necessitates we remove those to match previous behavior. */ if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) { Log.i(TAG, "removed obsolete native libraries for system package " + path); } } else if (nativeLibraryDir.getParentFile().getCanonicalPath() .equals(dataPathString)) { /* * Make sure the native library dir isn't a symlink to * something. If it is, ask installd to remove it and create * a directory so we can copy to it afterwards. */ boolean isSymLink; try { isSymLink = S_ISLNK(Libcore.os.lstat(nativeLibraryDir.getPath()).st_mode); } catch (ErrnoException e) { // This shouldn't happen, but we'll fail-safe. isSymLink = true; } //判断是否为链接,如果是,需要删除该链接 if (isSymLink) { mInstaller.unlinkNativeLibraryDirectory(dataPathString); } /* * If this is an internal application or our * nativeLibraryPath points to our data directory, unpack * the libraries if necessary. */ //在lib下建立和CPU类型对应的目录,例如AMR平台的是arm/,MIPS平台的是mips/ NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); } else { Slog.i(TAG, "Linking native library dir for " + path); mInstaller.linkNativeLibraryDirectory(dataPathString, pkg.applicationInfo.nativeLibraryDir); } } catch (IOException ioe) { Log.e(TAG, "Unable to get canonical file " + ioe.toString()); } } pkg.mScanPath = path; if ((scanMode&SCAN_NO_DEX) == 0) { //对该APK做dex优化 if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } } if (mFactoryTest && pkg.requestedPermissions.contains( android.Manifest.permission.FACTORY_TEST)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. //如果该APK已经存在,要先杀掉运行该APK的进程 if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { killApplication(pkg.applicationInfo.packageName, pkg.applicationInfo.uid); } //在此之前,四大组件的信息都属于Package的私有财产,现在需要把它们登记注册到PKMS内部的财产管理对象中。这样PKMS就可对外提供统一的组件信息,而不必拘泥于具体的Package synchronized (mPackages) { // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { mAppDirs.put(pkg.mPath, pkg); } // Add the new setting to mSettings mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages mPackages.put(pkg.applicationInfo.packageName, pkg); // Make sure we don't accidentally delete its data. mSettings.mPackagesToBeCleaned.remove(pkgName); // Take care of first install / last update times. if (currentTime != 0) { if (pkgSetting.firstInstallTime == 0) { pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime; } else if ((scanMode&SCAN_UPDATE_TIME) != 0) { pkgSetting.lastUpdateTime = currentTime; } } else if (pkgSetting.firstInstallTime == 0) { // We need *something*. Take time time stamp of the file. pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime; } else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) { if (scanFileTime != pkgSetting.timeStamp) { // A package on the system image has changed; consider this // to be an update. pkgSetting.lastUpdateTime = scanFileTime; } } //处理该Package中的provider信息 int N = pkg.providers.size(); StringBuilder r = null; int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); p.info.processName = fixProcessName(pkg.applicationInfo.processName, p.info.processName, pkg.applicationInfo.uid); // mProvidersByComponent提供基于ComponentName的Provider信息查询 mProvidersByComponent.put(new ComponentName(p.info.packageName, p.info.name), p); p.syncable = p.info.isSyncable; if (p.info.authority != null) { String names[] = p.info.authority.split(";"); p.info.authority = null; for (int j = 0; j < names.length; j++) { if (j == 1 && p.syncable) { // We only want the first authority for a provider to possibly be // syncable, so if we already added this provider using a different // authority clear the syncable flag. We copy the provider before // changing it because the mProviders object contains a reference // to a provider that we don't want to change. // Only do this for the second authority since the resulting provider // object can be the same for all future authorities for this provider. p = new PackageParser.Provider(p); p.syncable = false; } if (!mProviders.containsKey(names[j])) { mProviders.put(names[j], p); if (p.info.authority == null) { p.info.authority = names[j]; } else { p.info.authority = p.info.authority + ";" + names[j]; } if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.d(TAG, "Registered content provider: " + names[j] + ", className = " + p.info.name + ", isSyncable = " + p.info.isSyncable); } } else { PackageParser.Provider other = mProviders.get(names[j]); Slog.w(TAG, "Skipping provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + "): name already used by " + ((other != null && other.getComponentName() != null) ? other.getComponentName().getPackageName() : "?")); } } } if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(p.info.name); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r); } //处理该Package中的service信息 N = pkg.services.size(); r = null; for (i=0; i<N; i++) { PackageParser.Service s = pkg.services.get(i); s.info.processName = fixProcessName(pkg.applicationInfo.processName, s.info.processName, pkg.applicationInfo.uid); mServices.addService(s); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(s.info.name); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r); } //处理该Package中的receiver信息 N = pkg.receivers.size(); r = null; for (i=0; i<N; i++) { PackageParser.Activity a = pkg.receivers.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName, pkg.applicationInfo.uid); mReceivers.addActivity(a, "receiver"); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r); } //处理该Package中的Activity信息 N = pkg.activities.size(); r = null; for (i=0; i<N; i++) { PackageParser.Activity a = pkg.activities.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName, pkg.applicationInfo.uid); mActivities.addActivity(a, "activity"); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r); } N = pkg.permissionGroups.size(); r = null; for (i=0; i<N; i++) { PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i); PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name); if (cur == null) { mPermissionGroups.put(pg.info.name, pg); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(pg.info.name); } } else { Slog.w(TAG, "Permission group " + pg.info.name + " from package " + pg.info.packageName + " ignored: original from " + cur.info.packageName); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append("DUP:"); r.append(pg.info.name); } } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r); } N = pkg.permissions.size(); r = null; for (i=0; i<N; i++) { PackageParser.Permission p = pkg.permissions.get(i); HashMap<String, BasePermission> permissionMap = p.tree ? mSettings.mPermissionTrees : mSettings.mPermissions; p.group = mPermissionGroups.get(p.info.group); if (p.info.group == null || p.group != null) { BasePermission bp = permissionMap.get(p.info.name); if (bp == null) { bp = new BasePermission(p.info.name, p.info.packageName, BasePermission.TYPE_NORMAL); permissionMap.put(p.info.name, bp); } if (bp.perm == null) { if (bp.sourcePackage == null || bp.sourcePackage.equals(p.info.packageName)) { BasePermission tree = findPermissionTreeLP(p.info.name); if (tree == null || tree.sourcePackage.equals(p.info.packageName)) { bp.packageSetting = pkgSetting; bp.perm = p; bp.uid = pkg.applicationInfo.uid; if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(p.info.name); } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: base tree " + tree.name + " is from package " + tree.sourcePackage); } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: original from " + bp.sourcePackage); } } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append("DUP:"); r.append(p.info.name); } if (bp.perm == p) { bp.protectionLevel = p.info.protectionLevel; } } else { Slog.w(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " ignored: no group " + p.group); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r); } N = pkg.instrumentation.size(); r = null; for (i=0; i<N; i++) { PackageParser.Instrumentation a = pkg.instrumentation.get(i); a.info.packageName = pkg.applicationInfo.packageName; a.info.sourceDir = pkg.applicationInfo.sourceDir; a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir; a.info.dataDir = pkg.applicationInfo.dataDir; a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir; mInstrumentation.put(a.getComponentName(), a); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } } if (r != null) { if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r); } if (pkg.protectedBroadcasts != null) { N = pkg.protectedBroadcasts.size(); for (i=0; i<N; i++) { mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i)); } } pkgSetting.setTimeStamp(scanFileTime); } //到此,Package的私有财产完成了公有化改造。 return pkg;
这个函数的代码好长好长啊...............................................终于over了~~~
5.scanDirLI函数总结
scanDirLI函数用于对指定目录下的APK文件进行扫描,该函数的工作流程如下图:
扫描完APK文件后,Package的私有财产就充公了。PKMS提供了几个重要数据结构来保存这些财产。
第2大部分之第3小部分:扫描非系统Package
非系统Package是指那些不存储在系统目录下的APK文件,在构造函数中的代码如下:
// Prune any system packages that no longer exist. if (!mOnlyCore) { Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { //删除系统package中那些不存在的APK PackageSetting ps = psit.next(); if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 && !mPackages.containsKey(ps.name) && !mSettings.mDisabledSysPackages.containsKey(ps.name)) { psit.remove(); String msg = "System package " + ps.name + " no longer exists; wiping its data"; reportSettingsProblem(Log.WARN, msg); mInstaller.remove(ps.name, 0); mUserManager.removePackageForAllUsers(ps.name); } } } mAppInstallDir = new File(dataDir, "app"); //删除安装不成功的文件及临时文件 ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr(); //clean up list for(int i = 0; i < deletePkgsList.size(); i++) { //clean up here cleanupInstallFailedPackage(deletePkgsList.get(i)); } //delete tmp files deleteTempPackageFiles(); if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); //在普通模式下,还需要扫描/data/app以及/data/app_private目录 mAppInstallObserver = new AppDirObserver( mAppInstallDir.getPath(), OBSERVER_EVENTS, false); mAppInstallObserver.startWatching(); scanDirLI(mAppInstallDir, 0, scanMode, 0); mDrmAppInstallObserver = new AppDirObserver( mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); mDrmAppInstallObserver.startWatching(); scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode, 0); } else { mAppInstallObserver = null; mDrmAppInstallObserver = null; }
系统中存放APK文件的目录:
*系统Package目录包括:/system/frameworks、/system/app和/vendor/app。
*非系统Package目录包括:/data/app、/data/app-private。
第2大部分的总结:PKMS在这个阶段的任务十分繁重,操作繁琐,而且还要创建比较多的对象,所以它是一个耗时耗内存的操作。作者认为没有较好的方案的原因可能是因为APK之间存在一些依赖关系,导致不好优化。其实我觉得,系统的APK不存在依赖非系统的APK的情况,所以完全可以将非系统的APK的信息保存在文件中,启动时直接读取文件,这样就可以节省一些时间。
第3大部分:构造函数分析之扫尾工作
扫尾工作是:将第2大部分收集的信息再集中整理一次,比如将这些信息保存到文件中。代码如下:
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); Slog.i(TAG, "Time to scan packages: " + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. final boolean regrantPermissions = mSettings.mInternalSdkPlatform != mSdkVersion; if (regrantPermissions) Slog.i(TAG, "Platform changed from " + mSettings.mInternalSdkPlatform + " to " + mSdkVersion + "; regranting permissions for internal storage"); mSettings.mInternalSdkPlatform = mSdkVersion; //汇总并更新和Permission相关的信息 updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions); // 将信息写到package.xml、package.list及package-stopped.xml文件中 mSettings.writeLPr(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and // tidy. Runtime.getRuntime().gc(); mRequiredVerifierPackage = getRequiredVerifierLPr(); } // synchronized (mPackages) } // synchronized (mInstallLock) }
对PKMS构造函数的分析就到底为止了~~~~终于结束了!!!
知识点3:APK Installation分析(分析APK的安装及相关处理流程)
1.adb install分析
adb install有多个参数,举个例子,这里分析adb install frameworktest.apk。adb是一个命令,install是它的参数。此处直接跳转到处理Install参数的代码:
[--->commandline.c::adb_commandline]
int adb_commandline(int argc, char **argv) { ...... if(!strcmp(argv[0], "install")) { if (argc < 2) return usage(); //调用Install_app函数处理 return install_app(ttype, serial, argc, argv); } ...... }
[--->commandline.c::install_app]
int install_app(transport_type transport, char* serial, int argc, char** argv) { //要安装的APK现在还在Host机器上,要先把APK复制到手机中。 //这里需要设置复制目标的目录,如果安装在内部存储中,则目标目录为/data/local/tmp; //如果安装在SD卡上,则目标目录为/sdcard/tmp。 static const char *const DATA_DEST = "/data/local/tmp/%s"; static const char *const SD_DEST = "/sdcard/tmp/%s"; const char* where = DATA_DEST; char apk_dest[PATH_MAX]; char verification_dest[PATH_MAX]; char* apk_file; char* verification_file = NULL; int file_arg = -1; int err; int i; for (i = 1; i < argc; i++) { if (*argv[i] != '-') { file_arg = i; break; } else if (!strcmp(argv[i], "-i")) { // Skip the installer package name. i++; } else if (!strcmp(argv[i], "-s")) { where = SD_DEST; //-s参数指明该APK安装到SD卡上 } } if (file_arg < 0) { fprintf(stderr, "can't find filename in arguments\n"); return 1; } else if (file_arg + 2 < argc) { fprintf(stderr, "too many files specified; only takes APK file and verifier file\n"); return 1; } apk_file = argv[file_arg]; if (file_arg != argc - 1) { verification_file = argv[file_arg + 1]; } if (check_file(apk_file) || check_file(verification_file)) { return 1; } snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); if (verification_file != NULL) { snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); if (!strcmp(apk_dest, verification_dest)) { fprintf(stderr, "APK and verification file can't have the same name\n"); return 1; } } //获取目标文件的全路径,如果安装到内部存储中,则目标全路径为/data/local/tmp/安装包名,调用do_sync_push函数将此APK传送到手机的目标路径 err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */); if (err) { return err; } else { argv[file_arg] = apk_dest; /* destination name, not source location */ } //(a) Android 4.0新增了一个安装过程中的Verification功能。稍后介绍。 if (verification_file != NULL) { err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); if (err) { goto cleanup_apk; } else { argv[file_arg + 1] = verification_dest; /* destination name, not source location */ } } //(b) 执行pm命令。有意思! pm_command(transport, serial, argc, argv); if (verification_file != NULL) { delete_file(transport, serial, verification_dest); } cleanup_apk: //(c) 在手机中执行shell rm命令,删除刚才传送过去的目标APK文件。why? delete_file(transport, serial, apk_dest); return err; }
分析代码中的(a)(b)(c)三个关键点:
(a) 这个新增的Verification功能其实就是在安装时,把相关信息发送到指定的Verification程序(另外一个APK),由它对要安装的APK进行检查(Verify)。
(b) 调用pm_command进行安装,在下面分析。
(c) 为什么删除呢?因为PKMS在安装过程中会将该APK复制一份到/data/app目录下,所以/data/local/tmp目录下的对应文件就可以删除了。
2.pm分析
static int pm_command(transport_type transport, char* serial, int argc, char** argv) { char buf[4096]; snprintf(buf, sizeof(buf), "shell:pm"); while(argc-- > 0) { char *quoted; quoted = dupAndQuote(*argv++); strncat(buf, " ", sizeof(buf)-1); strncat(buf, quoted, sizeof(buf)-1); free(quoted); } //发送”shell:pm install”参数给手机端的adbd send_shellcommand(transport, serial, buf); return 0; }
手机端的adbd在收到客户端发来的shell pm命令时会启动一个shell,然后在其中执行pm。那pm到底是什么?为什么可以在shell下执行?
实际上,pm是一个脚本。
[--->/frameworks/base/cmds/pm/pm]
# Script to start "pm" on the device, which has a very rudimentary # shell. # base=/system export CLASSPATH=$base/framework/pm.jar exec app_process $base/bin com.android.commands.pm.Pm "$@"
在编译system.image时,Android.mk中会将该脚本复制到system/bin目录下。分析pm脚本克制,它就是通过app_process执行pm.jar包的main函数。
分析pm.java,app_process执行的就是它定义的main函数,它相当于java函数的入口函数。
public static void main(String[] args) { new Pm().run(args); } //直接分析run函数 public void run(String[] args) { boolean validCommand = false; ...... mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); ...... mArgs = args; String op = args[0]; mNextArg = 1; ...... if ("install".equals(op)) { runInstall(); return; } ...... }
接下来分析runInstall函数,代码如下:
private void runInstall() { int installFlags = 0; String installerPackageName = null; String opt; while ((opt=nextOption()) != null) { if (opt.equals("-l")) { installFlags |= PackageManager.INSTALL_FORWARD_LOCK; } else if (opt.equals("-r")) { installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } else if (opt.equals("-i")) { installerPackageName = nextOptionData(); ...... } ...... final Uri apkURI; final Uri verificationURI; // Populate apkURI, must be present final String apkFilePath = nextArg(); System.err.println("\tpkg: " + apkFilePath); if (apkFilePath != null) { apkURI = Uri.fromFile(new File(apkFilePath)); } else { System.err.println("Error: no package specified"); showUsage(); return; } // Populate verificationURI, optionally present //获取Verification Package的文件位置 final String verificationFilePath = nextArg(); if (verificationFilePath != null) { System.err.println("\tver: " + verificationFilePath); verificationURI = Uri.fromFile(new File(verificationFilePath)); } else { verificationURI = null; } //创建PackageInstallObserver,用于接收PKMS的安装结果 PackageInstallObserver obs = new PackageInstallObserver(); try { //(a)调用PKMS的installPackageWithVerification完成安装 mPm.installPackageWithVerification(apkURI, obs, installFlags, installerPackageName, verificationURI, null); synchronized (obs) { while (!obs.finished) { try { obs.wait(); //等待安装结果 } catch (InterruptedException e) { } } if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println("Success"); } else { System.err.println("Failure [" + installFailureToString(obs.result) + "]");//输出安装失败的原因 } } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); } }
pm解析参数后,最终通过PKMS的Binder客户端调用installPackageWithVerification以完成后续的安装工作。
以上写于2013-01-17 20:59
未完待续。。
先贴出来吧,有空看这块再补充,反思这半年.....