Android - PackageMangerService 分析 概述 PackageManagerService(简称 PKMS),是 Android 系统中核心服务之一,管理着所有跟 package 相关的工作,常见的比如安装、卸载应用。
PackageManagerService 是在 SystemServer 进程中启动的。如不了解 Android 是如何从开机到 Launcher 启动的过程,请先阅读:Android - 系统启动过程 。
PackageManagerService 启动 SystemServer 启动过程中涉及到的 PKMS 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private void startBootstrapServices () { Installer installer = mSystemServiceManager.startService(Installer.class); ... String cryptState = SystemProperties.get("vold.decrypt" ); if (ENCRYPTING_STATE.equals(cryptState)) { mOnlyCore = true ; } else if (ENCRYPTED_STATE.equals(cryptState)) { mOnlyCore = true ; } mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); } private void startOtherServices () { mSystemServiceManager.startService(MOUNT_SERVICE_CLASS); mPackageManagerService.performBootDexOpt(); /... mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mPackageManagerService.systemReady(); }
整个 system_server 进程启动过程,涉及 PKMS 服务的主要几个动作如下,接下来分别讲解每个过程:
PKMS.main()
PKMS.performBootDexOpt()
PKMS.systemReady()
PKMS.main() 1 2 3 4 5 6 7 8 9 public static PackageManagerService main (Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); ServiceManager.addService("package" , m); return m; }
该方法的主要功能创建 PKMS 对象,并将其注册到 ServiceManager
中,内部是一个 HashMap 的集合,存储了很多相关的 binder
服务,缓存起来,我们在使用的时候, 会通过 getService(key)
的方式去 map
中获取,ServiceManger 工作流程详见:Android - Binder 机制 。
关于 PKMS 对象的构造方法很长,分为以下几个阶段,每个阶段会输出相应的 EventLog。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public PackageManagerService (Context context, Installer installer, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); if (mSdkVersion <= 0 ) { Slog.w(TAG, "**** ro.build.version.sdk not set!" ); } String mode = SystemProperties.get("ro.bootmode" , "mode" ); engModeEnable = "engtest" .equals(mode) ? true : false ; Slog.i(TAG, "engModeEnable: " + engModeEnable + " ,mode:" + mode); mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; mNoDexOpt = "eng" .equals(SystemProperties.get("ro.build.type" )); mMetrics = new DisplayMetrics(); mSettings = new Settings(); mSettings.addSharedUserLPw("android.uid.system" , Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.phone" , RADIO_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.log" , LOG_UID, ApplicationInfo.FLAG_SYSTEM); mSettings.addSharedUserLPw("android.uid.nfc" , NFC_UID, ApplicationInfo.FLAG_SYSTEM); 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 ; } mPreInstallDir = new File("/system/preloadapp" ); mInstaller = new Installer(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); d.getMetrics(mMetrics); synchronized (mInstallLock) { synchronized (mPackages) { mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data" ); mAsecInternalPath = new File(dataDir, "app-asec" ).getPath(); mUserAppDataDir = new File(dataDir, "user" ); mDrmAppPrivateInstallDir = new File(dataDir, "app-private" ); sUserManager = new UserManager(mInstaller, mUserAppDataDir); readPermissions(); mRestoredSettings = mSettings.readLPw(); long startTime = SystemClock.uptimeMillis(); } } Runtime.getRuntime().gc(); LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl()); }
刚进入构造函数,就会遇到第一个较为复杂的数据结构 Settings
及它的 addSharedUserLPw()
函数。Settings 的作用是管理 Android 系统运行过程中的一些设置信息。到底是哪些信息呢?来看下面的分析。
Settings 先分析 addSharedUserLPw 函数。如下所示:
1 2 3 4 mSettings.addSharedUserLPw("android.uid.system" , Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM );
在进入对addSharedUserLPw 函数的分析前,先介绍一下 SYSTEM_UID 及相关知识。
Android 系统中 UID/GID 介绍:
UID 为用户 ID 的缩写,GID 为用户组 ID 的缩写,这两个概念均与 Linux 系统中进程的权限管理有关。一般说来,每一个进程都会有一个对应的 UID(即表示该进程属于哪个 user,不同 user 有不同权限)。一个进程也可分属不同的用户组(每个用户组都有对应的权限)。
提示 Linux 的 UID/GID 还可细分为几种类型,此处我们仅考虑普适意义的 UID/GID。
下面分析 addSharedUserLPw 函数,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 SharedUserSetting addSharedUserLPw (String name, int uid, int pkgFlags) { SharedUserSetting s = mSharedUsers.get(name); if (s != null ) { if (s.userId == uid) { return s; } return null ; } s = new SharedUserSetting(name, pkgFlags); s.userId = uid; if (addUserIdLPw(uid, s, name)) { mSharedUsers.put(name, s); return s; } return null ; }
从以上代码可知,Settings 中有一个 mSharedUsers 成员,该成员存储的是字符串与 SharedUserSetting 键值对,也就是说以字符串为 key 得到对应的 SharedUserSetting 对象。
那么 SharedUserSettings 是什么?它的目的是什么?来看一个例子。
该例子来源于 SystemUI 的 AndroidManifest.xml,如下所示:
1 2 3 4 5 6 <manifestxmlns:android="http: //schemas.android.com /apk /res /android " package ="com.android.systemui" coreApp ="true" android:sharedUserId ="android.uid.system" android:process ="system" > ...
在 xml 中,声明了一个名为 android:sharedUserId
的属性,其值为 android.uid.system
。 sharedUserId 看起来和 UID 有关,确实如此,它有两个作用:
两个或多个声明了同一种 sharedUserIds 的 APK 可共享彼此的数据,并且可运行在同一进程中。
更重要的是,通过声明特定的 sharedUserId,该 APK 所在进程将被赋予指定的 UID。例如,本例中的 SystemUI 声明了 system 的 uid,运行 SystemUI 的进程就可享有 system 用户所对应的权限(实际上就是将该进程的 uid 设置为 system 的 uid)了。
提示:除了在 AndroidManifest.xml 中声明 sharedUserId 外,Apk 在编译时还必须使用对应的证书进行签名。例如,本例的 SystemUI,在其 Android.mk 中需要额外声明 LOCAL_CERTIFICATE := platform,如此,才可获得指定的 UID。
通过以上介绍,我们能了解到如何组织一种数据结构来包括上面的内容。此处有三个关键点需注意:
XML 中 sharedUserId 属性指定了一个字符串,它是 UID 的字符串描述,故对应数据结构中也应该有这样一个字符串,这样就把代码和 XML 中的属性联系起来了。
在 Linux 系统中,真正的 UID 是一个整数,所以该数据结构中必然有一个整型变量。
多个 Package 可声明同一个 sharedUserId,因此该数据结构必然会保存那些声明了相同 sharedUserId的 Package 的某些信息。
了解了上面三个关键点,再来看 Android 是如何设计相应数据结构的,如图所示。
由上图可知:
Settings 类定义了一个 mSharedUsers 成员,它是一个 HashMap,以字符串(如“android.uid.system”)为Key,对应的 Value 是一个 SharedUserSettings 对象。
SharedUserSetting 派生自 GrantedPermissions 类,从 GrantedPermissions 类的命名可知,它和权限有关。SharedUserSetting 定义了一个成员变量 packages,类型为 HashSet,用于保存声明了相同 sharedUserId 的 Package 的权限设置信息。
每个 Package 有自己的权限设置。权限的概念由 PackageSetting 类表达。该类继承自 PackagesettingBase,而 PackageSettingBase 又继承自 GrantedPermissions。
Settings 中还有两个成员,一个是 mUserIds,另一个是 mOtherUserIds,这两位成员的类型分别是 ArrayList 和 SparseArray。其目的是以 UID 为索引,得到对应的 SharedUserSettings 对象。在一般情况下,以索引获取数组元素的速度,比以 key 获取 HashMap 中元素的速度要快很多。
提示:根据以上对 mUserIds 和 mOtherUserIds 的描述,可知这是典型的以空间换时间的做法。
下边来分析 addUserIdLPw 函数,它的功能就是将 SharedUserSettings 对象保存到对应的数组中,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private boolean addUserIdLPw (int uid, Object obj, Objectname) { if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) { return false ; } if (uid >= PackageManagerService.FIRST_APPLICATION_UID) { int N = mUserIds.size(); final int index = uid - PackageManagerService.FIRST_APPLICATION_UID; while (index >= N) { mUserIds.add(null ); N++; } mUserIds.set(index, obj); } else { mOtherUserIds.put(uid, obj); } return true ; }
readPermissions() 先来分析 readPermissions 函数,从其函数名可猜测到它和权限有关,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void readPermissions () { FilelibraryDir = new File(Environment.getRootDirectory(), "etc/permissions" ); for (File f : libraryDir.listFiles()) { if (f.getPath().endsWith("etc/permissions/platform.xml" )) { continue ; } readPermissionsFromXml(f); } finalFile permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml" ); readPermissionsFromXml(permFile); }
在 etc/permissions
目录下保存了一下配置文件:
函数 readPermissionsFromXml 使用 PULL 方式解析这些 XML 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 private void readPermissionsFromXml (File permFile) { FileReader permReader = null ; try { permReader = new FileReader(permFile); } try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(permReader); XmlUtils.beginDocument(parser, "permissions" ); while (true ) { String name = parser.getName(); if ("group" .equals(name)) { String gidStr = parser.getAttributeValue(null , "gid" ); if (gidStr != null ) { int gid = Integer.parseInt(gidStr); mGlobalGids = appendInt(mGlobalGids, gid); } } else if ("permission" .equals(name)) { String perm = parser.getAttributeValue(null , "name" ); perm = perm.intern(); readPermission(parser, perm); } else if ("assign-permission" .equals(name)) { String perm = parser.getAttributeValue(null , "name" ); String uidStr = parser.getAttributeValue(null , "uid" ); int uid = Process.getUidForName(uidStr); perm = perm.intern(); HashSet<String> perms = mSystemPermissions.get(uid); if (perms == null ) { perms = newHashSet < String > (); mSystemPermissions.put(uid, perms); } perms.add(perm); } else if ("library" .equals(name)) { String lname = parser.getAttributeValue(null , "name" ); String lfile = parser.getAttributeValue(null , "file" ); if (lname == null ) { } else if (lfile == null ) { } else { mSharedLibraries.put(lname, lfile); } } else if ("feature" .equals(name)) { String fname = parser.getAttributeValue(null , "name" ); { FeatureInfo fi = newFeatureInfo(); fi.name = fname; mAvailableFeatures.put(fname, fi); } } } } }
readPermissions 函数就是将 XML 中的标签转换成对应的数据结构。
readLPw() readLPw 函数的功能也是解析文件,不过这些文件的内容却是在 PKMS 正常启动后生成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Settings() { FiledataDir = Environment.getDataDirectory(); FilesystemDir = new File(dataDir, "system" ); systemDir.mkdirs(); 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" ); }
上面 5 个文件共分为三组,这里简单介绍一下这些文件的来历(不考虑临时的 backup 文件)。
packages.xml: PKMS 扫描完目标文件夹后会创建该文件。当系统进行程序安装、卸载和更新等操作时,均会更新该文件。该文件保存了系统中与 package 相关的一些信息。
packages.list:描述系统中存在的所有非系统自带的 APK 的信息。当这些程序有变动时,PKMS 就会更新该文件。
packages-stopped.xml:从系统自带的设置程序中进入应用程序页面,然后在选择强制停止(ForceStop)某个应用时,系统会将该应用的相关信息记录到此文件中。也就是该文件保存系统中被用户强制停止的 Package 的信息。
readLPw 的函数功能就是解析其中的 XML 文件的内容,然后建立并更新对应的数据结构。例如,停止的 package 重启之后依然是 stopped 状态。
第一阶段总结 PKMS 构造函数在第一阶段的工作,主要是扫描并解析 XML 文件,将其中的信息保存到特定的数据结构中。
第一阶段扫描的 XML 文件与权限及上一次扫描得到的 Package 信息有关,它为 PKMS 下一阶段的工作提供了重要的参考信息。
扫描 Package PKMS 构造函数第二阶段的工作就是扫描系统中的 APK 了。由于需要逐个扫描文件,因此手机上装的程序越多,PKMS 的工作量越大,系统启动速度也就越慢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 mRestoredSettings = mSettings.readLPw(); longstartTime = SystemClock.uptimeMillis(); intscanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX; if (mNoDexOpt) { scanMode |= SCAN_NO_DEX; } finalHashSet<String> libFiles = new HashSet<String>(); mFrameworkDir = newFile(Environment.getRootDirectory(), "framework" ); mDalvikCacheDir = new File(dataDir, "dalvik-cache" ); booleandidDexOpt = false ; StringbootClassPath = System.getProperty("java.boot.class.path" ); if (bootClassPath != null ) { String[] paths = splitString(bootClassPath, ':' ); for (int i = 0 ; i < paths.length; i++) { try { if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { libFiles.add(paths[i]); mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true ); didDexOpt = true ; } } } } if (mSharedLibraries.size() > 0 ) { } libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk" ); String[] frameworkFiles = mFrameworkDir.list(); if (frameworkFiles != null ) { } if (didDexOpt) { String[] files = mDalvikCacheDir.list(); if (files != null ) { for (int i = 0 ; i < files.length; i++) { String fn = files[i]; if (fn.startsWith("data@app@" ) || fn.startsWith("data@app-private@" )) { (newFile(mDalvikCacheDir, fn)).delete(); } } } }
清空 cache 文件后,PKMS 终于进入重点段了。接下来看 PKMS 第二阶段工作的核心内容,即扫描 Package。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 mFrameworkInstallObserver = new AppDirObserver(mFrameworkDir.getPath(), OBSERVER_EVENTS, true ); mFrameworkInstallObserver.startWatching(); scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_NO_DEX, 0 ); mSystemAppDir = new File(Environment.getRootDirectory(), "app" ); mSystemInstallObserver = new AppDirObserver(mSystemAppDir.getPath(), OBSERVER_EVENTS, true ); mSystemInstallObserver.startWatching(); scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0 ); 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 ); mInstaller.moveFiles();
由以上代码可知,PKMS 将扫描以下几个目录。
/system/frameworks:该目录中的文件都是系统库,例如:framework.jar、services.jar、framework-res.apk。不过 scanDirLI 只扫描APK文件,所以 framework-res.apk 是该目录中唯一“受宠”的文件。
/system/app:该目录下全是默认的系统应用,例如:Browser.apk、SettingsProvider.apk 等。
/vendor/app:该目录中的文件由厂商提供,即厂商特定的 APK 文件,不过目前市面上的厂商都把自己的应用放在 /system/app 目录下。
PKMS 调用 scanDirLI 函数进行扫描,下面来分析此函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private void scanDirLI (File dir, int flags, int scanMode, long currentTime) { String[] files = dir.list(); int i; for (i = 0 ; i < files.length; i++) { File file = new File(dir, files[i]); if (!isPackageFilename(files[i])) { continue ; } PackageParser.Package pkg = scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { file.delete(); } } }
接着来分析 scanPackageLI 函数。PKMS 中有两个同名的 scanPackageLI 函数,后面会一一见到。先来看第一个也是最先碰到的 scanPackageLI 函数。
scanPackageLI() 首次相遇的 scanPackageLI 函数的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 private PackageParser.Package scanPackageLI (FilescanFile, int parseFlags, int scanMode, long currentTime) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; StringscanPath = scanFile.getPath(); parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(scanPath); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); finalPackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags); PackageSetting ps = null ; PackageSetting updatedPkg; if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) return null ; 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 ) { } else { resPath = pkg.mScanPath; } codePath = pkg.mScanPath; setApplicationInfoPaths(pkg, codePath, resPath); return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime); }
scanPackageLI 函数首先调用 PackageParser 对 APK 文件进行解析。根据前面的介绍可知,PackageParser 完成了从物理文件到对应数据结构的转换。下面来分析这个 PackageParser。
PackageParser PackageParser 主要负责 APK 文件的解析,即解析 APK 文件中的 AndroidManifest.xml 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public Package parsePackage (File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) { mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = sourceFile.getPath(); 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); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); assetError = false ; } String[] errorText = new String[1 ]; Package pkg = null ; Exception errorException = null ; try { pkg = parsePackage(res, parser, flags, errorText); } parser.close(); assmgr.close(); pkg.mPath = destCodePath; pkg.mScanPath = mArchiveSourcePath; pkg.mSignatures = null ; return pkg; } }
以上代码中调用了另一个同名的 parsePackage 函数,此函数内容较长,但功能单一,就是解析 AndroidManifest.xml 中的各种标签,这里只提取其中相关的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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 ; String pkgName = parsePackageName(parser, attrs, flags, outError); int type; final Package pkg = new Package(pkgName); boolean foundApp = false ; while (如果解析未完成) { StringtagName = parser.getName(); if (tagName.equals("application" )) { parseApplication(pkg, res, parser, attrs, flags, outError); } else if (tagName.equals("permission-group" )) { parsePermissionGroup(pkg, res, parser, attrs, outError); } else if (tagName.equals("permission" )) { parsePermission(pkg, res, parser, attrs, outError); } else if (tagName.equals("uses-permission" )) { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission); String name = sa.getNonResourceString(com.android.internal. R.styleable.AndroidManifestUsesPermission_name); if (name != null && !pkg.requestedPermissions.contains(name)) { pkg.requestedPermissions.add(name.intern()); } } else if (tagName.equals("uses-configuration" )) { ConfigurationInfocPref = new ConfigurationInfo(); pkg.configPreferences.add(cPref); } } }
上面代码展示了 AndroidManifest.xml 解析的流程,其中比较重要的函数是 parserApplication,它用于解析 application 标签及其子标签(Android 的四大组件在 application 标签中已声明)。
PackageParser 及其内部重要成员的信息。
PackageParser 定了相当多的内部类,这些内部类的作用就是保存对应的信息。解析 AndroidManifest.xml 文件得到的信息由 Package 保存。从该类的成员变量可看出,和 Android 四大组件相关的信息分别由 activites、receivers、providers、services 保存。由于一个 APK 可声明多个组件,因此 activites 和 receiver s等均声明为 ArrayList。
以 PackageParser.Activity 为例,它从 Component 派生。Component 是一个模板类,元素类型是 ActivityIntentInfo,此类的顶层基类是 IntentFilter。PackageParser.Activity 内部有一个 ActivityInfo 类型的成员变量,该变量保存的就是四大组件中 Activity 的信息。细心的读者可能会有疑问,为什么不直接使用 ActivityInfo,而是通过 IntentFilter 构造出一个使用模板的复杂类型 PackageParser.Activity 呢?原来,Package 除了保存信息外,还需要支持 Intent 匹配查询。例如,设置 Intent 的 Action 为某个特定值,然后查找匹配该 Intent 的 Activity。由于 ActivityIntentInfo 是从 IntentFilter 派生的,因此它它能判断自己是否满足该 Intent 的要求,如果满足,则返回对应的 ActivityInfo。
PackageParser 定了一个轻量级的数据结构 PackageLite,该类仅存储 Package 的一些简单信息。我们在介绍 Package 安装的时候,会遇到 PackageLite。
在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象,下一步就是将该 Package 加入到系统中。此时调用的函数就是另外一个 scanPackageLI,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private PackageParser.PackagescanPackageLI( PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime) { FilescanFile = new File(pkg.mScanPath); mScanningPath = scanFile; if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0 ) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; } if (pkg.packageName.equals("android" )) { synchronized (mPackages) { if (mAndroidApplication != null ) { mPlatformPackage = pkg; pkg.mVersionCode = mSdkVersion; mAndroidApplication = pkg.applicationInfo; 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; mResolveInfo.priority = 0 ; mResolveInfo.preferredOrder = 0 ; mResolveInfo.match = 0 ; mResolveComponentName = new ComponentName( mAndroidApplication.packageName, mResolveActivity.name); } } } }
刚进入 scanPackageLI 函数,我们就发现了一个极为重要的内容,即单独判断并处理 packageName 为 android
的 Package。和该 Package 对应的APK是 framework-res.apk,有图为证。
framework-res.apk 的 AndroidManifest.xml:
实际上,framework-res.apk 还包含了以下几个常用的 Activity。
ChooserActivity:当多个 Activity 符合某个 Intent 的时候,系统会弹出此 Activity,由用户选择合适的应用来处理。
RingtonePickerActivity:铃声选择 Activity。
ShutdownActivity:关机前弹出的选择对话框。
由前述知识可知,该 Package 和系统息息相关,因此它得到了 PKMS 的特别青睐,主要体现在以下几点。
mPlatformPackage 成员用于保存该 Package 信息。
mAndroidApplication 用于保存此 Package 中的 ApplicationInfo。
mResolveActivity 指向用于表示 ChooserActivity 信息的 ActivityInfo。
mResolveInfo 为 ResolveInfo 类型,它用于存储系统解析 Intent(经 IntentFilter 的过滤)后得到的结果信息,例如:满足某个 Intent 的 Activity 的信息。由前面的代码可知,mResolveInfo 的 activityInfo 其实指向的就是 mResolveActivity。
注意:在从 PKMS 中查询满足某个 Intent 的 Activity 时,返回的就是 ResolveInfo,再根据 ResolveInfo 的信息得到具体的 Activity。
此处保存这些信息,主要是为了提高运行过程中的效率。Goolge工 程师可能觉得 ChooserActivity 使用的地方比较多,所以这里单独保存了此 Activity 的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 if (mPackages.containsKey(pkg.packageName) || mSharedLibraries.containsKey(pkg.packageName)) { return null ; } File destCodeFile = newFile(pkg.applicationInfo.sourceDir); FiledestResourceFile = new File(pkg.applicationInfo.publicSourceDir); SharedUserSettingsuid = null ; PackageSetting pkgSetting = null ; synchronized (mPackages) { } final long scanFileTime = scanFile.lastModified();final boolean forceDex = (scanMode & SCAN_FORCE_DEX) != 0 ;pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, pkg.applicationInfo.uid); if (mPlatformPackage == pkg) { dataPath = new File(Environment.getDataDirectory(), "system" ); pkg.applicationInfo.dataDir = dataPath.getPath(); } else { dataPath = getDataPathForPackage(pkg.packageName, 0 ); if (dataPath.exists()) { } else { int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid); if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null ) { } } if (pkg.applicationInfo.nativeLibraryDir != null ) { try { final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { NativeLibraryHelper.removeNativeBinariesFromDirLI( nativeLibraryDir)){ } else if (nativeLibraryDir.getParentFile().getCanonicalPath() .equals(dataPathString)) { boolean isSymLink; try { isSymLink = S_ISLNK(Libcore.os.lstat( nativeLibraryDir.getPath()).st_mode); } if (isSymLink) { mInstaller.unlinkNativeLibraryDirectory(dataPathString); } NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir); } else { mInstaller.linkNativeLibraryDirectory(dataPathString, pkg.applicationInfo.nativeLibraryDir); } } } pkg.mScanPath = path; if ((scanMode & SCAN_NO_DEX) == 0 ) { performDexOptLI(pkg, forceDex, (scanMode & SCAN_DEFER_DEX); } if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0 ) { killApplication(pkg.applicationInfo.packageName, pkg.applicationInfo.uid); } synchronized (mPackages) { if ((scanMode & SCAN_MONITOR) != 0 ) { mAppDirs.put(pkg.mPath, pkg); } mSettings.insertPackageSettingLPw(pkgSetting, pkg); mPackages.put(pkg.applicationInfo.packageName, pkg); int N = pkg.providers.size(); int i; for (i = 0 ; i < N; i++) { PackageParser.Providerp = pkg.providers.get(i); p.info.processName = fixProcessName( pkg.applicationInfo.processName, p.info.processName, pkg.applicationInfo.uid); mProvidersByComponent.put(new ComponentName( } N = pkg.services.size(); r = null ; for (i = 0 ; i < N; i++) { PackageParser.Service s = pkg.services.get(i); mServices.addService(s); } N = pkg.receivers.size(); r = null ; for (i = 0 ; i < N; i++) { PackageParser.Activity a = pkg.receivers.get(i); mReceivers.addActivity(a, "receiver" ); } N = pkg.activities.size(); r = null ; for (i = 0 ; i < N; i++) { PackageParser.Activity a = pkg.activities.get(i); mActivities.addActivity(a, "activity" ); } N = pkg.permissionGroups.size(); N = pkg.permissions.size(); N = pkg.instrumentation.size(); if (pkg.protectedBroadcasts != null ) { N = pkg.protectedBroadcasts.size(); for (i = 0 ; i < N; i++) { mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i)); } } return pkg; } } }
scanPackageLI() 总结
扫描非系统 Package,非系统 Package 就是指那些不存储在系统目录下的 APK 文件,这部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 if (!mOnlyCore) { Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { } mAppInstallDir = new File(dataDir, "app" ); if (!mOnlyCore) { mAppInstallObserver = new AppDirObserver( mAppInstallDir.getPath(), OBSERVER_EVENTS, false ); mAppInstallObserver.startWatching(); scanDirLI(mAppInstallDir, 0 , scanMode, 0 ); mDrmAppInstallObserver = newAppDirObserver( 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。
第二阶段总结 PKMS 构造函数第二阶段的工作任务非常繁重,要创建比较多的对象,所以它是一个耗时耗内存的操作。在工作中,我们一直想优化该流程以加快启动速度,例如:延时扫描不重要的 APK,或者保存 Package 信息到文件中,然后在启动时从文件中恢复这些信息以减少 APK 文件读取并解析 XML 的工作量。但是一直没有一个比较完满的解决方案,原因有很多。比如:APK 之间有着比较微妙的依赖关系,因此到底延时扫描哪些 APK,尚不能确定。
构造函数扫尾工作 下面分析 PKMS 第三阶段的工作,这部分任务比较简单,就是将第二阶段收集的信息再集中整理一次,比如将有些信息保存到文件中,相关代码如下:
1 2 3 4 5 6 7 8 9 10 11 mSettings.mInternalSdkPlatform= mSdkVersion; updatePermissionsLPw(null , null , true , regrantPermissions,regrantPermissions); mSettings.writeLPr(); Runtime.getRuntime().gc(); mRequiredVerifierPackage= getRequiredVerifierLPr(); }
从流程角度看,PKMS 构造函数的功能还算清晰,无非是扫描 XML 或 APK 文件,但是其中涉及的数据结构及它们之间的关系却较为复杂。这里有一些建议供读者参考:
理解 PKMS 构造函数工作的三个阶段及其各阶段的工作职责。
了解 PKMS 第二阶段工作中解析 APK 文件的几个关键步骤。
了解重点数据结构的名字和大体功能。
获取 PackageManager 服务 ContextImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 public PackageManager getPackageManager () { if (mPackageManager != null ) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null ) { return (mPackageManager = new ApplicationPackageManager(this , pm)); } return null ; }
获取 PKMS 服务,并创建 ApplicationPackageManager 对象。
ActivityThread.java
1 2 3 4 5 6 7 8 public static IPackageManager getPackageManager () { if (sPackageManager != null ) { return sPackageManager; } IBinder b = ServiceManager.getService("package" ); sPackageManager = IPackageManager.Stub.asInterface(b); return sPackageManager; }
与 ServiceManager 通讯获取到 PKMS 的代理对象。
PackageManagerService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 public void performBootDexOpt () { enforceSystemOrRoot("Only the system can request dexopt be performed" ); IMountService ms = PackageHelper.getMountService(); if (ms != null ) { final boolean isUpgrade = isUpgrade(); boolean doTrim = isUpgrade; if (doTrim) { Slog.w(TAG, "Running disk maintenance immediately due to system update" ); } else { final long interval = android.provider.Settings.Global.getLong( mContext.getContentResolver(), android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL, DEFAULT_MANDATORY_FSTRIM_INTERVAL); if (interval > 0 ) { final long timeSinceLast = System.currentTimeMillis() - ms.lastMaintenance(); if (timeSinceLast > interval) { doTrim = true ; } } } if (doTrim) { ms.runMaintenance(); } } final ArraySet<PackageParser.Package> pkgs; synchronized (mPackages) { pkgs = mPackageDexOptimizer.clearDeferredDexOptPackages(); } if (pkgs != null ) { ArrayList<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>(); for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { PackageParser.Package pkg = it.next(); if (pkg.coreApp) { sortedPkgs.add(pkg); it.remove(); } } Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); ArraySet<String> pkgNames = getPackageNamesForIntent(intent); for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { PackageParser.Package pkg = it.next(); if (pkgNames.contains(pkg.packageName)) { sortedPkgs.add(pkg); it.remove(); } } filterRecentlyUsedApps(pkgs); for (PackageParser.Package pkg : pkgs) { sortedPkgs.add(pkg); } if (mLazyDexOpt) { filterRecentlyUsedApps(sortedPkgs); } int i = 0 ; int total = sortedPkgs.size(); File dataDir = Environment.getDataDirectory(); long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir); ... for (PackageParser.Package pkg : sortedPkgs) { long usableSpace = dataDir.getUsableSpace(); if (usableSpace < lowThreshold) { break ; } performBootDexOpt(pkg, ++i, total); } } } private void filterRecentlyUsedApps (Collection<PackageParser.Package> pkgs) { if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) { int total = pkgs.size(); int skipped = 0 ; long now = System.currentTimeMillis(); for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) { PackageParser.Package pkg = i.next(); long then = pkg.mLastPackageUsageTimeInMills; if (then + mDexOptLRUThresholdInMills < now) { i.remove(); skipped++; } } } } private void performBootDexOpt (PackageParser.Package pkg, int curr, int total) { if (!isFirstBoot()) { ActivityManagerNative.getDefault().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, curr, total), true ); } PackageParser.Package p = pkg; synchronized (mInstallLock) { mPackageDexOptimizer.performDexOpt(p, null , false , false , true , false , false ); } }
PKMS.systemReady() PackageManagerService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void systemReady () { mSystemReady = true ; ... synchronized (mPackages) { ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>(); for (int i=0 ; i<mSettings.mPreferredActivities.size(); i++) { PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); removed.clear(); for (PreferredActivity pa : pir.filterSet()) { if (mActivities.mActivities.get(pa.mPref.mComponent) == null ) { removed.add(pa); } } if (removed.size() > 0 ) { for (int r=0 ; r<removed.size(); r++) { PreferredActivity pa = removed.get(r); pir.removeFilter(pa); } mSettings.writePackageRestrictionsLPr( mSettings.mPreferredActivities.keyAt(i)); } } for (int userId : UserManagerService.getInstance().getUserIds()) { if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) { grantPermissionsUserIds = ArrayUtils.appendInt( grantPermissionsUserIds, userId); } } } sUserManager.systemReady(); for (int userId : grantPermissionsUserIds) { mDefaultPermissionPolicy.grantDefaultPermissions(userId); } if (mPostSystemReadyMessages != null ) { for (Message msg : mPostSystemReadyMessages) { msg.sendToTarget(); } mPostSystemReadyMessages = null ; } final StorageManager storage = mContext.getSystemService(StorageManager.class); storage.registerListener(mStorageListener); mInstallerService.systemReady(); mPackageDexOptimizer.systemReady(); MountServiceInternal mountServiceInternal = LocalServices.getService(MountServiceInternal.class); mountServiceInternal.addExternalStoragePolicy(...); }
PackageManagerService 启动完整流程图:
installd PackageManagerServie 服务负责应用的安装、卸载等相关工作,而真正干活的还是 installd。 其中 PKMS 执行权限为 system,而进程 installd 的执行权限为 root。
启动 installd 是由 Android 系统的 init 进程(pid=1),在解析 init.rc 文件的如下代码块时,通过 fork 创建的用户空间的守护进程 installd。
1 2 3 service installd /system/bin/installd class main socket installd stream 600 system system
installd 是随着系统启动过程中 main class 而启动的,并且会创建一个 socket 套接字,用于跟上层的 PKMS 进行交互。 installd 的启动入口 frameworks/base/cmds/installd/installd.c 的 main() 方法,接下来从这里开始说起。
installd.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 int main (const int argc __unused, char *argv[]) { char buf[BUFFER_MAX]; struct sockaddr addr ; socklen_t alen; int lsocket, s; if (initialize_globals() < 0 ) { exit (1 ); } if (initialize_directories() < 0 ) { exit (1 ); } lsocket = android_get_control_socket(SOCKET_PATH); if (listen(lsocket, 5 )) { exit (1 ); } fcntl(lsocket, F_SETFD, FD_CLOEXEC); for (;;) { alen = sizeof (addr); s = accept(lsocket, &addr, &alen); if (s < 0 ) { continue ; } fcntl(s, F_SETFD, FD_CLOEXEC); for (;;) { unsigned short count; if (readx(s, &count, sizeof (count))) { break ; } if ((count < 1 ) || (count >= BUFFER_MAX)) { break ; } if (readx(s, buf, count)) { break ; } buf[count] = 0 ; if (execute(s, buf)) break ; } close(s); } return 0 ; }
installd.cpp -> initialize_globals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 int initialize_globals () { if (get_path_from_env(&android_data_dir, "ANDROID_DATA" ) < 0 ) { return -1 ; } if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0 ) { return -1 ; } if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0 ) { return -1 ; } if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0 ) { return -1 ; } if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT" ) < 0 ) { return -1 ; } if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0 ) { return -1 ; } if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/" ) < 0 ) { return -1 ; } android_system_dirs.count = 4 ; android_system_dirs.dirs = (dir_rec_t *) calloc (android_system_dirs.count, sizeof (dir_rec_t )); dir_rec_t android_root_dir; if (get_path_from_env(&android_root_dir, "ANDROID_ROOT" ) < 0 ) { return -1 ; } android_system_dirs.dirs[0 ].path = build_string2(android_root_dir.path, APP_SUBDIR); android_system_dirs.dirs[0 ].len = strlen (android_system_dirs.dirs[0 ].path); android_system_dirs.dirs[1 ].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR); android_system_dirs.dirs[1 ].len = strlen (android_system_dirs.dirs[1 ].path); android_system_dirs.dirs[2 ].path = strdup("/vendor/app/" ); android_system_dirs.dirs[2 ].len = strlen (android_system_dirs.dirs[2 ].path); android_system_dirs.dirs[3 ].path = strdup("/oem/app/" ); android_system_dirs.dirs[3 ].len = strlen (android_system_dirs.dirs[3 ].path); return 0 ; }
installd.cpp -> initialize_directories
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int initialize_directories () { int res = -1 ; char version_path[PATH_MAX]; snprintf (version_path, PATH_MAX, "%s.layout_version" , android_data_dir.path); int oldVersion; if (fs_read_atomic_int(version_path, &oldVersion) == -1 ) { oldVersion = 0 ; } int version = oldVersion; char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX); char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX); char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0" ); if (access(primary_data_dir, R_OK) < 0 ) { if (symlink(legacy_data_dir, primary_data_dir)) { goto fail; } } return res; }
installd.cpp -> execute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 static int execute (int s, char cmd[BUFFER_MAX]) { char reply[REPLY_MAX]; char *arg[TOKEN_MAX+1 ]; unsigned i; unsigned n = 0 ; unsigned short count; int ret = -1 ; reply[0 ] = 0 ; arg[0 ] = cmd; while (*cmd) { if (isspace (*cmd)) { *cmd++ = 0 ; n++; arg[n] = cmd; if (n == TOKEN_MAX) { goto done; } } if (*cmd) { cmd++; } } for (i = 0 ; i < sizeof (cmds) / sizeof (cmds[0 ]); i++) { if (!strcmp (cmds[i].name,arg[0 ])) { if (n != cmds[i].numargs) { ALOGE("%s requires %d arguments (%d given)\n" , cmds[i].name, cmds[i].numargs, n); } else { ret = cmds[i].func(arg + 1 , reply); } goto done; } } done: if (reply[0 ]) { n = snprintf (cmd, BUFFER_MAX, "%d %s" , ret, reply); } else { n = snprintf (cmd, BUFFER_MAX, "%d" , ret); } if (n > BUFFER_MAX) n = BUFFER_MAX; count = n; if (writex(s, &count, sizeof (count))) return -1 ; if (writex(s, cmd, count)) return -1 ; return 0 ; }
Installer 当守护进程 installd 启动完成后,上层 framework 便可以通过 socket 跟该守护进程进行通信。 在 SystemServer 启动服务的过程中创建 Installer 对象,便会有一次跟 installd 通信的过程。
SystemServer.java
1 2 3 4 5 private void startBootstrapServices () { Installer installer = mSystemServiceManager.startService(Installer.class); }
Installer.java
1 2 3 4 5 6 7 8 9 10 public Installer (Context context) { super (context); mInstaller = new InstallerConnection(); } public void onStart () { Slog.i(TAG, "Waiting for installd to be ready." ); mInstaller.waitForConnection(); }
先创建 Installer 对象,再调用 onStart() 方法,该方法中主要工作是等待 socket 通道建立完成。
InstallerConnection.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public void waitForConnection () { for (;;) { if (execute("ping" ) >= 0 ) { return ; } Slog.w(TAG, "installd not ready" ); SystemClock.sleep(1000 ); } } public int execute (String cmd) { String res = transact(cmd); try { return Integer.parseInt(res); } catch (NumberFormatException ex) { return -1 ; } } public synchronized String transact (String cmd) { if (!connect()) { return "-1" ; } if (!writeCommand(cmd)) { if (!connect() || !writeCommand(cmd)) { return "-1" ; } } final int replyLength = readReply(); if (replyLength > 0 ) { String s = new String(buf, 0 , replyLength); return s; } else { return "-1" ; } } private boolean connect () { if (mSocket != null ) { return true ; } Slog.i(TAG, "connecting..." ); try { mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress("installd" , LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream(); } catch (IOException ex) { disconnect(); return false ; } return true ; } private boolean writeCommand (String cmdString) { final byte [] cmd = cmdString.getBytes(); final int len = cmd.length; if ((len < 1 ) || (len > buf.length)) { return false ; } buf[0 ] = (byte ) (len & 0xff ); buf[1 ] = (byte ) ((len >> 8 ) & 0xff ); try { mOut.write(buf, 0 , 2 ); mOut.write(cmd, 0 , len); } catch (IOException ex) { disconnect(); return false ; } return true ; } private int readReply () { if (!readFully(buf, 2 )) { return -1 ; } final int len = (((int ) buf[0 ]) & 0xff ) | ((((int ) buf[1 ]) & 0xff ) << 8 ); if ((len < 1 ) || (len > buf.length)) { disconnect(); return -1 ; } if (!readFully(buf, len)) { return -1 ; } return len; } private boolean readFully (byte [] buffer, int len) { try { Streams.readFully(mIn, buffer, 0 , len); } catch (IOException ioe) { disconnect(); return false ; } return true ; }
可见,一次 transact 过程为先 connect() 来判断是否建立 socket 连接,如果已连接则通过 writeCommand() 将命令写入 socket 的 mOut 管道,等待从管道的 mIn 中 readFully() 读取应答消息。
Apk 安装过程分析 adb install 分析 adb install 有多个参数,这里仅考虑最简单的,如: adb install frameworktest.apk
。adb 是一个命令,install 是它的参数。此处直接跳到处理 install 参数的代码:
commandline.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 int adb_commandline (int argc, char **argv) { if (!strcmp (argv[0 ], "install" )) { return install_app(ttype, serial, argc, argv); } } int install_app (transport_type transport, char *serial, int argc, char ** argv) { 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" )) { i++; } else if (!strcmp (argv[i], "-s" )) { where = SD_DEST; } } apk_file = argv[file_arg]; err = do_sync_push(apk_file, apk_dest, 1 ); pm_command(transport,serial, argc, argv); cleanup_apk: delete_file(transport, serial, apk_dest); return err; }
commandline.c
1 2 3 4 5 6 7 8 9 static int pm_command (transport_type transport,char * serial, int argc, char ** argv) { char buf[4096 ]; snprintf (buf,sizeof (buf), "shell:pm" ); send_shellcommand(transport, serial, buf); return 0 ; }
手机端的 adbd 在收到客户端发来的 shell:pm 命令时会启动一个 shell,然后在其中执行 pm。
pm 实际上是一个脚本,其内容如下:
1 2 3 4 5 6 7 # Script to start "pm" on the device,which has a very rudimentary # shell. # base=/system export CLASSPATH=$base/frameworks/pm.jarexec app_process $base/bincom.android.commands.pm.Pm "$@"
在编译 system.image 时,Android.mk 中会将该脚本复制到 system/bin 目录下。从 pm 脚本的内容来看,它就是通过 app_process 执行 pm.jar 包的 main 函数。
pm.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public static void main (String[] args) { new Pm().run(args); } 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 ; } } private void runInstall () { intinstallFlags = 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; final String apkFilePath = nextArg(); System.err.println("/tpkg: " + apkFilePath); if (apkFilePath != null ) { apkURI = Uri.fromFile(new File(apkFilePath)); } final String verificationFilePath = nextArg(); if (verificationFilePath != null ) { verificationURI = Uri.fromFile(new File(verificationFilePath)); }else { verificationURI = null ; } PackageInstallObserver obs = new PackageInstallObserver(); try { mPm.installPackageWithVerification(apkURI, obs, installFlags,installerPackageName, verificationURI,null ); synchronized (obs) { while (!obs.finished) { try { obs.wait(); } } if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println("Success" ); } } }
Pm 解析参数后,最终通过 PKMS 的 Binder 客户端调用 installPackageWithVerification 以完成后续的安装工作,所以,下面进入 PKMS 看看安装到底是怎么一回事。
installPackageWithVerification 分析 PackageManagerService.java::installPackageWithVerification
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void installPackageWithVerification (UripackageURI, IPackageInstallObserverobserver, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INSTALL_PACKAGES,null); final int uid = Binder.getCallingUid(); final int filteredFlags; if (uid == Process.SHELL_UID || uid == 0 ) { filteredFlags = flags | PackageManager.INSTALL_FROM_ADB; }else { filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB; } final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, filteredFlags,installerPackageName, verificationURI,manifestDigest); mHandler.sendMessage(msg); }
INIT_COPY 处理 INIT_COPY 只是安装流程的第一步。先来看相关代码:
PackageManagerService.java::handleMesssage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public void handleMessage (Message msg) { try { doHandleMessage(msg); } } void doHandleMessage (Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (!mBound) { if (!connectToService()) { params.serviceError(); return ; } else { mPendingInstalls.add(idx, params); } } else { mPendingInstalls.add(idx, params); if (idx == 0 ) { mHandler.sendEmptyMessage(MCS_BOUND); } } break ; } case MCS_BOUND: { } } }
这里假设之前已经成功启动了 DefaultContainerService(以后简称 DCS),并且 idx 为零,所以这是 PKMS 首次处理安装请求,也就是说,下一个将要处理的是 MCS_BOUND 消息。
MCS_BOUND 处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void doHandleMessage (Message msg) { switch (msg.what) { case INIT_COPY: { } case MCS_BOUND: { if (msg.obj != null ) { mContainerService = (IMediaContainerService) msg.obj; } if (mContainerService == null ) { if (!mBound) { mPendingInstalls.clear(); } } else if (mPendingInstalls.size() > 0 ) { HandlerParams params = mPendingInstalls.get(0 ); if (params != null ) { if (params.startCopy()) { if (mPendingInstalls.size() > 0 ) { mPendingInstalls.remove(0 ); } if (mPendingInstalls.size() == 0 ) { if (mBound) { removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000 ); } } else { mHandler.sendEmptyMessage(MCS_BOUND); } } } } break ; } } }
MCS_BOUND 的处理还算简单,就是调用 HandlerParams 的 startCopy 函数。
PackageManagerService.java::HandlerParams.startCopy()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 final boolean startCopy () { booleanres; try { if (++mRetries > MAX_RETRIES) { mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false ; } else { handleStartCopy(); res= true ; } } ... handleReturnCode(); return res; }
在上述代码中,基类的 startCopy 将调用子类实现的 handleStartCopy 和 handleReturnCode 函数。下面来看 InstallParams 是如何实现这两个函数的。
InstallParams 分析 PackageManagerService::InstallParams.handleStartCopy()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 public void handleStartCopy () throwsRemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; final boolean fwdLocked = (flags &PackageManager.INSTALL_FORWARD_LOCK) != 0 ; final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0 ; final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0 ; PackageInfoLite pkgLite = null ; if (onInt && onSd) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (fwdLocked && onSd) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { final long lowThreshold; final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager.getService( DeviceStorageMonitorService.SERVICE); if (dsm == null ) { lowThreshold = 0L ; }else { lowThreshold = dsm.getMemoryLowThreshold(); } try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,Intent.FLAG_GRANT_READ_URI_PERMISSION); pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags,lowThreshold); }finally int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if ...{ } else { loc = installLocationPolicy(pkgLite, flags); if (!onSd && !onInt) { if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { flags |= PackageManager.INSTALL_EXTERNAL; flags &=~PackageManager.INSTALL_INTERNAL; } } } } final InstallArgs args = createInstallArgs(this ); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage); if (requiredUid != -1 && isVerificationEnabled()) { } else { ret = args.copyApk(mContainerService, true ); } } mRet = ret; }
在以上代码中,一共列出了五个关键点,总结如下:
调用 DCS 的 getMinimalPackageInfo 函数,将得到一个 PackageLite 对象,该对象是一个轻量级的用于描述 APK 的结构(相比PackageParser.Package 来说)。在这段代码逻辑中,主要想取得其 recommendedInstallLocation 的值。此值表示该 APK 推荐的安装路径。
调用 installLocationPolicy 检查推荐的安装路径。例如:系统 Package 不允许安装在 SD 卡上。
createInstallArgs 将根据安装位置创建不同的 InstallArgs。如果是内部存储,则返回 FileInstallArgs,否则为 SdInstallArgs。
在正式安装前,应先对该 APK 进行必要的检查。这部分代码后续再介绍。
调用 InstallArgs 的 copyApk。对本例来说,将调用 FileInstallArgs 的 copyApk 函数。
DefaultContainerService 分析 DefaultContainerService.java::getMinimalPackageInfo()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public PackageInfoLite getMinimalPackageInfo (finalUri fileUri, int flags, longthreshold) { PackageInfoLite ret = new PackageInfoLite(); String scheme = fileUri.getScheme(); String archiveFilePath = fileUri.getPath(); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); PackageParser.PackageLite pkg = PackageParser.parsePackageLite(archiveFilePath,0 ); if (pkg == null ) { return ret; } ret.packageName = pkg.packageName; ret.installLocation = pkg.installLocation; ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,archiveFilePath, flags, threshold); return ret; }
APK 可在 AndroidManifest.xml 中声明一个安装位置,不过 DCS 除了解析该位置外,还需要做进一步检查,这个工作由 recommendAppInstallLocation 函数完成,代码如下:
DefaultContainerService.java::recommendAppInstallLocation()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 private int recommendAppInstallLocation (intinstallLocation, StringarchiveFilePath, int flags,long threshold) { int prefer; boolean checkBoth = false ; check_inner: { if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) { prefer = PREFER_INTERNAL; break check_inner; } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0 ) { prefer = PREFER_INTERNAL; break check_inner; } } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { prefer= PREFER_INTERNAL; checkBoth = true ; breakcheck_inner; } intinstallPreference = Settings.System.getInt(getApplicationContext() .getContentResolver(), Settings.Secure.DEFAULT_INSTALL_LOCATION, PackageHelper.APP_INSTALL_AUTO); if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) { prefer = PREFER_INTERNAL; break check_inner; } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) { prefer = PREFER_EXTERNAL; breakcheck_inner; } prefer =PREFER_INTERNAL; } final boolean emulated = Environment.isExternalStorageEmulated(); final FileapkFile = new File(archiveFilePath); boolean fitsOnInternal = false ; if (checkBoth || prefer == PREFER_INTERNAL) { try { fitsOnInternal = isUnderInternalThreshold(apkFile, threshold); } } boolean fitsOnSd = false ; if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { try { fitsOnSd = isUnderExternalThreshold(apkFile); } } if (prefer== PREFER_INTERNAL) { if (fitsOnInternal) { return PackageHelper.RECOMMEND_INSTALL_INTERNAL; } } else if (!emulated && prefer == PREFER_EXTERNAL) { if (fitsOnSd) { returnPackageHelper.RECOMMEND_INSTALL_EXTERNAL; } } if (checkBoth) { if (fitsOnInternal) { return PackageHelper.RECOMMEND_INSTALL_INTERNAL; }else if (!emulated && fitsOnSd) { return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; } } return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } }
DCS 的 getMinimalPackageInfo 函数为了得到一个推荐的安装路径做了不少工作,其中,各种安装策略交叉影响。这里总结一下相关的知识点:
APK 在 AndroidManifest.xml 中设置的安装点默认为 AUTO,在具体对应时倾向内部空间。
用户在 Settings 数据库中设置的安装位置。
检查外部存储或内部存储是否有足够空间。
InstallArgs 的 copyApk 函数分析 至此,我们已经得到了一个合适的安装位置,下一步工作就由 copyApk 来完成。
PackageManagerService.java::InstallArgs.copyApk()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int copyApk (IMediaContainerService imcs, booleantemp) throws RemoteException { if (temp) { createCopyFile(); } FilecodeFile = new File(codeFileName); ParcelFileDescriptor out = null ; try { out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE); } int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); ret = imcs.copyResource(packageURI, out); } finally { } return ret; }
handleReturnCode 分析 在 HandlerParams 的 startCopy 函数中,handleStartCopy 执行完之后,将调用 handleReturnCode 开展后续工作,代码如下:
PackageManagerService.java::InstallParams.HandleParams()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 void handleReturnCode () { if (mArgs != null ) { processPendingInstall(mArgs, mRet); } } private void processPendingInstall (finalInstallArgs args, final intcurrentStatus) { mHandler.post(new Runnable() { public void run () { mHandler.removeCallbacks(this ); PackageInstalledInfo res = new PackageInstalledInfo(); res.returnCode = currentStatus; res.uid = -1 ; res.pkg = null ; res.removedInfo = new PackageRemovedInfo(); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageLI(args, true , res); } args.doPostInstall(res.returnCode); } final boolean update = res.removedInfo.removedPackage != null ; boolean doRestore = (!update && res.pkg != null && res.pkg.applicationInfo.backupAgentName != null ); int token; if (mNextInstallToken < 0 ) mNextInstallToken = 1 ; token = mNextInstallToken++; PostInstallData data = new PostInstallData(args, res); mRunningInstalls.put(token, data); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore){ } if (!doRestore) { Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0 ); mHandler.sendMessage(msg); } } }); }
由上面代码可知,handleReturnCode 主要做了 4 件事情:
调用 InstallArgs 的 doPreInstall 函数,在本例中是 FileInstallArgs 的 doPreInstall 函数。
调用 PKMS 的 installPackageLI 函数进行 APK 安装,该函数内部将调用 InstallArgs的doRename 对临时文件进行改名。另外,还需要扫描此 APK 文件。此过程和之前介绍的“扫描系统 Package”一节的内容类似。至此,该 APK 中的私有财产就全部被登记到 PKMS 内部进行保存了。
调用 InstallArgs 的 doPostInstall 函数,在本例中是 FileInstallArgs 的 doPostInstall 函数。
此时,该 APK 已经安装完成(不论失败还是成功),继续向 mHandler 抛送一个 POST_INSTALL 消息,该消息携带一个 token,通过它可从 mRunningInstalls 数组中取得一个 PostInstallData 对象。
这里介绍一下 FileInstallArgs 的 doRename 函数,它的功能是将临时文件改名,最终的文件的名称一般为“包名-数字.apk”。其中,数字是一个 index,从 1 开始。
POST_INSTALL 处理 PackageManagerService.java::doHandleMessage()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 void doHandleMessage (Message msg) { switch (msg.what) { case INIT_COPY: { } case MCS_BOUND: { } case POST_INSTALL: { PostInstallData data = mRunningInstalls.get(msg.arg1); mRunningInstalls.delete(msg.arg1); boolean deleteOld = false ; if (data != null ) { InstallArgs args = data.args; PackageInstalledInfo res = data.res; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { final String packageName = res.pkg.applicationInfo.packageName; res.removedInfo.sendBroadcast(false , true , false ); Bundle extras = new Bundle(1 ); extras.putInt(Intent.EXTRA_UID, res.uid); final boolean update = res.removedInfo.removedPackage != null ; if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true ); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null , null , updateUsers); if (update) { } } Runtime.getRuntime().gc(); if (deleteOld) { synchronized (mInstallLock) { res.removedInfo.args.doPostDeleteLI(true ); } } if (args.observer != null ) { try { Bundle extras = extrasForInstallResult(res); args.observer.onPackageInstalled(res.name, res.returnCode, res.returnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists." ); } } } else { Slog.e(TAG, "Bogus post-install token " + msg.arg1); } } break ; } }
Apk 安装流程总结
参考资料
扫一扫关注我的公众账号