Android应用管理一 -- APK包的安装、卸载和优化(PackageManagerService)
Android的应用管理主要是通过PackageManagerService来完成的。PackageManagerService服务负责各种APK包的安装、卸载、优化和查询。
PackageManagerService在启动时会扫描所有的APK文件和Jar包,然后把他们的信息读取出来,保存在内存中,这样系统在运行时就能迅速找到各种应用和组件的信息。扫描过程中如果遇到没有优化的文件,还要执行转换工作,将app文件从dex格式转换为oat格式(Android5.0之前是转换为odex格式)。
启动后,PackageManagerService将提供安装包的信息查询服务以及应用的安装和卸载服务。Android5.x,/data/app目录或者/system/app目录下存放的是以应用名称命名的目录,在这个目录下存放的是apk文件和一个lib目录,lib目录存放的是应用的so文件。
Android中的应用可以简单的分为两大类:系统应用和普通应用。
系统应用是指位于/system/app或者/system/priv-app目录下的应用。priv-app目录中存放的是一些系统底层应用,如setting,systemui等。/system/app存放的则是系统级的应用,如Phone、Contacts等。在PackageManagerService中,所有的system应用包括这两个目录下的应用,而所谓的private应用特指priv-app目录下的应用。
普通应用:用户安装的应用,位于目录/data/app下。普通应用还可以安装在SD卡上,系统应用不可以。
通常情况下,系统应用是不能删除的,但是可以升级。升级的方法是安装一个包名相同,但是具有更高版本号的应用在/data/app目录下。对于系统中的这种升级情况,Android会在/data/system/packages.xml文件中用标签
系统目录/data/dalvik-cache下保存的是大部分的apk文件和jar包的odex版本。odex是一种优化过的格式,执行速度比apk文件的dex格式更快。
每个应用都有保存数据的目录,位于/data/data/<包名>/目录下。其中数据目录下常见的两个子目录:shared_prefs目录中保存的是应用的设置文件,database保存的是应用的数据库文件。
一、了解PackageManagerService
在应用中如果使用PackageManagerService服务,通常调用的是Context的getPackageManager()方法,这个方法返回的是PackageManager对象,注意Context是一个抽象类,ContextImpl类继承了Context,方法如下:
@Override public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; }
从getPackageManager()方法的代码中可以看到它返回的PackageManager对象实际上是一个ApplicationPackageManager对象,这个对象创建时使用了IPackageManager对象作为参数。IPackageManager对象是一个PackageManagerService的引用对象。因此,ApplicationPackageManager对象就是PackageManagerService的代理对象。ApplicationPackageManager类继承自PackageManager类。PackageManager类中定义了应用可以操作PackageManagerService的接口。ActivityThread类的getPackageManager()方法代码如下:
public static IPackageManager getPackageManager() { if (sPackageManager != null) { //Slog.v("PackageManager", "returning cur default = " + sPackageManager); return sPackageManager; } IBinder b = ServiceManager.getService("package"); //Slog.v("PackageManager", "default service binder = " + b); sPackageManager = IPackageManager.Stub.asInterface(b); //Slog.v("PackageManager", "default service = " + sPackageManager); return sPackageManager; }
PackageManagerService的两个重要成员变量mInstallerService和mInstller与应用安装有密切关系。mInstallerService是类PackageInstallerService的实例对象,一个应用的安装过程比较长,Android5.0中新增加了PackageInstallerService来管理应用的安装过程。另一个成员变量mInstaller是类Installer的实例对象,它也有一个名为mInstaller的成员变量,其类型是InstallerConnection,而InstallerConnection中存在着和Deamon进程Installd通信的Socket命令通道。实际上系统中进行apk文件格式转换、建立数据目录等工作最后都是由installd进程来完成的。这些对象之间的关系如下图所示。
1、理解packages.xml和Settings类
这里先看一下Settings这个类,这个类用来保存和PackageManagerService相关的一些设置,它所保存的内容在解析应用时会用到。那么Settings中保存的值是从哪里来的?作用又是什么呢?我们先看一下Settings的构造方法,代码如下:
Settings(Context context) { this(context, Environment.getDataDirectory());//获取用户数据目录:/data }
Settings(Context context, File dataDir) { mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs();//在data目录下创建system目录:/data/system FileUtils.setPermissions(mSystemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1);//设置目录的属性为0775 mSettingsFilename = new File(mSystemDir, "packages.xml");//data/system/packages.xml mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml"); mPackageListFilename = new File(mSystemDir, "packages.list"); FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID); // Deprecated: Needed for migration mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); }
如下是Android N 的构造方法:
Settings(File dataDir, Object lock) { mLock = lock; mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock); mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); FileUtils.setPermissions(mSystemDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1); mSettingsFilename = new File(mSystemDir, "packages.xml"); mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml"); mPackageListFilename = new File(mSystemDir, "packages.list"); FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID); final File kernelDir = new File("/config/sdcardfs"); mKernelMappingFilename = kernelDir.exists() ? kernelDir : null; // Deprecated: Needed for migration mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); }
Settings的构造方法会创建data目录下的system目录,这个目录用来保存很多系统文件。主要的工作是创建了5个位于目录/data/system下的File对象,分别是: - packages.xml:记录系统中所有安装的应用信息,包括基本信息、签名和权限。
- packages-backup.xml:packages.xml文件的备份。
- packages.list:保存普通应用的数据目录和uid等信息。
- packages-stopped.xml:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会将应用的信息记录到该文件中。
- packages-stopped-backup.xml:pacakges-stopped.xml文件的备份。
packages.xml是PackageManagerService启动时需要用的文件,先看下文件的内容:
......
上面是文件的一个片段,通过标签标签
便签
标签
PackageSetting类继承了PackageSettingBase类,PackageSettingBase类又继承了GrantedPermissions类,应用的基本信息保存在PackageSettingBase类的成员变量中,申明的权限保存在GrantedPremissions类,签名则保持在SharedUserSetting类的成员变量signatures中。
类SharedUserSetting用来描述具有相同sharedUserId的应用信息,它的成员变量packages保存了所有具有相同sharedUserId的应用信息的引用。这些应用的签名是相同的,所以只需在成员变量signatures中保存一份。通过这个对象,Android运行时很容易检索到和某个应用拥有相同sharedUserId的其他应用。
标签
private final ArrayMap mPackages = new ArrayMap();
private final ArrayMap mDisabledSysPackages = new ArrayMap();
package.xml文件中除了标签
if (tagName.equals("package")) { readPackageLPw(parser); } else if (tagName.equals("permissions")) { readPermissionsLPw(mPermissions, parser); } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { ...... addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode)); } } else if (tagName.equals("renamed-package")) { ...... mRenamedPackages.put(nname, oname);
标签 // Packages that have been uninstalled and still need their external // storage data deleted. final ArrayList mPackagesToBeCleaned = new ArrayList();
标签final ArrayMap mRenamedPackages = new ArrayMap();
2、服务的初始化过程 PackageManagerService也是在SystemServer中开始初始化的,代码如下:
mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);......try { mPackageManagerService.performBootDexOpt(); } catch (Throwable e) { reportWtf("performing boot dexopt", e); }......try { mPackageManagerService.systemReady(); } catch (Throwable e) { reportWtf("making Package Manager Service ready", e); }
SystemServer对PMS的初始化主要是通过上面代码完成的。首先看下PMS对象的创建,main()函数的代码如下: public static final PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); ServiceManager.addService("package", m); return m; }
main函数比较简单,只是创建PMS对象并在ServiceManager中注册。 PMS的构造方法主要完成两件事,第一是把系统中的apk文件和jar包从dex格式转换成ART的oat格式;第二是扫描系统中所有安装的应用,把他们的信息提取出来。
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); if (mSdkVersion <= 0) {//检查SDK版本 Slog.w(TAG, "**** ro.build.version.sdk not set!"); } Object bridgeObject; try { /* * load and create the security bridge:com.android.services.SecurityBridge.core.PackageManagerSB */ bridgeObject = getClass().getClassLoader().loadClass(SECURITY_BRIDGE_NAME).newInstance(); mSecurityBridge = (PackageManagerMonitor)bridgeObject; } catch (Exception e){ Slog.w(TAG, "No security bridge jar found, using default"); mSecurityBridge = new PackageManagerMonitor(); } mContext = context; mFactoryTest = factoryTest;//设置运行模式。工厂模式是一种测试模式 mOnlyCore = onlyCore;//onlyCore为true表示只处理系统的应用,通常为false mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics();//DisplayMetrics对象存储屏幕的显示信息 mSettings = new Settings(context);//新创建一个settings对象 mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); //上面添加SharedUserSetting对象到mSettings中,sharedUserId属性相同的包可以运行在同一个进程中,或者相互读取资源。这里添加了6中系统的uid:system、phone、log、nfc、Bluetooth、shell。 // TODO: add a property to control this? long dexOptLRUThresholdInMinutes; if (mLazyDexOpt) { dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. } else { dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. } mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; 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; } mInstaller = installer;//应用安装器 getDefaultDisplayMetrics(context, mMetrics);//设置DisplayMetrics对象 //读取系统配置来初始化mGlobalGids、mSystemPermissions、mAvailableFeatures SystemConfig systemConfig = SystemConfig.getInstance(); mGlobalGids = systemConfig.getGlobalGids(); mSystemPermissions = systemConfig.getSystemPermissions();//保存在对象内部的变量列表 mAvailableFeatures = systemConfig.getAvailableFeatures(); synchronized (mInstallLock) { // writer synchronized (mPackages) { mHandlerThread = new ServiceThread(TAG,//创建用来处理消息的线程,并加入到Watchdog的监控中 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); //为/data目录下的子目录生成文件对象 File dataDir = Environment.getDataDirectory(); mAppDataDir = new File(dataDir, "data");///data/data存放应用数据的目录 mAppInstallDir = new File(dataDir, "app");///data/app存放安装的应用 mAppLib32InstallDir = new File(dataDir, "app-lib");//存放应用自带的native库 mAsecInternalPath = new File(dataDir, "app-asec").getPath(); mUserAppDataDir = new File(dataDir, "user");//存放用户的数据文件 mDrmAppPrivateInstallDir = new File(dataDir, "app-private");//存放drm保护的应用 sUserManager = new UserManagerService(context, this, mInstallLock, mPackages);//创建用户管理服务 // Propagate permission configuration in to package manager.通过systemConfig得到系统中定义的permission。这些permission保存在/etc/permissions目录下的文件中。 ArrayMap permConfig = systemConfig.getPermissions(); for (int i=0; i libConfig = systemConfig.getSharedLibraries(); for (int i=0; i alreadyDexOpted = new ArraySet(); /** * Add everything in the in the boot class path to the * list of process files because dexopt will have been run * if necessary during zygote startup. */ final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); //把环境变量BOOTCALSSPATH中定义的包加入到已优化的集合alreadyDexOpted if (bootClassPath != null) { String[] bootClassPathElements = splitString(bootClassPath, ':'); for (String element : bootClassPathElements) { alreadyDexOpted.add(element); } } else { Slog.w(TAG, "No BOOTCLASSPATH found!"); }
if (systemServerClassPath != null) {//把环境变量SYSTEMSERVERCLASSPATH中定义的包加入到以优化集合alreadyDexOpted中 String[] systemServerClassPathElements = splitString(systemServerClassPath, ':'); for (String element : systemServerClassPathElements) { alreadyDexOpted.add(element); } } else { Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); } final List allInstructionSets = getAllInstructionSets();//获取没有优化过的库或工具 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()])); /** * Ensure all external libraries have had dexopt run on them. */ if (mSharedLibraries.size() > 0) { // NOTE: For now, we're compiling these system "shared libraries" // (and framework jars) into all available architectures. It's possible // to compile them only when we come across an app that uses them (there's // already logic for that in scanPackageLI) but that adds some complexity. for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (SharedLibraryEntry libEntry : mSharedLibraries.values()) { final String lib = libEntry.path; if (lib == null) { continue; } try { byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null, dexCodeInstructionSet, false); if (dexoptRequired != DexFile.UP_TO_DATE) { alreadyDexOpted.add(lib); // The list of "shared libraries" we have at this point is if (dexoptRequired == DexFile.DEXOPT_NEEDED) { mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); } else { mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet); } } } 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()); } } } } File frameworkDir = new File(Environment.getRootDirectory(), "framework"); // Gross hack for now: we know this file doesn't contain any // code, so don't dexopt it to avoid the resulting log spew. alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");//将framework-res.apk加入到以优化列表 // Gross hack for now: we know this file is only part of // the boot class path for art, so don't dexopt it to // avoid the resulting log spew. alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");//将core-libart.jar加入以优化列表 /** * And there are a number of commands implemented in Java, which * we currently need to do the dexopt on so that they can be * run from a non-root shell. */ String[] frameworkFiles = frameworkDir.list();//对framework目录下的文件执行opt到odex的转换 if (frameworkFiles != null) { // TODO: We could compile these only for the most preferred ABI. We should // first double check that the dex files for these commands are not referenced // by other system apps. for (String dexCodeInstructionSet : dexCodeInstructionSets) { for (int i=0; i
// Collect vendor overlay packages. // (Do this before scanning any apps.) // For security and version matching reason, only consider // overlay packages if they reside in VENDOR_OVERLAY_DIR. File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0); // Find base frameworks (resource packages without code). scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0); // Collected privileged system packages./system/priv-app final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0); // Collect ordinary system packages./system/app final File systemAppDir = new File(Environment.getRootDirectory(), "app"); scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all vendor packages. File vendorAppDir = new File("/vendor/app"); try { vendorAppDir = vendorAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); // Collect all OEM packages. final File oemAppDir = new File(Environment.getOemDirectory(), "app"); scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); mInstaller.moveFiles(); // Prune any system packages that no longer exist. final List possiblyDeletedUpdatedSystemApps = new ArrayList(); final ArrayMap expectingBetter = new ArrayMap<>(); if (!mOnlyCore) { Iterator psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { PackageSetting ps = psit.next(); /* * If this is not a system app, it can't be a * disable system app. */ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { continue; } /* * If the package is scanned, it's not erased. */ final PackageParser.Package scannedPkg = mPackages.get(ps.name); if (scannedPkg != null) { /* * If the system app is both scanned and in the * disabled packages list, then it must have been * added via OTA. Remove it from the currently * scanned package so the previously user-installed * application can be scanned. */ if (mSettings.isDisabledSystemPackageLPr(ps.name)) { logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known codePath=" + ps.codePathString + ", installStatus=" + ps.installStatus + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.mVersionCode); removePackageLI(ps, true); expectingBetter.put(ps.name, ps.codePath); } continue; } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { psit.remove(); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; wiping its data"); removeDataDirsLI(ps.name); } else { final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); if (disabledPs.codePath == null || !disabledPs.codePath.exists()) { possiblyDeletedUpdatedSystemApps.add(ps.name); } } } } //look for any incomplete package installations ArrayList 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(); // Remove any shared userIDs that have no associated packages mSettings.pruneSharedUsersLPw(); if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanFlags | SCAN_REQUIRE_KNOWN, 0); /** * Remove disable package settings for any updated system * apps that were removed via an OTA. If they're not a * previously-updated app, remove them completely. * Otherwise, just revoke their system-level permissions. */ for (String deletedAppName : possiblyDeletedUpdatedSystemApps) { PackageParser.Package deletedPkg = mPackages.get(deletedAppName); mSettings.removeDisabledSystemPackageLPw(deletedAppName); String msg; if (deletedPkg == null) { msg = "Updated system package " + deletedAppName + " no longer exists; wiping its data"; removeDataDirsLI(deletedAppName); } else { msg = "Updated system app + " + deletedAppName + " no longer present; removing system privileges for " + deletedAppName; deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName); deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; } logCriticalInfo(Log.WARN, msg); } /** * Make sure all system apps that we expected to appear on * the userdata partition actually showed up. If they never * appeared, crawl back and revive the system version. */ for (int i = 0; i < expectingBetter.size(); i++) { final String packageName = expectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { final File scanFile = expectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); final int reparseFlags; if (FileUtils.contains(privilegedAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED; } else if (FileUtils.contains(systemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(vendorAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else if (FileUtils.contains(oemAppDir, scanFile)) { reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } mSettings.enableSystemPackageLPw(packageName); try { scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " + e.getMessage()); } } } } // Now that we know all of the shared libraries, update all clients to have // the correct library paths.更新所有应用的动态库路径 updateAllSharedLibrariesLPw(); for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { // NOTE: We ignore potential failures here during a system scan (like // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */, false /* force dexopt */, false /* defer dexopt */); } // Now that we know all the packages we are keeping, // read and update their last usage times. mPackageUsage.readLP(); 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.如果平台的SDK版本和上次启动时相比发生了变化,可能permission的定义也改变了,因此需要重新赋予应用权限 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; updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | (regrantPermissions ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) : 0)); // If this is the first boot, and it is a normal boot, then // we need to initialize the default preferred apps. if (!mRestoredSettings && !onlyCore) { mSettings.readDefaultPreferredAppsLPw(this, 0); } // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories.如果这是执行OTA后的第一次启动,需要清除cache mIsUpgrade = !Build.FINGERPRINT.equals(mSettings.mFingerprint); if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (String pkgName : mSettings.mPackages.keySet()) { deleteCodeCacheDirsLI(pkgName); } mSettings.mFingerprint = Build.FINGERPRINT; } // All the changes are done during package scanning.所有操作执行完成,更新数据库版本 mSettings.updateInternalDatabaseVersion(); // can downgrade to reader mSettings.writeLPr();把mSettings中的内容保存到packages.xml EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); mRequiredVerifierPackage = getRequiredVerifierLPr(); } // synchronized (mPackages) } // synchronized (mInstallLock) //创建PackageInstallerService./data/app mInstallerService = new PackageInstallerService(context, this, mAppInstallDir); // 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(); }
PackageManagerService构造方法的执行过程就是先读取保存在packages.xml文件中的上次扫描的结果,保存在mSettings的成员变量中,然后扫描设备中几个应用目录下的应用文件,并把扫描的结果保存在PackageManagerService的成员变量mPackages中,通过对比上次扫描的结果来检查本次扫描到的应用中是否有被升级包覆盖的系统应用,如果有,则从mPackages中去除掉。这样mPackages的记录和mSettings的记录就一致了,最后,将本次扫描的结果写回packages.xml文件中。
PackageManagerService的初始化工作都是在它的构造方法中完成的,主要完成以下任务:
(1)、添加一些用户id,如shell、phone等;
(2)、建立并启动PackageHandler消息循环,用于处理apk安装请求,如adb install,packageInstaller安装过程中就会发送消息。
(3)、解析/system/etc/permisssions下的xml文件,主要是platform.xml,建立permission和gid之间的关系,可以指定一个权限与几个组对应,当一个apk被授予这个权限时它也同时属于这几个组;readPermission(parser, perm)方法给一些底层用户分配一些权限,如shell授予各种permission,把一个权限授予一个uid,当apk使用这个uid运行时,就具备了这个权限系统增加的一些应用需要link的扩展的jar库,系统每增加一个硬件,都要添加相应的feature,将解析结果放入mAcailableFeature。
(4)、检查/data/system/packages.xml是否存在,里面记录了系统的permission,以及每个apk的name、codePath、flags、version、userid等,这些信息主要是通过apk安装的时候解析AndroidManifext.xml获取到的,解析完apk后将更新信息写入到这个文件并保存到flash,下次开机直接从里面读取相关信息添加到内存相关列表中,当有apk安装、升级、删除时会更新这个文件。
(5)、检查环境变量BOOTCLASSPATH、SYSTEMSERVERCLASSPATH,mSharedLibraries以及/system/framework下的jar是否需要dexopt,需要则通过dexopt进行优化,这里主要是调用mInstaller.dexopt进行优化。
(6)、调用scanDirLi启动apk解析,解析目录包括:/system/framework、/system/priv-app、/system/app、/vendor/app、/data/app、/data/app-private等。
3、添加用户
添加用户调用的是Settings类的addSharedUserLPw方法。
/** * 检查mSharedUsers中是否有这个用户,没有则创建一个SharedUserSetting对象, * 保存到mUserIds或mOtherUserIds中,并添加到mSharedUsers中 * @param name * @param uid * @param pkgFlags * @return */ SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { 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; } s = new SharedUserSetting(name, pkgFlags);//创建SharedUserSetting对象 s.userId = uid;//将uid赋给SharedUserSetting对象的userId属性 if (addUserIdLPw(uid, s, name)) {//保存到mUserIds或mOtherUserIds中 mSharedUsers.put(name, s);//添加到mSharedUsers中 return s; } return null; }
首先检查mSharedusers数组中是否含有对应name的SharedUserSetting对象,如果没有则创建SharedUserSetting对象,调用addUserIdLPw方法添加到mUserIds或mOtherUserIds数组中,并添加到mSharedUsers中,返回查询的对象。 /** * 将uid及对应的SharedUserSetting对象添加到mUserIds或mOtherUserIds * @param uid * @param obj SharedUserSetting对象 * @param name * @return */ private boolean addUserIdLPw(int uid, Object obj, Object name) { if (uid > Process.LAST_APPLICATION_UID) {//大于应用程序最大的id,19999 return false; } if (uid >= Process.FIRST_APPLICATION_UID) {// 10000 int N = mUserIds.size(); final int index = uid - Process.FIRST_APPLICATION_UID; while (index >= N) {//先添加元素,后设置值 mUserIds.add(null); N++; } if (mUserIds.get(index) != null) { PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate user id: " + uid + " name=" + name); return false; } mUserIds.set(index, obj);//把该uid和对应的SharedUserSetting保存 } else {//系统用户 if (mOtherUserIds.get(uid) != null) { PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared id: " + uid + " name=" + name); return false; } mOtherUserIds.put(uid, obj);//保存到mOtherUserIds中 } return true; }
在构造方法中添加了6个系统用户:android.uid.system、android.uid.phone、android.uid.log、android.uid.nfc、android.uid.bluetooth、android.uid.shell。 4、建立并启动PackageHandler消息循环机制
PackageHandler用来处理安装apk等过程中的各种消息。这里比较简单,这要是建立一个PackageHandler,来处理消息。
5、处理permission文件
在PackageManagerService的构造方法中,调用了SystemConfig的getSystemPermissions()方法来获取系统的Permission列表。getSystemPermisssions()方法返回的是保存在对象内部的变量列表,如下:
public SparseArray> getSystemPermissions() { return mSystemPermissions; }
这个列表的建立是在SystemConfig的构造方法中完成的,如下: SystemConfig() { // Read configuration from system /system/etc/sysconfig readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "sysconfig"), false); // Read configuration from the old permissions dir /system/etc/permisssions readPermissions(Environment.buildPath( Environment.getRootDirectory(), "etc", "permissions"), false); // Only read features from OEM config /oem/etc/sysconfig readPermissions(Environment.buildPath( Environment.getOemDirectory(), "etc", "sysconfig"), true); readPermissions(Environment.buildPath(// /oem/etc/permissions Environment.getOemDirectory(), "etc", "permissions"), true); }
Permission的读取是通过readPermissions()方法完成的,代码如下: void readPermissions(File libraryDir, boolean onlyFeatures) { // Read permissions from given directory. if (!libraryDir.exists() || !libraryDir.isDirectory()) { if (!onlyFeatures) { Slog.w(TAG, "No directory " + libraryDir + ", skipping"); } return;//如果目录不存在,或libraryDir不是一个目录,退出 } if (!libraryDir.canRead()) { Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return;//如果目录是不可读的,退出 } // Iterate over the files in the directory and scan .xml files File platformFile = null; for (File f : libraryDir.listFiles()) {//遍历目录中的文件,并扫描xml文件 // We'll read platform.xml last if (f.getPath().endsWith("etc/permissions/platform.xml")) { platformFile = f; continue;//先不处理platform.xml } if (!f.getPath().endsWith(".xml")) { Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue;//仅处理xml文件,其余文件忽略 } if (!f.canRead()) { Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue;//如果文件不可读,忽略 } readPermissionsFromXml(f, onlyFeatures);//解析xml文件 } // Read platform permissions last so it will take precedence if (platformFile != null) {//单独处理/system/etc/permissions/platform.xml文件 readPermissionsFromXml(platformFile, onlyFeatures); } }
readPermissions()方法的工作就是读取指定目录下的xml文件,然后调用readPermissionsFromXml()方法来处理文件。目录下存放的是和permission相关的文件,其中platform.xml文件的内容和其他文件的内容不同。例如/system/etc/permission/目录下的android.hardware.bluetooth.xml文件内容如下:
这个文件只是定义了一个feature的名称。除了platform文件,其他的xml文件也都是类似的内容。再看下platform.xml的内容,如下: <?xml version="1.0" encoding="utf-8"?> ...... ...... ......
platform.xml文件主要有3块内容组成。 - 标签
表示把属性name中的字符串表示的权限赋予 标签中的属性gid中的用户组。 - 标签表示把属性name中的字符串表示的权限赋予属性uid的用户。
- 标签
表示除了framework中的动态库以外,系统将为应用自动加载的动态库。
final SparseArray> mSystemPermissions;
(3)标签6、解析系统已经安装的包
Android启动时要扫描和安装现有app,或许是为了防止corrupted(损坏)信息的存在,或许是为了能在升级系统或更新系统属性后保持app也有效,也或是因为有些信息(如uid)必须要动态生成和调整的。总之,为了要还原现有app的安装信息,这些信息被放在/data/system/package.xml文件中,由Settings管理。另外,/data/system/packages.list记录了app的uid和数据路径等信息。packages.xml这个文件很庞大,记载了每个应用的详细情况,包括permission、shared-user、key等等,每一个应用都对应一个package节点,所以一开机就可以把系统内安装的所有应用都读到Settings的mPackages对象中,它是一个map,维护了系统内所有应用的详情。当然这个文件里维护的是全系统的所有应用,不区分用户。readLPw()方法就是用于恢复这些信息,实现方法位于/frameworks\base\services\core\java\com\android\server\pm\Settings.java。
首先检测/data/system/packages-backup.xml文件是否存在,该文件是packages.xml文件的备份文件,如果存在则解析该文件,因为packages.xml文件可能已经被破坏;不存在则解析packages.xml。 readLPw()先打开packages.xml,再通过XmlPullParser类解析其内容。它会根据不同的tag调用相应的函数来读取信息:
boolean readLPw(PackageManagerService service, List users, int sdkVersion, boolean onlyCore) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) {// /data/system/packages-backup.xml try { str = new FileInputStream(mBackupSettingsFilename); mReadMessages.append("Reading from backup settings file\n"); PackageManagerService.reportSettingsProblem(Log.INFO, "Need to read from backup settings file"); if (mSettingsFilename.exists()) {// /data/system/packages.xml // If both the backup and settings file exist, we // ignore the settings since it might have been // corrupted.// 如果packages.xml和packages-backup.xml文件都存在,则删除packages.xml文件,因为这个文件可能已被破坏 Slog.w(PackageManagerService.TAG, "Cleaning up settings file " + mSettingsFilename); mSettingsFilename.delete(); } } catch (java.io.IOException e) { // We'll try for the normal settings file. } } mPendingPackages.clear(); mPastSignatures.clear(); try { if (str == null) {///data/system/packages-backup.xml文件不存在 if (!mSettingsFilename.exists()) {// 如果/data/system/packages.xml文件也不存在,直接返回false mReadMessages.append("No settings file found\n"); PackageManagerService.reportSettingsProblem(Log.INFO, "No settings file; creating initial state"); mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion; mFingerprint = Build.FINGERPRINT; return false; } str = new FileInputStream(mSettingsFilename);//packages-backup.xml文件不存在时,创建packages.xml文件输入流 } XmlPullParser parser = Xml.newPullParser(); parser.setInput(str, null);//xml文件解析器 int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { ; } if (type != XmlPullParser.START_TAG) { mReadMessages.append("No start tag found in settings file\n"); PackageManagerService.reportSettingsProblem(Log.WARN, "No start tag found in package manager settings"); Slog.wtf(PackageManagerService.TAG, "No start tag found in package manager settings"); return false; } 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; }// 它会根据不同的tag调用相应的函数来读取信息: String tagName = parser.getName(); if (tagName.equals("package")) {// 解析应用,创建一个PackageSetting对象,添加到mPendingPackages中 readPackageLPw(parser); } else if (tagName.equals("permissions")) {// 解析系统定义了哪些权限,由那个包定义的,构建BasePermission结构都添加到mPermissions readPermissionsLPw(mPermissions, parser); } else if (tagName.equals("permission-trees")) { readPermissionsLPw(mPermissionTrees, parser); } else if (tagName.equals("shared-user")) { //解析系统中可被用户共享的一些uid所拥有的权限// 代表一个共享UID,通常,共同实现一系列相似功能的APK共享一个UID。 readSharedUserLPw(parser); } else if (tagName.equals("preferred-packages")) { // no longer used. } else if (tagName.equals("preferred-activities")) { // Upgrading from old single-user implementation; // these are the preferred activities for user 0. readPreferredActivitiesLPw(parser, 0);//解析优先设置的activity } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated readPersistentPreferredActivitiesLPw(parser, 0);//"persistent-preferred-activities" } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated readCrossProfileIntentFiltersLPw(parser, 0);//"crossProfile-intent-filters" } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser);//解析升级的系统应用,保存到mDisabledSysPackages中 } else if (tagName.equals("cleaning-package")) { String name = parser.getAttributeValue(null, ATTR_NAME); String userStr = parser.getAttributeValue(null, ATTR_USER); String codeStr = parser.getAttributeValue(null, ATTR_CODE); if (name != null) { int userId = 0; boolean andCode = true; try { if (userStr != null) { userId = Integer.parseInt(userStr); } } catch (NumberFormatException e) { } if (codeStr != null) { andCode = Boolean.parseBoolean(codeStr); } addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode)); } } else if (tagName.equals("renamed-package")) { String nname = parser.getAttributeValue(null, "new"); String oname = parser.getAttributeValue(null, "old"); if (nname != null && oname != null) { mRenamedPackages.put(nname, oname); } } else if (tagName.equals("last-platform-version")) {//解析 mInternalSdkPlatform = mExternalSdkPlatform = 0; try { String internal = parser.getAttributeValue(null, "internal"); if (internal != null) { mInternalSdkPlatform = Integer.parseInt(internal); } String external = parser.getAttributeValue(null, "external"); if (external != null) { mExternalSdkPlatform = Integer.parseInt(external); } } catch (NumberFormatException e) { } mFingerprint = parser.getAttributeValue(null, "fingerprint"); } else if (tagName.equals("database-version")) {//解析 mInternalDatabaseVersion = mExternalDatabaseVersion = 0; try { String internalDbVersionString = parser.getAttributeValue(null, "internal"); if (internalDbVersionString != null) { mInternalDatabaseVersion = Integer.parseInt(internalDbVersionString); } String externalDbVersionString = parser.getAttributeValue(null, "external"); if (externalDbVersionString != null) { mExternalDatabaseVersion = Integer.parseInt(externalDbVersionString); } } catch (NumberFormatException ignored) { } } else if (tagName.equals("verifier")) { final String deviceIdentity = parser.getAttributeValue(null, "device"); try { mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity); } catch (IllegalArgumentException e) { Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: " + e.getMessage()); } } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT); mReadExternalStorageEnforced = "1".equals(enforcement); } else if (tagName.equals("keyset-settings")) { mKeySetManagerService.readKeySetsLPw(parser); } else { Slog.w(PackageManagerService.TAG, "Unknown element under : " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } str.close(); } catch (XmlPullParserException e) { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); } catch (java.io.IOException e) { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); } final int N = mPendingPackages.size(); for (int i = 0; i < N; i++) { final PendingPackage pp = mPendingPackages.get(i); Object idObj = getUserIdLPr(pp.sharedId); if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString, pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, null, true /* add */, false /* allowInstall */); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, "Unable to create application package for " + pp.name); continue; } p.copyFrom(pp); } else if (idObj != null) { String msg = "Bad package setting: package " + pp.name + " has shared uid " + pp.sharedId + " that is not a shared uid\n"; mReadMessages.append(msg); PackageManagerService.reportSettingsProblem(Log.ERROR, msg); } else { String msg = "Bad package setting: package " + pp.name + " has shared uid " + pp.sharedId + " that is not defined\n"; mReadMessages.append(msg); PackageManagerService.reportSettingsProblem(Log.ERROR, msg); } } mPendingPackages.clear(); if (mBackupStoppedPackagesFilename.exists() || mStoppedPackagesFilename.exists()) { // Read old file readStoppedLPw(); mBackupStoppedPackagesFilename.delete(); mStoppedPackagesFilename.delete(); // Migrate to new file format,迁移到新的文件格式 writePackageRestrictionsLPr(0); } else { if (users == null) { readPackageRestrictionsLPr(0); } else { for (UserInfo user : users) { readPackageRestrictionsLPr(user.id); } } } /* * Make sure all the updated system packages have their shared users * associated with them.确保所有更新的系统应用包 有与它们相关联的共享用户。 */ final Iterator disabledIt = mDisabledSysPackages.values().iterator(); while (disabledIt.hasNext()) { final PackageSetting disabledPs = disabledIt.next(); final Object id = getUserIdLPr(disabledPs.appId);//uid if (id != null && id instanceof SharedUserSetting) { disabledPs.sharedUser = (SharedUserSetting) id; } } mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " + mSharedUsers.size() + " shared uids\n"); return true; }
7、扫描应用目录的过程
PMS的构造方法中调用了scanDirLI()方法来扫描某个目录中apk文件,如下:
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) { final File[] files = dir.listFiles();//遍历dir目录下的文件 if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + dir); return;// 如果扫描的目录是空的,返回 } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } Log.d(TAG, "start scanDirLI:"+dir); int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask",0); boolean bMultitask = (iMultitaskNum > 1); Log.d(TAG, "bMultitask:" + bMultitask + " max thread:" + iMultitaskNum); final MultiTaskDealer dealer = bMultitask ? MultiTaskDealer.startDealer(MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null; for (File file : files) {// 判断文件是否是应用文件 final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } final File ref_file = file; final int ref_parseFlags = parseFlags; final int ref_scanFlags = scanFlags; final long ref_currentTime = currentTime; Runnable scanTask = new Runnable() { public void run() { try {// 调用scanPackageLI方法继续扫描文件 scanPackageLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK, ref_scanFlags, ref_currentTime, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage()); // Delete invalid userdata apps。删除安装失败的文件和数据目录 if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 && e.error == PackageManager.INSTALL_FAILED_INVALID_APK) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file); if (ref_file.isDirectory()) { FileUtils.deleteContents(ref_file); } ref_file.delete(); } } }}; if(dealer!=null) dealer.addTask(scanTask); else scanTask.run(); } if(dealer!=null) dealer.waitAll(); Log.d(TAG, "end scanDirLI:"+dir); }
scanDirLI()只扫描指定目录下的文件,不会去扫描子目录,同时只扫描后缀为“apk”的文件。调用scanPackageLI()方法来继续扫描过程。在scanDirLI方法中调用的参数是File对象,因此scanPackageLI调用第一个参数为File对象的方法,作用是把File对象解析成PackageParser.Package对象,这个结果会传递给scanPackageLI方法的第二个实现,用来创建PackageManagerService中和应用相关的各种内部数据结构。如下: (1)解析文件:
/* * Scan a package and return the newly parsed package. * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser();//创建文件解析器对象 pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) { parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; } final PackageParser.Package pkg; try {//调用PackageParser对象的parsePackage方法,解析APK文件,生成PackageParser.Package对象 pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); }
scanPackageLI方法创建一个文件解析器PackageParser对象,然后调用它的parsePackage()方法来解析参数中传递的文件对象scanFile,得到一个PackageParser.Package对象。 (2)处理更改了包名的应用。
这里是在检查应用是否属于
PackageSetting ps = null; PackageSetting updatedPkg; // reader synchronized (mPackages) { // Look to see if we already know about this package.获取旧的包名 String oldName = mSettings.mRenamedPackages.get(pkg.packageName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) { // This package has been renamed to its original name. Let's // use that.这个包已经被重新命名为原来的名称,根据原始包名创建一个PackageSetting对象 ps = mSettings.peekPackageLPr(oldName); } // If there was no original package, see one for the real package name. if (ps == null) {//如果没有原始包,根据新包名创建一个PackageSetting对象 ps = mSettings.peekPackageLPr(pkg.packageName); }
(3)处理安装了升级包的系统应用。 // Check to see if this package could be hiding/updating a system // package. Must look for it either under the original or real // package name depending on our state. 检查应用是否存在升级包 updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName); if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg); } boolean updatedPkgBetter = false; // First check if this is a system package that may involve an update如果扫描的是系统应用并且有升级包 if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { // If new package is not located in "/system/priv-app" (e.g. due to an OTA), // it needs to drop FLAG_PRIVILEGED. if (locationIsPrivileged(scanFile)) {//位于/system/priv-app目录下 updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED; } else { updatedPkg.pkgFlags &= ~ApplicationInfo.FLAG_PRIVILEGED; } if (ps != null && !ps.codePath.equals(scanFile)) { // The path has changed from what was last scanned... check the // version of the new path against what we have stored to determine // what to do.如果包名和上次扫描的结果不相同了,根据版本号来决定如何处理 if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath); if (pkg.mVersionCode <= ps.versionCode) {//如果扫描文件的版本号低于升级包的版本,忽略这个文件 // The system package has been updated and the code path does not match // Ignore entry. Skip it. Slog.i(TAG, "Package " + ps.name + " at " + scanFile + " ignored: updated version " + ps.versionCode + " better than this " + pkg.mVersionCode); if (!updatedPkg.codePath.equals(scanFile)) { Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : " + ps.name + " changing from " + updatedPkg.codePathString + " to " + scanFile); updatedPkg.codePath = scanFile; updatedPkg.codePathString = scanFile.toString(); } updatedPkg.pkg = pkg; throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null); } else { // The current app on the system partition is better than // what we have updated to on the data partition; switch // back to the system partition version. // At this point, its safely assumed that package installation for // apps in system partition will go through. If not there won't be a working // version of the app // writer如果扫描文件的版本号高于升级包的版本,则把升级包删除掉 synchronized (mPackages) { // Just remove the loaded entries from package lists. mPackages.remove(ps.name); } logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile + " reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } synchronized (mPackages) { mSettings.enableSystemPackageLPw(ps.name); } updatedPkgBetter = true; } } } if (updatedPkg != null) {//如果升级包存在,把属性设为系统应用 // An updated system app will not have the PARSE_IS_SYSTEM flag set // initially parseFlags |= PackageParser.PARSE_IS_SYSTEM; // An updated privileged app will not have the PARSE_IS_PRIVILEGED // flag set initially if ((updatedPkg.pkgFlags & ApplicationInfo.FLAG_PRIVILEGED) != 0) { parseFlags |= PackageParser.PARSE_IS_PRIVILEGED; } }
这段代码的作用就是处理带有升级包的应用。 (4)扫描文件的签名。
// Verify certificates against what was last scanned collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
(5)处理应用的包名有冲突的情况。 /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; if (updatedPkg == null && ps != null//如果扫描的文件是系统应用,但是也存在一个同名的普通应用 && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) { /* * Check to make sure the signatures match first. If they don't, * wipe the installed application and its data. */ if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {//比较它们的签名,如果不相同就删除扫描到的系统应用 logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but" + " signatures don't match existing userdata copy; removing"); deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false); ps = null; } else { /* * If the newly-added system app is an older version than the * already installed version, hide it. It will be scanned later * and re-added like an update.如果扫描的系统应用和安装的应用签名相同,则比较他们的版本; */ if (pkg.mVersionCode <= ps.versionCode) {//如果安装的应用版本高,则构成了升级关系,把系统应用隐藏。 shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile + " but new version " + pkg.mVersionCode + " better than installed " + ps.versionCode + "; hiding system"); } else {//删除安装的应用 /* * The newly found system app is a newer version that the * one previously installed. Simply remove the * already-installed application and replace it with our own * while keeping the application data. */ logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile + " reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } } }
(6)处理应用的代码路径和资源路径: // The apk is forward locked (not public) if its code and resources // are kept in different files. (except for app in either system or // vendor path). // 如果一个应用的代码路径和资源路径不相同,则加上PARSE_FORWARD_LOCK标记,这类应用是forward locked类应用 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { if (ps != null && !ps.codePath.equals(ps.resourcePath)) { parseFlags |= PackageParser.PARSE_FORWARD_LOCK; } } // TODO: extend to support forward-locked splits String resourcePath = null; String baseResourcePath = null; if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) { if (ps != null && ps.resourcePathString != null) {//如果是forward locked类应用,且他们的资源路径不为空,单独设置他们的资源路径 resourcePath = ps.resourcePathString; baseResourcePath = ps.resourcePathString; } else { // Should not happen at all. Just log an error. Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); } } else {//如果是普通应用,设置他们的资源路径和代码路径一致 resourcePath = pkg.codePath; baseResourcePath = pkg.baseCodePath; } // Set application objects path explicitly. pkg.applicationInfo.setCodePath(pkg.codePath); pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); pkg.applicationInfo.setResourcePath(resourcePath); pkg.applicationInfo.setBaseResourcePath(baseResourcePath); pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
(7)调用参数为PackageParser.Package对象的scanPackageLI方法。 // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user);
(8)结束: /* * If the system app should be overridden by a previously installed * data, hide the system app now and let the /data/app scan pick it up * again. */ if (shouldHideSystemApp) { synchronized (mPackages) { /* * We have to grant systems permissions before we hide, because * grantPermissions will assume the package update is trying to * expand its permissions.如果扫描的应用带有升级包,把他们的关系保存到mSettings中 */ grantPermissionsLPw(pkg, true, pkg.packageName); mSettings.disableSystemPackageLPw(pkg.packageName); } } return scannedPkg; }
scanPackageLI方法在解析出了apk文件的信息后,主要是通过对比上次启动的结果来检查系统中是否存在和扫描的文件相冲突的应用包存在,如果有scanPackageLI方法会设法纠正错误,保证系统启动后的一致性。完成错误检查后,将进入第二阶段,调用PackageParser.Package对象的scanPackageLI方法来构建PackageManagerService中的数据结构。
接下来分析下应用安装流程: Android应用管理二 --APK包的安装、卸载和优化(PackageManagerService)
更多相关文章
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- Nginx系列教程(六)| 手把手教你搭建 LNMP 架构并部署天空网络电影
- android中的设计模式--观察者模式
- android gen 目录消失
- Android中实现应用程序的国际化与本地化
- Android(安卓)Studio中使用过程中遇到的问题(二)--SVN的安装与使用