PackageManagerService 源码扫描安装包分析
平台:android 8.0 源码
本文分析的问题:
apk的安装有如下四种方式:
- apk随着PMS的启动而安装(本文)
- adb install安装
- ODM内置商店静默安装
- 拷贝apk到手机,界面安装
这四种方式在代码里的实现其实就是如下两种:
- PMS调用scanDirLI扫描安装(本文分析的方式)
- 直接或间接调用installPackageAsUser安装
在systemserver的分析过程中,在startBootstrapServices()中启动PMS是通过调用PackageManagerService.main()启动了PMS服务的,本文接着从这开始分析.frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// Self-check for initial settings.PackageManagerServiceCompilerMapping.checkProperties();PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();ServiceManager.addService("package", m);return m;}
上面的main()方法比较简单,重点就是创建了PackageManagerService对象,并且把它加入到ServiceManager中,本文的重点就是分析PackageManagerService对象的创建. 在这其中会涉及到在同一个包下面的其他.java类,比较核心的有:Settings.java, 接着分析PackageManagerService.java的构造方法,该方法相当的长,分段进行分析
public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());/// M: Add for Mtprof tool.mMTPROFDisable = false;addBootEvent("Android:PackageManagerService_Start");//mSdkVersion标示SDK版本,让apk知道自己运行在哪个版本if (mSdkVersion <= 0) {Slog.w(TAG, "**** ro.build.version.sdk not set!");}mContext = context;//标示了CTA表示在产品要在国内买,需要过CTA,类似CTS/// M: CTA requirement - permission controlmPermissionReviewRequired = CtaUtils.isCtaSupported() ? true :context.getResources().getBoolean(R.bool.config_permissionReviewRequired);///@}//是否运行在工厂模式下mFactoryTest = factoryTest;//判断是否只扫描系统目录,这个在后面多次用到mOnlyCore = onlyCore;//获取手机分辨率信息mMetrics = new DisplayMetrics();//mPackages是一个map集合存放packagesetting对象,是全局变量.PMS中的每一个应用程序的安装信息都是使用一个packagesetting对象进行描述//Settings类是用来管理应用程序的安装信息的mSettings = new Settings(mPackages);
由于这个Settings类比较重要,接下来分析Settings的构造方法,Settings的构造函数主要用于创建一些目录和文件,并配置相应的权限.
frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(Object lock) {//Environment.getDataDirectory()获取的是/data目录this(Environment.getDataDirectory(), lock);}Settings(File dataDir, Object lock) {mLock = lock;mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);//目录指向为/data/systemmSystemDir = new File(dataDir, "system");//创建前面指向好的目录mSystemDir.mkdirs();FileUtils.setPermissions(mSystemDir.toString(),FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH|FileUtils.S_IXOTH,-1, -1);//packages.xml和packages-backup.xml为一组,用于描述系统所安装的Package信息,其中packages-backup.xml是packages.xml的备份//PKMS写把数据写到backup文件中,信息全部写成功后在改名为非backup文件,以防止在写文件的过程中出错,导致信息丢失mSettingsFilename = new File(mSystemDir, "packages.xml");mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");//packages.list保存中所有安装apk的信息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;//packages-stopped.xml用于描述系统中强行停止运行的package信息,backup也是备份文件// Deprecated: Needed for migrationmStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");}
回到frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,继续分析PackageManagerService的构造方法:
//设置系统的shareuserid,这个是在manifest.xml中设置,拥有相同shareuserid的app将会共享权限.//shareuserid属性设置为"android.uid.system"那么说明是系统appmSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.log", LOG_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
用到了Settings类的方法:addSharedUserLPw,作用是添加共享用户:
//name和uid一一对应,例如:"android.uid.system":Process.SYSTEM_UID(1000)// "android.uid.phone" :RADIO_UID(Process.PHONE_UID, 1001)// 在PMS中,每一个共享Linux用户都是使用一个SharedUserSetting对象来描述的。这些对象保存在mSharedUsers中SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {//依据key(这里其实就是包名)值,从mSharedUsers对象中获取对应的SharedUserSetting对象.SharedUserSetting s = mSharedUsers.get(name);if (s != null) {//如果在mSharedUsers里面存在给的包名对应的SharedUserSetting对象if (s.userId == uid) {//并且userId=uid,说明PMS已经为该应用程序分配过了uidreturn s;}PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate shared user, keeping first: " + name);return null;}// 如果mSharedUsers中不存在与该包名对应的SharedUserSetting对象,则为该应用程序分配一个参数uid所描述的Linux用户IDs = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);s.userId = uid;// 在系统中保存值为uid的Linux用户ID,成功返回trueif (addUserIdLPw(uid, s, name)) {//将新new的SharedUserSetting对象添加到mSharedUsers中mSharedUsers.put(name, s);return s;}return null;}
在系统保存值为uid的Linux用户ID,使用到了addUserIdLPw方法:
private boolean addUserIdLPw(int uid, Object obj, Object name) {if (uid > Process.LAST_APPLICATION_UID) {//大于19999说明是一个非法的uid,超出了uid的上线return false;}if (uid >= Process.FIRST_APPLICATION_UID) {//大于等于10000普通apk的uid,保存在mUserIds中//计算数组长度int N = mUserIds.size();//计算索引值final int index = uid - Process.FIRST_APPLICATION_UID;while (index >= N) {//索引值大于数组长度,在N到index之间的位置都填上nullmUserIds.add(null);N++;}// 如果数组的目标索引值位置有不为null的值,说明已经添加过if (mUserIds.get(index) != null) {PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate user id: " + uid+ " name=" + name);return false;}//没有添加过,则在索引位置填上objmUserIds.set(index, obj);} else {//小于10000,系统apk使用的uid,保存在mOtherUserIds中if (mOtherUserIds.get(uid) != null) {PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate shared id: " + uid+ " name=" + name);return false;}mOtherUserIds.put(uid, obj);}return true;}
PMS在创建了Settings之后会,调用一系列的addSharedUserLPw方法,形成下图的结构:
如图所示,PKMS将根据参数构建出SharedUserSettings对象,可以通过两个维度来引用创建出的对象,即名称和uid。
在Settings中mSharedUsers是一个map对象,利用名称作为索引管理SharedUserSettings对象。
Settings中的mOtherUserIds和mUserIds,均是利用userId作为索引管理SharedUserSettings对象。不同的是mOtherUserIds是SparseArray,
以系统uid作为键值;mUserIds是ArrayList,普通APK的uid为ArrayList的下标。
接着分析一下这个SharedUserSettings:
//重点看这一个方法void addPackage(PackageSetting packageSetting) {if (packages.add(packageSetting)) {//SharedUserSettings保存着一个packages集合用来存储packageSetting<-存储一个应用的安装信息setFlags(this.pkgFlags | packageSetting.pkgFlags);setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);}}
回到frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,继续分析PackageManagerService的构造方法:
// 应用安装器,构造函数传入的InstallerService,与底层Installd通信,installer由SystemServer构造mInstaller = installer;// 实例化优化器,PackageDexOptimizer用于辅助进行dex优化mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,"*dexopt*");//在pm/dex/包下,用于记录dex文件使用情况的记录在/data/system/package-dex-usage.list中mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);//定义一些回调函数mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());//定义权限更改监听器mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());//获取默认显示屏信息getDefaultDisplayMetrics(context, mMetrics);Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");//获得SystemConfig对象,通过该对象获取系统配置SystemConfig systemConfig = SystemConfig.getInstance();
由于SystemConfig是一个比较重要的类,下面的代码多次使用,进行详细分析.
frameworks/base/core/java/com/android/server/SystemConfig.java
static SystemConfig sInstance;//SystemConfig是单例模式public static SystemConfig getInstance() {synchronized (SystemConfig.class) {if (sInstance == null) {sInstance = new SystemConfig();}return sInstance;}}SystemConfig() {//从不少目录读取权限,可能有些目录不存在,但是/system一定存在//Environment.getRootDirectory()获取到的是/system目录,从/system目录读取权限// Read configuration from systemreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);// Read configuration from the old permissions dirreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);//从/vendor目录下读取权限// Allow Vendor to customize system configs around libs, features, permissions and appsint vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |ALLOW_APP_CONFIGS;readPermissions(Environment.buildPath(Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);readPermissions(Environment.buildPath(Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);//从/odm目录读取权限// Allow ODM to customize system configs around libs, features and appsint odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);//从/oem目录读取权限// Only allow OEM to customize featuresreadPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);readPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);}//接着看readPermissions方法的实现void readPermissions(File libraryDir, int permissionFlag) {//检测是否存在,是否可读// Read permissions from given directory.if (!libraryDir.exists() || !libraryDir.isDirectory()) {if (permissionFlag == ALLOW_ALL) {Slog.w(TAG, "No directory " + libraryDir + ", skipping");}return;}if (!libraryDir.canRead()) {Slog.w(TAG, "Directory " + libraryDir + " cannot be read");return;}// 遍历目标文件夹下所有的.xml文件// Iterate over the files in the directory and scan .xml filesFile platformFile = null;for (File f : libraryDir.listFiles()) {// 最后解析platform.xml文件// We'll read platform.xml lastif (f.getPath().endsWith("etc/permissions/platform.xml")) {platformFile = f;continue;}if (!f.getPath().endsWith(".xml")) {Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");continue;}if (!f.canRead()) {Slog.w(TAG, "Permissions library file " + f + " cannot be read");continue;}//将可读取的xml进行读取readPermissionsFromXml(f, permissionFlag);//下面详解readPermissionsFromXml方法}// 最后解析platform.xml文件,该文件的优先级最高// Read platform permissions last so it will take precedenceif (platformFile != null) {readPermissionsFromXml(platformFile, permissionFlag);}}
readPermissions就是从指定目录下,读取xml中的配置的权限信息.将xml文件转换成对应的数据结构.eg:/system/etc/permissions/platform.xml. 看一下platform.xml里面的主要标签:
- permission和group字段用于建立Linux层GID和Android层permission字段之间的映射关系;
- assign-permission用于向指定的uid赋予相应的权限;
- library字段用于可链接的指定系统库;
- allow-in-power-save-except-idle用于指定进程在省电模式下(非Idle)仍可上网;
- backup-transport-whitelisted-service用于指定服务具有传输备份数据的权利;
上面这些标签是用readPermissionsFromXml方法解析的.在readPermissions方法中两次调用到了readPermissionsFromXml,该方法是SystemConfig的核心方法.源码:
private void readPermissionsFromXml(File permFile, int permissionFlag) {FileReader permReader = null;try {//利用file构造fileReaderpermReader = new FileReader(permFile);} catch (FileNotFoundException e) {Slog.w(TAG, "Couldn't find or open permissions file " + permFile);return;}//读取系统属性"ro.config.low_ram",如果该属性为true,不会加载指定notLowRam的feature属性final boolean lowRam = ActivityManager.isLowRamDeviceStatic();try {//创建xml解析器XmlPullParser parser = Xml.newPullParser();//给解析器输入FileReader读取的内容parser.setInput(permReader);//寻找解析的起点int type;while ((type=parser.next()) != parser.START_TAG&& type != parser.END_DOCUMENT) {;}if (type != parser.START_TAG) {throw new XmlPullParserException("No start tag found");}if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {throw new XmlPullParserException("Unexpected start tag in " + permFile+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");}//根据SystemConfig构造方法中设置的flag,决定当前目录下,从xml文件中解析内容的范围boolean allowAll = permissionFlag == ALLOW_ALL;boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;while (true) {XmlUtils.nextElement(parser);//解析完成退出if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {break;}String name = parser.getName();//解析group标签if ("group".equals(name) && allowAll) {//以字符串的形式获取属性值,如果获取的属性值是一个int,那么就要进行转换String gidStr = parser.getAttributeValue(null, "gid");if (gidStr != null) {//将Gid字符串转化成整形,保存到mGlobalGids中int gid = android.os.Process.getGidForName(gidStr);mGlobalGids = appendInt(mGlobalGids, gid);} else {Slog.w(TAG, "<group> without gid in " + permFile + " at "+ parser.getPositionDescription());}XmlUtils.skipCurrentTag(parser);continue;} else if ("permission".equals(name) && allowPermissions) {//解析permission标签String perm = parser.getAttributeValue(null, "name");if (perm == null) {Slog.w(TAG, "<permission> without name in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}perm = perm.intern();//调用readPermission解析permission标签,注意不是readPermissions方法!!!readPermission(parser, perm);} else if ("assign-permission".equals(name) && allowPermissions) {//解析assign-permission标签,记录uid分配的权限,最终记录到mSystemPermissions//获取权限名称String perm = parser.getAttributeValue(null, "name");if (perm == null) {Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}//获取该权限的uid的值String uidStr = parser.getAttributeValue(null, "uid");if (uidStr == null) {Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}//将uid从string转换成intint uid = Process.getUidForName(uidStr);if (uid < 0) {Slog.w(TAG, "<assign-permission> with unknown uid \""+ uidStr + " in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}//使用了字符串池,intern()是String类的方法perm = perm.intern();//从系统获取该uid已经拥有的权限ArraySet<String> perms = mSystemPermissions.get(uid);//如果该uid之前没有添加过权限,创建一个,并将添加到mSystemPermissions中if (perms == null) {perms = new ArraySet<String>();mSystemPermissions.put(uid, perms);//保存着以uid为key的权限映射表,是一个稀疏数组}//将uid新增的权限,加入到它的ArraySetperms.add(perm);XmlUtils.skipCurrentTag(parser);} else if ("library".equals(name) && allowLibs) {//解析library标签,最终记录到mSharedLibrariesString lname = parser.getAttributeValue(null, "name");String lfile = parser.getAttributeValue(null, "file");if (lname == null) {Slog.w(TAG, "<library> without name in " + permFile + " at "+ parser.getPositionDescription());} else if (lfile == null) {Slog.w(TAG, "<library> without file in " + permFile + " at "+ parser.getPositionDescription());} else {//Log.i(TAG, "Got library " + lname + " in " + lfile);//将解析好的library标签的内容添加到mSharedLibraries,是一个map集合mSharedLibraries.put(lname, lfile);}XmlUtils.skipCurrentTag(parser);continue;} else if ("feature".equals(name) && allowFeatures) {//解析feature标签String fname = parser.getAttributeValue(null, "name");int fversion = XmlUtils.readIntAttribute(parser, "version", 0);boolean allowed;if (!lowRam) {//如果是低内存就不解析allowed = true;} else {String notLowRam = parser.getAttributeValue(null, "notLowRam");allowed = !"true".equals(notLowRam);}if (fname == null) {Slog.w(TAG, "<feature> without name in " + permFile + " at "+ parser.getPositionDescription());} else if (allowed) {//将feature构造成featureInfo,加入到mAvailableFeatures对象中,该方法在SystemConfig中addFeature(fname, fversion);}XmlUtils.skipCurrentTag(parser);continue;} else if ("unavailable-feature".equals(name) && allowFeatures) {//mUnavailableFeatures保存不支持的featureString fname = parser.getAttributeValue(null, "name");if (fname == null) {Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "+ parser.getPositionDescription());} else {mUnavailableFeatures.add(fname);}XmlUtils.skipCurrentTag(parser);continue;} else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {//保存省电模式下,可以使用网络的应用String pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "+ permFile + " at " + parser.getPositionDescription());} else {//保存在mAllowInPowerSaveExceptIdle中mAllowInPowerSaveExceptIdle.add(pkgname);}XmlUtils.skipCurrentTag(parser);continue;} else if ("allow-in-power-save".equals(name) && allowAll) {//mAllowInPowerSave与mAllowInPowerSaveExceptIdle类似,权限更高//这与Android M新特性Doze and App Standby模式有关//DeviceIdleController用于判断设备是否进入Idle状态,进入Idle状态时,mAllowInPowerSaveExceptIdle中的应用要被禁掉//但mAllowInPowerSave中的应用仍可运行String pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "+ parser.getPositionDescription());} else {mAllowInPowerSave.add(pkgname);}XmlUtils.skipCurrentTag(parser);continue;} else if ("allow-in-data-usage-save".equals(name) && allowAll) {//mAllowInDataUsageSave保存此标签对应的packageName//貌似android 7新增了一个节省数据流量的能力,有此标签的应用在节省数据流量时,仍可访问网络String pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile+ " at " + parser.getPositionDescription());} else {mAllowInDataUsageSave.add(pkgname);}XmlUtils.skipCurrentTag(parser);continue;} else if ("allow-unthrottled-location".equals(name) && allowAll) {String pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<allow-unthrottled-location> without package in "+ permFile + " at " + parser.getPositionDescription());} else {mAllowUnthrottledLocation.add(pkgname);}XmlUtils.skipCurrentTag(parser);continue;} else if ("allow-implicit-broadcast".equals(name) && allowAll) {String action = parser.getAttributeValue(null, "action");if (action == null) {Slog.w(TAG, "<allow-implicit-broadcast> without action in " + permFile+ " at " + parser.getPositionDescription());} else {mAllowImplicitBroadcasts.add(action);}XmlUtils.skipCurrentTag(parser);continue;} else if ("app-link".equals(name) && allowAppConfigs) {String pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<app-link> without package in " + permFile + " at "+ parser.getPositionDescription());} else {mLinkedApps.add(pkgname);}XmlUtils.skipCurrentTag(parser);} else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {//mSystemUserWhitelistedApps保存此标签对应的packageName//指定以system user权限运行的appString pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile+ " at " + parser.getPositionDescription());} else {mSystemUserWhitelistedApps.add(pkgname);}XmlUtils.skipCurrentTag(parser);} else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {//mSystemUserBlacklistedApp保存此标签对应的packageName//指定在system user权限下,不应该运行的appString pkgname = parser.getAttributeValue(null, "package");if (pkgname == null) {Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile+ " at " + parser.getPositionDescription());} else {mSystemUserBlacklistedApps.add(pkgname);}XmlUtils.skipCurrentTag(parser);} else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {//mDefaultVrComponents保存此标签对应的packageName//指定默认运行在VR模式下的componentsString pkgname = parser.getAttributeValue(null, "package");String clsname = parser.getAttributeValue(null, "class");if (pkgname == null) {Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile+ " at " + parser.getPositionDescription());} else if (clsname == null) {Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile+ " at " + parser.getPositionDescription());} else {mDefaultVrComponents.add(new ComponentName(pkgname, clsname));}XmlUtils.skipCurrentTag(parser);} else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {//mBackupTransportWhitelist保存此标签对应的packageName//保存能够传输备份数据的服务String serviceName = parser.getAttributeValue(null, "service");if (serviceName == null) {Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "+ permFile + " at " + parser.getPositionDescription());} else {ComponentName cn = ComponentName.unflattenFromString(serviceName);if (cn == null) {Slog.w(TAG,"<backup-transport-whitelisted-service> with invalid service name "+ serviceName + " in "+ permFile+ " at " + parser.getPositionDescription());} else {mBackupTransportWhitelist.add(cn);}}XmlUtils.skipCurrentTag(parser);} else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)&& allowAppConfigs) {String pkgname = parser.getAttributeValue(null, "package");String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");if (pkgname == null || carrierPkgname == null) {Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"+ " without package or carrierAppPackage in " + permFile + " at "+ parser.getPositionDescription());} else {List<String> associatedPkgs =mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(carrierPkgname);if (associatedPkgs == null) {associatedPkgs = new ArrayList<>();mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(carrierPkgname, associatedPkgs);}associatedPkgs.add(pkgname);}XmlUtils.skipCurrentTag(parser);} else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {readPrivAppPermissions(parser);} else {XmlUtils.skipCurrentTag(parser);continue;}}} catch (XmlPullParserException e) {Slog.w(TAG, "Got exception parsing permissions.", e);} catch (IOException e) {Slog.w(TAG, "Got exception parsing permissions.", e);} finally {IoUtils.closeQuietly(permReader);}//对相关的feature加密// Some devices can be field-converted to FBE, so offer to splice in// those features if not already defined by the static configif (StorageManager.isFileEncryptedNativeOnly()) {addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);}//移除mUnavailableFeatures中记录的不支持的featurefor (String featureName : mUnavailableFeatures) {removeFeature(featureName);}}
回到frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,继续分析PackageManagerService的构造方法:
mGlobalGids = systemConfig.getGlobalGids();mSystemPermissions = systemConfig.getSystemPermissions();mAvailableFeatures = systemConfig.getAvailableFeatures();Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
分别调用SystemConfig类的getGlobalGids(),getSystemPermissions(),getAvailableFeatures()方法:
public int[] getGlobalGids() {return mGlobalGids;}public SparseArray<ArraySet<String>> getSystemPermissions() {return mSystemPermissions;}public ArrayMap<String, FeatureInfo> getAvailableFeatures() {return mAvailableFeatures;}
可以看出这三个方法只是将readPermissionsFromXml方法中解析xml文件得到的数据结构获取出来.PMS调用SystemConfig的作用就是解析xml.
回到frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,继续分析PackageManagerService的构造方法:
//mHandlerThread将负责Apk的安装和卸载mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();//PackageHandler、ProcessLoggingHandler共用ServiceThread// 以mHandlerThread线程的looper创建的Handler实例,该Handler运行在mHandlerThread线程// 该消息循环用于处理apk的安装请求mHandler = new PackageHandler(mHandlerThread.getLooper());mProcessLoggingHandler = new ProcessLoggingHandler();//Watchdog监控ServiceThread是否长时间阻塞Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);mInstantAppRegistry = new InstantAppRegistry(this);//在/data目录下创建一系列的目录File dataDir = Environment.getDataDirectory();//获取的是/data目录mAppInstallDir = new File(dataDir, "app");//data/app保存用户自己的appmAppLib32InstallDir = new File(dataDir, "app-lib");mAsecInternalPath = new File(dataDir, "app-asec").getPath();mDrmAppPrivateInstallDir = new File(dataDir, "app-private");// /data/app-parivate目录,保存的是受DRM保护的私有app//实例化多用户管理服务,用于管理多用户sUserManager = new UserManagerService(context, this,new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);//取出SystemConfig中的mPermissions//向包管理器中传播权限配置。// Propagate permission configuration in to package manager.ArrayMap<String, SystemConfig.PermissionEntry> permConfig= systemConfig.getPermissions();//从SystemConfig中的mPermissions获取信息,存储到mSettings.mPermissions中for (int i=0; i<permConfig.size(); i++) {SystemConfig.PermissionEntry perm = permConfig.valueAt(i);// 根据权限名获取基本权限信息BasePermission bp = mSettings.mPermissions.get(perm.name);if (bp == null) {bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);mSettings.mPermissions.put(perm.name, bp);}if (perm.gids != null) {bp.setGids(perm.gids, perm.perUser);}}//取出systemConfig对象中的链接库信息,保存到PKMS中ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();final int builtInLibCount = libConfig.size();for (int i = 0; i < builtInLibCount; i++) {String name = libConfig.keyAt(i);String path = libConfig.valueAt(i);addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);}// 解析SELinux的策略文件mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");//mFirstBoot用于判断机器是否时第一次开机,这里的readLPw恢复上一次的应用程序安装信息,扫描package.xml文件,如果没有该文件判断为第一次开机mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
Android系统每次启动时,都会重新安装一遍系统中的应用程序,但是有些应用程序信息每次安装都是需要保持一致的,
如应用程序的Linux用户ID等。否则应用程序每次在系统重启后表现可能不一致。因此PMS每次在安装完成应用程序之后,
都需要将它们的信息保存下来,以便下次安装时可以恢复回来。恢复上一次的应用程序安装信息是通过Settings类的readLPw方法实现的。
重点分析Settings类的方法readLPw:
进入frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {FileInputStream str = null;// 先检查/data/system/packages-backup.xml文件是否存在,// 如果存在就将它的内容作为上一次的应用程序安装信息if (mBackupSettingsFilename.exists()) {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()) {// If both the backup and settings file exist, we// ignore the settings since it might have been// corrupted.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();mKeySetRefs.clear();mInstallerPackages.clear();try {if (str == null) {if (!mSettingsFilename.exists()) {mReadMessages.append("No settings file found\n");PackageManagerService.reportSettingsProblem(Log.INFO,"No settings file; creating initial state");// It's enough to just touch version details to create them// with default values// 如果原文件不存在,根据默认值创建版本信息。findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();return false;}str = new FileInputStream(mSettingsFilename);}XmlPullParser parser = Xml.newPullParser();parser.setInput(str, StandardCharsets.UTF_8.name());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");/// M: Create version info when packages.xml is corruptfindOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);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;}String tagName = parser.getName();if (tagName.equals("package")) {// 解析标签为package的元素,一个应用一个package标签.// 获取上一次安装这个应用程序时所分配给它的Linux用户IDreadPackageLPw(parser);//1} else if (tagName.equals("permissions")) {// 解析系统定义了哪些权限,由哪个包定义readPermissionsLPw(mPermissions, parser);} else if (tagName.equals("permission-trees")) {readPermissionsLPw(mPermissionTrees, parser);} else if (tagName.equals("shared-user")) {// shared-user标签是以sharedUserId的名字为name属性,然后为它分配一个userId赋值给userId属性。// 其他应用用到该sharedUserId的,userId都是shared-user标签中的userId属性值// 解析上一次应用程序安装信息中的共享Linux用户信息// 就是前面在Settings类中addSharedUserLPw方法写信息,最终保存在mSharedUsers中readSharedUserLPw(parser);//解析上一次应用程序安装信息中的共享linux用户信息} else if (tagName.equals("preferred-packages")) {......str.close();} catch (XmlPullParserException e) {......
分析readPackageLPw方法:
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {//定义变量......try {//获取应用程序包名,Linux用户Id,sharedUserId等信息,将前面定义的变量初始化name = parser.getAttributeValue(null, ATTR_NAME);realName = parser.getAttributeValue(null, "realName");idStr = parser.getAttributeValue(null, "userId");uidError = parser.getAttributeValue(null, "uidError");sharedIdStr = parser.getAttributeValue(null, "sharedUserId");codePathStr = parser.getAttributeValue(null, "codePath");resourcePathStr = parser.getAttributeValue(null, "resourcePath");//初始化变量,并且做相应的合法性判断,eg:==NULL?.....} else if (userId > 0) {//packageSetting用于保存一个应用程序的的安装信息,使用addPackageLPw将以上app的安装信息封装成packageSetting对象保存在mPackages中packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),//addPackageLPw源码分析new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,pkgPrivateFlags, flagsEx, parentPackageName, null /*childPackageNames*/,null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);//对返回的packageSetting对象进行合法性判断if (PackageManagerService.DEBUG_SETTINGS)Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="+ userId + " pkg=" + packageSetting);if (packageSetting == null) {PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "+ userId + " while parsing settings at "+ parser.getPositionDescription());} else {packageSetting.setTimeStamp(timeStamp);packageSetting.firstInstallTime = firstInstallTime;packageSetting.lastUpdateTime = lastUpdateTime;}
进入addPackageLPw方法:
// 在系统中保存值为userId的Linux用户ID// 在PMS中,每一个应用程序的安装信息都是使用一个PackageSetting对象来描述的。这些对象保存在mPackages中。PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,String legacyNativeLibraryPathString, String primaryCpuAbiString,String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, intpkgFlags, int pkgPrivateFlags, int flagsEx, String parentPackageName,List<String> childPackageNames, String[] usesStaticLibraries,int[] usesStaticLibraryNames) {PackageSetting p = mPackages.get(name);if (p != null) {//判断一下mPackages是否已经有了if (p.appId == uid) {return p;//在mPackages应经存在,直接将mPackages里的PackageSetting对象直接返回}PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate package, keeping first: " + name);return null;}//mPackages里面没有,依据传入的信息,new一个PackageSetting对象,并且添加到mPackages中,并将该对象返回/// M: [FlagExt] Add flagsExp = new PackageSetting(name, realName, codePath, resourcePath,legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, flagsEx, parentPackageName,childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);p.appId = uid;if (addUserIdLPw(uid, p, name)) {//addUserIdLPw是在系统中保存值为uid的Linux用户Id,该方法在前面分析过mPackages.put(name, p);return p;}return null;}
回到readPackageLPw方法,继续分析:
} else if (sharedIdStr != null) {// 如果sharedIdStr不为null,说明安装该应用时PMS给它分配了一个共享的uid。此时不能马上保存该uid,// 因为这个uid不属于它自己所有,而是所有shareuserId了该uid的app共享.所以等解析完shared-user节点之后,再为它保存上一次所使用的Linux用户IDif (sharedUserId > 0) {/// M: [FlagExt] Add flagsExpackageSetting = new PackageSetting(name.intern(), realName, new File(codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,versionCode, pkgFlags, pkgPrivateFlags, flagsEx, parentPackageName,null /*childPackageNames*/, sharedUserId,null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);packageSetting.setTimeStamp(timeStamp);packageSetting.firstInstallTime = firstInstallTime;packageSetting.lastUpdateTime = lastUpdateTime;//将是shareuserId的应用信息,先暂时保存在mPendingPackages中mPendingPackages.add(packageSetting);if (PackageManagerService.DEBUG_SETTINGS)Log.i(PackageManagerService.TAG, "Reading package " + name+ ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);} else {PackageManagerService.reportSettingsProblem(Log.WARN,"Error in package manager settings: package " + name+ " has bad sharedId " + sharedIdStr + " at "+ parser.getPositionDescription());}} else {PackageManagerService.reportSettingsProblem(Log.WARN,"Error in package manager settings: package " + name + " has bad userId "+ idStr + " at " + parser.getPositionDescription());}} catch (NumberFormatException e) {PackageManagerService.reportSettingsProblem(Log.WARN,"Error in package manager settings: package " + name + " has bad userId "+ idStr + " at " + parser.getPositionDescription());}if (packageSetting != null) {packageSetting.uidError = "true".equals(uidError);packageSetting.installerPackageName = installerPackageName;packageSetting.isOrphaned = "true".equals(isOrphaned);packageSetting.volumeUuid = volumeUuid;packageSetting.categoryHint = categoryHint;packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;packageSetting.primaryCpuAbiString = primaryCpuAbiString;packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;packageSetting.updateAvailable = "true".equals(updateAvailable);// Handle legacy string here for single-user modefinal String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);if (enabledStr != null) {try {packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null);} catch (NumberFormatException e) {if (enabledStr.equalsIgnoreCase("true")) {packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, null);} else if (enabledStr.equalsIgnoreCase("false")) {packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);} else if (enabledStr.equalsIgnoreCase("default")) {packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);} else {PackageManagerService.reportSettingsProblem(Log.WARN,"Error in package manager settings: package " + name+ " has bad enabled value: " + idStr + " at "+ parser.getPositionDescription());}}} else {packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);}if (installerPackageName != null) {mInstallerPackages.add(installerPackageName);}final String installStatusStr = parser.getAttributeValue(null, "installStatus");if (installStatusStr != null) {if (installStatusStr.equalsIgnoreCase("false")) {packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_INCOMPLETE;} else {packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;}}int outerDepth = parser.getDepth();int type;while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {continue;}String tagName = parser.getName();// Legacyif (tagName.equals(TAG_DISABLED_COMPONENTS)) {readDisabledComponentsLPw(packageSetting, parser, 0);} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {readEnabledComponentsLPw(packageSetting, parser, 0);} else if (tagName.equals("sigs")) {packageSetting.signatures.readXml(parser, mPastSignatures);} else if (tagName.equals(TAG_PERMISSIONS)) {readInstallPermissionsLPr(parser,packageSetting.getPermissionsState());packageSetting.installPermissionsFixed = true;} else if (tagName.equals("proper-signing-keyset")) {long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));Integer refCt = mKeySetRefs.get(id);if (refCt != null) {mKeySetRefs.put(id, refCt + 1);} else {mKeySetRefs.put(id, 1);}packageSetting.keySetData.setProperSigningKeySet(id);} else if (tagName.equals("signing-keyset")) {// from v1 of keysetmanagerservice - no longer used} else if (tagName.equals("upgrade-keyset")) {long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));packageSetting.keySetData.addUpgradeKeySetById(id);} else if (tagName.equals("defined-keyset")) {long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));String alias = parser.getAttributeValue(null, "alias");Integer refCt = mKeySetRefs.get(id);if (refCt != null) {mKeySetRefs.put(id, refCt + 1);} else {mKeySetRefs.put(id, 1);}packageSetting.keySetData.addDefinedKeySet(id, alias);} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {readDomainVerificationLPw(parser, packageSetting);} else if (tagName.equals(TAG_CHILD_PACKAGE)) {String childPackageName = parser.getAttributeValue(null, ATTR_NAME);if (packageSetting.childPackageNames == null) {packageSetting.childPackageNames = new ArrayList<>();}packageSetting.childPackageNames.add(childPackageName);} else {PackageManagerService.reportSettingsProblem(Log.WARN,"Unknown element under <package>: " + parser.getName());XmlUtils.skipCurrentTag(parser);}}} else {XmlUtils.skipCurrentTag(parser);}}
回到readLPw继续分析:
// If the build is setup to drop runtime permissions// on update drop the files before loading them.if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {final VersionInfo internal = getInternalVersion();if (!Build.FINGERPRINT.equals(internal.fingerprint)) {for (UserInfo user : users) {mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(user.id);}}}//在readPackageLPw方法中将shareuserId的app安装信息都暂时的保存在mPendingPackages,现在进行处理final int N = mPendingPackages.size();for (int i = 0; i < N; i++) {final PackageSetting p = mPendingPackages.get(i);final int sharedUserId = p.getSharedUserId();// 根据uid获取对应的对象,如果在mUserIds或mOtherUserIds中存在一个与userId对应的Object对象,// 且该对象是SharedUserSetting的类型,则说明pp所描述的应用程序上一次所使用的Linux用户ID是有效的final Object idObj = getUserIdLPr(sharedUserId);//每一个共享Linux用户Id都是使用SharedUserSetting对象来描述,并且保存在mSharedUsers中if (idObj instanceof SharedUserSetting) {final SharedUserSetting sharedUser = (SharedUserSetting) idObj;p.sharedUser = sharedUser;p.appId = sharedUser.userId;//addPackageSettingLPw将PackageSetting对象p添加到mPackages,把sharedUser赋给p.sharedUser保存addPackageSettingLPw(p, sharedUser);} else if (idObj != null) {String msg = "Bad package setting: package " + p.name + " has shared uid "+ sharedUserId + " that is not a shared uid\n";mReadMessages.append(msg);PackageManagerService.reportSettingsProblem(Log.ERROR, msg);} else {String msg = "Bad package setting: package " + p.name + " has shared uid "+ sharedUserId + " that is not defined\n";mReadMessages.append(msg);PackageManagerService.reportSettingsProblem(Log.ERROR, msg);}}mPendingPackages.clear();if (mBackupStoppedPackagesFilename.exists()|| mStoppedPackagesFilename.exists()) {// Read old filereadStoppedLPw();mBackupStoppedPackagesFilename.delete();mStoppedPackagesFilename.delete();// Migrate to new file formatwritePackageRestrictionsLPr(UserHandle.USER_SYSTEM);} else {for (UserInfo user : users) {readPackageRestrictionsLPr(user.id);}}for (UserInfo user : users) {mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);}/** Make sure all the updated system packages have their shared users* associated with them.*/final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();while (disabledIt.hasNext()) {final PackageSetting disabledPs = disabledIt.next();final Object id = getUserIdLPr(disabledPs.appId);if (id != null && id instanceof SharedUserSetting) {disabledPs.sharedUser = (SharedUserSetting) id;}}mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "+ mSharedUsers.size() + " shared uids\n");writeKernelMappingLPr();return true;}
经过上面的readLPw函数,将之前手机里面的安装信息都加载进来,接下来进入下一个阶段:开始扫描手机指定放apk的目录.
回到frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,继续分析PackageManagerService的构造方法:
// 记录开始扫描的时间long startTime = SystemClock.uptimeMillis();.....//获取目录:/system/framework/File frameworkDir = new File(Environment.getRootDirectory(), "framework");.....//初始化扫描参数// Set flag to monitor and not change apk file paths when// scanning install directories.int scanFlags = SCAN_BOOTING | SCAN_INITIAL;if (mIsUpgrade || mFirstBoot) {scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;}// Collect vendor overlay packages. (Do this before scanning any apps.)// For security and version matching reason, only consider// overlay packages if they reside in the right directory.scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags//scanDirTracedLI详解其实调用还是scanDirLI,下来分析scanDirLI| PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);//依次扫描放apk的目录......
scanDirLI是用来扫描一个指定目录下的apk文件.接下来分析scanDirLI方法:
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {final File[] files = dir.listFiles();if (ArrayUtils.isEmpty(files)) {Log.d(TAG, "No files in app dir " + dir);return;}/// M: Add for Mtprof tool.addBootEvent("Android:PMS_scan_data:" + dir.getPath().toString());//为了加快扫描速度,使用多线程进行扫描,启动多线程扫描来自QCOM平台int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;ParallelPackageParser parallelPackageParser = new ParallelPackageParser(mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,mParallelPackageParserCallback);// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {// Added by zhongyang_lib for apk libif(file == null){continue;}try{if(!file.getAbsolutePath().equals(file.getCanonicalPath()) && file.getName().endsWith(".so")) {//indicate file is a link fileLog.i(TAG,"file: "+file.getAbsolutePath()+" is a link file: "+ file.getCanonicalPath());file = new File(file.getCanonicalPath());}}catch(IOException e){Log.e(TAG,"scan link file:"+file.getAbsolutePath()+" caused an exception",e);continue;}parallelPackageParser.submit(file, parseFlags);fileCount++;}// Process results one by onefor (; fileCount > 0; fileCount--) {ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();Throwable throwable = parseResult.throwable;int errorCode = PackageManager.INSTALL_SUCCEEDED;//merge from qcom platform by tian.pan@tcl.com for task5306252 startRunnable scanTask = new Runnable() {public void run() {int parserErrorCode = errorCode;if (throwable == null) {// Static shared libraries have synthetic package namesif (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(parseResult.pkg);}try {if (errorCode == PackageManager.INSTALL_SUCCEEDED) {//使用scanPackageLI解析apk文件scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,currentTime, null);}} catch (PackageManagerException e) {parserErrorCode = e.error;Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());}} else if (throwable instanceof PackageParser.PackageParserException) {PackageParser.PackageParserException e = (PackageParser.PackageParserException)throwable;parserErrorCode = e.error;Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());} else {throw new IllegalStateException("Unexpected exception occurred while parsing "+ parseResult.scanFile, throwable);}// Delete invalid userdata appsif ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&parserErrorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile);//删除无效的apk文件removeCodePathLI(parseResult.scanFile);}}};if (dealer != null) {dealer.addTask(scanTask);} else {scanTask.run();}}if (dealer != null) {dealer.waitAll();}//merge from qcom platform by tian.pan@tcl.com for task5306252 endparallelPackageParser.close();}
scanDirLI方法只是去扫描了指定的文件夹
- /system/framework
- /system/app
- /vendor/app
- /data/app
/data/app-private 然后并没有处理扫描到的apk文件,而是交给scanPackageLI去处理, 上面代码中使用到了一个核心的方法scanPackageLI,这个方法就是实现对apk文件进行解析和安装的文件.该方法有三个,参数对应的是6参数的,来看源码:
/*** Scans a package and returns the newly parsed package.//返回的是刚刚解析的这个包.* @throws PackageManagerException on a parse error.*/private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)throws PackageManagerException {// If the package has children and this is the first dive in the function// we scan the package with the SCAN_CHECK_ONLY flag set to see whether all// packages (parent and children) would be successfully scanned before the// actual scan since scanning mutates internal state and we want to atomically// install the package and its children.// 从注释看,一个package里面可能存在多个需要解析的文件if ((scanFlags & SCAN_CHECK_ONLY) == 0) {if (pkg.childPackages != null && pkg.childPackages.size() > 0) {scanFlags |= SCAN_CHECK_ONLY;}} else {scanFlags &= ~SCAN_CHECK_ONLY;}// Scan the parent//真正的解析工作是scanPackageInternalLI,返回的解析的包也是scanPackageInternalLI返回的PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,scanFlags, currentTime, user);// Scan the childrenfinal int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;for (int i = 0; i < childCount; i++) {PackageParser.Package childPackage = pkg.childPackages.get(i);scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,//是scanPackageInternalLI干活currentTime, user);}if ((scanFlags & SCAN_CHECK_ONLY) != 0) {return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);}return scannedPkg;}
OTA升级使用到scanPackageInternalLI,该方法作用就是比对新旧包,判断是否更换.分析scanPackageInternalLI的源码:
/*** Scans a package and returns the newly parsed package.* @throws PackageManagerException on a parse error.*///scanPackageInternalLI方法是把扫描到的androidmainifest数据和之前在手机内扫描得到的数据做对比,然后进行对应操作。private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)throws PackageManagerException {PackageSetting ps = null;PackageSetting updatedPkg;// readersynchronized (mPackages) {// 看是否是已知的包,并对其进行操作// Look to see if we already know about this package.String oldName = mSettings.getRenamedPackageLPr(pkg.packageName);if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {// This package has been renamed to its original name. Let's// use that.ps = mSettings.getPackageLPr(oldName);}// If there was no original package, see one for the real package name.if (ps == null) {ps = mSettings.getPackageLPr(pkg.packageName);}// Check to see if this package could be hiding/updating a system// package. Must look for it either under the original or real// package name depending on our state.updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);// 非第一次启动 如果是一个已卸载的系统应用或者vendor app(厂商应用)则直接返回null/// M: Skip an uninstalled system or vendor appif (!isFirstBoot() && (isVendorApp(pkg) || isSystemApp(pkg))&& ((updatedPkg != null)|| (ps.getInstallStatus() == PackageSettingBase.PKG_INSTALL_INCOMPLETE))) {Slog.d(TAG, "Skip scanning " + scanFile.toString() + ", package " + updatedPkg +", install status: " + ps.getInstallStatus());return null;} else if (ps == null && updatedPkg != null) {Slog.d(TAG, "Skip scanning uninstalled package: " + pkg.packageName);return null;}// If this is a package we don't know about on the system partition, we// may need to remove disabled child packages on the system partition// or may need to not add child packages if the parent apk is updated// on the data partition and no longer defines this child package.if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {// If this is a parent package for an updated system app and this system// app got an OTA update which no longer defines some of the child packages// we have to prune them from the disabled system packages.PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);if (disabledPs != null) {//和OTA升级相关final int scannedChildCount = (pkg.childPackages != null)? pkg.childPackages.size() : 0;final int disabledChildCount = disabledPs.childPackageNames != null? disabledPs.childPackageNames.size() : 0;for (int i = 0; i < disabledChildCount; i++) {String disabledChildPackageName = disabledPs.childPackageNames.get(i);boolean disabledPackageAvailable = false;for (int j = 0; j < scannedChildCount; j++) {PackageParser.Package childPkg = pkg.childPackages.get(j);if (childPkg.packageName.equals(disabledChildPackageName)) {disabledPackageAvailable = true;break;}}if (!disabledPackageAvailable) {mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);}}}}}// 首先检查是否包含更新的系统包,包括vendor目录下final boolean isUpdatedPkg = updatedPkg != null;/// M: [Operator] Package in vendor folder should also be checked.final boolean isUpdatedSystemPkg = isUpdatedPkg&& ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0|| (policyFlags & PackageParser.PARSE_IS_OPERATOR) != 0);boolean isUpdatedPkgBetter = false;// First check if this is a system package that may involve an updateif (isUpdatedSystemPkg) {//是一个更新的系统包// If new package is not located in "/system/priv-app" (e.g. due to an OTA),// it needs to drop FLAG_PRIVILEGED.//如果新的包不在priv-app下,应该下调Flagif (locationIsPrivileged(scanFile)) {updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;} else {updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;}//如果扫描到的包路径发生了变化,根据已经存储的内容来判断该如何操作此app,记录下flag用于后面的使用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);///M: [Operator] Allow vendor package downgrade.///Always install the updated one on data partition.if (pkg.mVersionCode < ps.versionCode|| ((policyFlags & PackageParser.PARSE_IS_OPERATOR) != 0)/// M: Removable system app support|| isRemovableSysApp(pkg.packageName)) {// The system package has been updated and the code path does not match// Ignore entry. Skip it.if (DEBUG_INSTALL) 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.resourcePath = scanFile;updatedPkg.resourcePathString = scanFile.toString();}updatedPkg.pkg = pkg;updatedPkg.versionCode = pkg.mVersionCode;// Update the disabled system child packages to point to the package too.final int childCount = updatedPkg.childPackageNames != null? updatedPkg.childPackageNames.size() : 0;for (int i = 0; i < childCount; i++) {String childPackageName = updatedPkg.childPackageNames.get(i);PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(childPackageName);if (updatedChildPkg != null) {updatedChildPkg.pkg = pkg;updatedChildPkg.versionCode = pkg.mVersionCode;}}} else {// /system下的package比我们在/data目下pacakage好// 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// writersynchronized (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, getAppDexInstructionSets(ps));synchronized (mInstallLock) {args.cleanUpResourcesLI();}synchronized (mPackages) {mSettings.enableSystemPackageLPw(ps.name);}isUpdatedPkgBetter = true;}}}String resourcePath = null;String baseResourcePath = null;if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !isUpdatedPkgBetter) {if (ps != null && ps.resourcePathString != null) {resourcePath = ps.resourcePathString;baseResourcePath = ps.resourcePathString;} else {// Should not happen at all. Just log an error.Slog.e(TAG, "Resource path not set for package " + pkg.packageName);}} else {resourcePath = pkg.codePath;baseResourcePath = pkg.baseCodePath;}//精确的设置app的路径// Set application objects path explicitly.pkg.setApplicationVolumeUuid(pkg.volumeUuid);pkg.setApplicationInfoCodePath(pkg.codePath);pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);pkg.setApplicationInfoResourcePath(resourcePath);pkg.setApplicationInfoBaseResourcePath(baseResourcePath);pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);// throw an exception if we have an update to a system application, but, it's not more// recent than the package we've already scannedif (isUpdatedSystemPkg && !isUpdatedPkgBetter) {// Set CPU Abis to application info.if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPkg);derivePackageAbi(pkg, scanFile, cpuAbiOverride, false, mAppLib32InstallDir);} else {pkg.applicationInfo.primaryCpuAbi = updatedPkg.primaryCpuAbiString;pkg.applicationInfo.secondaryCpuAbi = updatedPkg.secondaryCpuAbiString;}throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "+ scanFile + " ignored: updated version " + ps.versionCode+ " better than this " + pkg.mVersionCode);}if (isUpdatedPkg) {// An updated system app will not have the PARSE_IS_SYSTEM flag set// initially/** M: [Operator] Only system app have system flag @{ */if (isSystemApp(updatedPkg)) {policyFlags |= PackageParser.PARSE_IS_SYSTEM;}/// M: [Operator] Add operator flags for updated vendor package/// We should consider an operator app with flag changed after OTAif ((isVendorApp(updatedPkg) || locationIsOperator(updatedPkg.codePath))&& locationIsOperator(ps.codePath)) {policyFlags |= PackageParser.PARSE_IS_OPERATOR;}/** @} */// An updated privileged app will not have the PARSE_IS_PRIVILEGED// flag set initiallyif ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;}}//扫描验证证书// Verify certificates against what was last scannedcollectCertificatesLI(ps, pkg, scanFile, policyFlags);//一个新的system app,但有一个已安装的同名非系统app// 这里的代码是处理这样的情景:当系统更新后,可能更新包会多出一些system app出来,那么如果此时用户恰好安装了一个同包名的app.// 两者的签名还不一致,那么就删除扫描到的系统应用的信息。// 当两者签名一致时,如果扫描到的app版本更高,那么就删除安装的应用;如果扫描的app版本低,那么隐藏扫描到的系统应用。/** A new system app appeared, but we already had a non-system one of the* same name installed earlier.*/boolean shouldHideSystemApp = false;if (!isUpdatedPkg && ps != null&& (policyFlags & 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");try (PackageFreezer freezer = freezePackage(pkg.packageName,"scanPackageInternalLI")) {deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);}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) {//新添加app更旧,则隐藏此app,后续会重新添加shouldHideSystemApp = true;logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile+ " but new version " + pkg.mVersionCode + " better than installed "+ ps.versionCode + "; hiding system");} else {//如果比已安装的app新,则保留app数据直接安装更新此system app/** 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, getAppDexInstructionSets(ps));synchronized (mInstallLock) {args.cleanUpResourcesLI();}}}}// The apk is forward locked (not public) if its code and resources// are kept in different files. (except for app in either system or// vendor path).// TODO grab this value from PackageSettings//如果app中制定的资源在别的路径,则从PackageSettings中抓取if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {if (ps != null && !ps.codePath.equals(ps.resourcePath)) {policyFlags |= PackageParser.PARSE_FORWARD_LOCK;}}final int userId = ((user == null) ? 0 : user.getIdentifier());if (ps != null && ps.getInstantApp(userId)) {scanFlags |= SCAN_AS_INSTANT_APP;}// Note that we invoke the following method only if we are about to unpack an applicationPackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags//这里是5参数的scanPackageLI方法,第一个参数是PackageParser.Package类型| SCAN_UPDATE_SIGNATURE, currentTime, user);/** If the system app should be overridden by a previously installed* data, hide the system app now and let the /data/app scan pick it up* again.*/if (shouldHideSystemApp) {//该package被隐藏synchronized (mPackages) {mSettings.disableSystemPackageLPw(pkg.packageName, true);}}return scannedPkg;}
从上面的分析看出scanPackageInternalLI方法主要还是进行比对新旧package信息(主要是OTA升级带来的升级包),进行相关的参数判断,设置不同的参数,最后交给5参数的scanPackageLI方法, 从5参数的scanPackageLI方法开始才开始,所有合法性验证都通过,开始解析,安装这个package.:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,int scanFlags, long currentTime, @Nullable UserHandle user)throws PackageManagerException {boolean success = false;try {//scanPackageDirtyLI是安装的核心函数,不论是开机扫描还是adb安装都会调用到该函数进行APK安装final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,currentTime, user);success = true;return res;} finally {if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {// DELETE_DATA_ON_FAILURES is only used by frozen pathsdestroyAppDataLIF(pkg, UserHandle.USER_ALL,StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);}}}
快来看看这个scanPackageDirtyLI函数:
``` // 获得前面所解析的应用程序的组件配置信息,以及为这个应用程序分配Linux用户ID private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)throws PackageManagerException {
if (DEBUG_PACKAGE_SCANNING) { if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Scanning package " + pkg.packageName);
}
applyPolicy(pkg, policyFlags);
// Begin added by Xutao.Wu for Task5185134 on 2017/09/22 if (PackageManager.TCT_RETAILDEMO_PACKAGE_NAME.equals(pkg.packageName)){ if (DEBUG_INSTALL) Slog.w(TAG,”scanPackageDirtyLI, set retaildemo as SYSTEM PRIVILEGED”); pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; } // End added by Xutao.Wu for Task5185134 on 2017/09/22 // 方法的作用是判断给定的pkg是不是有效的 assertPackageIsValid(pkg, policyFlags, scanFlags);
// Initialize package source and resource directories final File scanFile = new File(pkg.codePath); final File destCodeFile = new File(pkg.applicationInfo.getCodePath()); final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
SharedUserSetting suid = null; PackageSetting pkgSetting = null;
// Getting the package setting may have a side-effect, so if we // are only checking if scan would succeed, stash a copy of the // old setting to restore at the end. PackageSetting nonMutatedPs = null;
// We keep references to the derived CPU Abis from settings in oder to reuse // them in the case where we’re not upgrading or booting for the first time. String primaryCpuAbiFromSettings = null; String secondaryCpuAbiFromSettings = null;
// writer // 为参数pkg所描述的应用程序分配Linux用户ID synchronized (mPackages) { if (pkg.mSharedUserId != null) {// 如果该应用有sharedUserId属性,则从mSettings中获取要为它分配的共享uid
// SIDE EFFECTS; may potentially allocate a new shared usersuid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);if (DEBUG_PACKAGE_SCANNING) {if ((policyFlags & PackageParser.PARSE_CHATTY) != 0)Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId+ "): packages=" + suid.packages);}
}
// Check if we are renaming from an original package name. PackageSetting origPackage = null; String realName = null; if (pkg.mOriginalPackages != null) {
// This package may need to be renamed to a previously// installed name. Let's check on that...final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage);if (pkg.mOriginalPackages.contains(renamed)) {// This package had originally been installed as the// original name, and we have already taken care of// transitioning to the new one. Just update the new// one to continue using the old name.realName = pkg.mRealPackage;if (!pkg.packageName.equals(renamed)) {// Callers into this function may have already taken// care of renaming the package; only do it here if// it is not already done.pkg.setPackageName(renamed);}} else {for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {if ((origPackage = mSettings.getPackageLPr(pkg.mOriginalPackages.get(i))) != null) {// We do have the package already installed under its// original name... should we use it?if (!verifyPackageUpdateLPr(origPackage, pkg)) {// New package is not compatible with original.origPackage = null;continue;} else if (origPackage.sharedUser != null) {// Make sure uid is compatible between packages.if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {Slog.w(TAG, "Unable to migrate data from " + origPackage.name+ " to " + pkg.packageName + ": old uid "+ origPackage.sharedUser.name+ " differs from " + pkg.mSharedUserId);origPackage = null;continue;}// TODO: Add case when shared user id is added [b/28144775]} else {if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "+ pkg.packageName + " to old name " + origPackage.name);}break;}}}
}
if (mTransferedPackages.contains(pkg.packageName)) {
Slog.w(TAG, "Package " + pkg.packageName+ " was transferred to another, but its .apk remains");
}
// See comments in nonMutatedPs declaration if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);if (foundPs != null) {nonMutatedPs = new PackageSetting(foundPs);}
}
if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) {
PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);if (foundPs != null) {primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;}
}
// 创建PackageSetting并设置 pkgSetting = mSettings.getPackageLPr(pkg.packageName); if (pkgSetting != null && pkgSetting.sharedUser != suid) {
PackageManagerService.reportSettingsProblem(Log.WARN,"Package " + pkg.packageName + " shared user changed from "+ (pkgSetting.sharedUser != null? pkgSetting.sharedUser.name : "<nothing>")+ " to "+ (suid != null ? suid.name : "<nothing>")+ "; replacing with new");pkgSetting = null;
} final PackageSetting oldPkgSetting =
pkgSetting == null ? null : new PackageSetting(pkgSetting);
final PackageSetting disabledPkgSetting =
mSettings.getDisabledSystemPkgLPr(pkg.packageName);
String[] usesStaticLibraries = null; if (pkg.usesStaticLibraries != null) {
usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];pkg.usesStaticLibraries.toArray(usesStaticLibraries);
}
if (pkgSetting == null) {
final String parentPackageName = (pkg.parentPackage != null)? pkg.parentPackage.packageName : null;final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;// REMOVE SharedUserSetting from method; update in a separate callpkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,/// M: [FlagExt] Add flagsExpkg.applicationInfo.flagsEx, user,true /*allowInstall*/, instantApp, parentPackageName,pkg.getChildPackageNames(), UserManagerService.getInstance(),usesStaticLibraries, pkg.usesStaticLibrariesVersions);// SIDE EFFECTS; updates system state; move elsewhereif (origPackage != null) {mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);}mSettings.addUserToSettingLPw(pkgSetting);
} else {
// REMOVE SharedUserSetting from method; update in a separate call.//// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,// secondaryCpuAbi are not known at this point so we always update them// to null here, only to reset them at a later point.Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile,pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),UserManagerService.getInstance(), usesStaticLibraries,pkg.usesStaticLibrariesVersions);
} // SIDE EFFECTS; persists system state to files on disk; move elsewhere mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
// SIDE EFFECTS; modifies system state; move elsewhere if (pkgSetting.origPackage != null) {
// If we are first transitioning from an original package,// fix up the new package's name now. We need to do this after// looking up the package under its new name, so getPackageLP// can take care of fiddling things correctly.pkg.setPackageName(origPackage.name);// File a report about this.String msg = "New package " + pkgSetting.realName+ " renamed to replace old package " + pkgSetting.name;reportSettingsProblem(Log.WARN, msg);// Make a note of it.if ((scanFlags & SCAN_CHECK_ONLY) == 0) {mTransferedPackages.add(origPackage.name);}// No longer need to retain this.pkgSetting.origPackage = null;
}
// SIDE EFFECTS; modifies system state; move elsewhere if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
// Make a note of it.mTransferedPackages.add(pkg.packageName);
}
if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {
/** M: [Operator] Operator package should not have FLAG_UPDATED_SYSTEM_APP @{ */PackageSetting oldPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);if (oldPs != null && !isVendorApp(oldPs)) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;}/** @} */
}
if ((scanFlags & SCAN_BOOTING) == 0
&& (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {// Check all shared libraries and map to their actual file path.// We only do this here for apps not on a system dir, because those// are the only ones that can fail an install due to this. We// will take care of the system apps by updating all of their// library paths after the scan is done. Also during the initial// scan don't update any libs as we do this wholesale after all// apps are scanned to avoid dependency based scanning.updateSharedLibrariesLPr(pkg, null);//创建共享库
}
if (mFoundPolicyFile) {
SELinuxMMAC.assignSeInfoValue(pkg);//设置SELinux策略
} pkg.applicationInfo.uid = pkgSetting.appId; pkg.mExtras = pkgSetting;
// Static shared libs have same package with different versions where// we internally use a synthetic package name to allow multiple versions// of the same package, therefore we need to compare signatures against// the package setting for the latest library version.//验证签名信息的合法性PackageSetting signatureCheckPs = pkgSetting;if (pkg.applicationInfo.isStaticSharedLibrary()) {SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);if (libraryEntry != null) {signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);}}if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {if (checkUpgradeKeySetLP(signatureCheckPs, pkg)) {// We just determined the app is signed correctly, so bring// over the latest parsed certs.pkgSetting.signatures.mSignatures = pkg.mSignatures;} else {if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,"Package " + pkg.packageName + " upgrade keys do not match the "+ "previously installed version");} else {pkgSetting.signatures.mSignatures = pkg.mSignatures;String msg = "System package " + pkg.packageName+ " signature changed; retaining data.";reportSettingsProblem(Log.WARN, msg);}}} else {try {// SIDE EFFECTS; compareSignaturesCompat() changes KeysetManagerServiceverifySignaturesLP(signatureCheckPs, pkg);// We just determined the app is signed correctly, so bring// over the latest parsed certs.pkgSetting.signatures.mSignatures = pkg.mSignatures;} catch (PackageManagerException e) {///M: Add for operator APP.if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0&& (policyFlags & PackageParser.PARSE_IS_OPERATOR) == 0) {throw e;}// The signature has changed, but this package is in the system// image... let's recover!pkgSetting.signatures.mSignatures = pkg.mSignatures;// However... if this package is part of a shared user, but it// doesn't match the signature of the shared user, let's fail.// What this means is that you can't change the signatures// associated with an overall shared user, which doesn't seem all// that unreasonable.if (signatureCheckPs.sharedUser != null) {if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {throw new PackageManagerException(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,"Signature mismatch for shared user: "+ pkgSetting.sharedUser);}}// File a report about this.String msg = "System package " + pkg.packageName+ " signature changed; retaining data.";reportSettingsProblem(Log.WARN, msg);}}if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {// This package wants to adopt ownership of permissions from// another package.// 是否需要获取其他包的权限for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {final String origName = pkg.mAdoptPermissions.get(i);final PackageSetting orig = mSettings.getPackageLPr(origName);if (orig != null) {if (verifyPackageUpdateLPr(orig, pkg)) {Slog.i(TAG, "Adopting permissions from " + origName + " to "+ pkg.packageName);// SIDE EFFECTS; updates permissions system state; move elsewheremSettings.transferPermissionsLPw(origName, pkg.packageName);}}}}}//确定进程名称pkg.applicationInfo.processName = fixProcessName(pkg.applicationInfo.packageName,pkg.applicationInfo.processName);if (pkg != mPlatformPackage) {// Get all of our default paths setuppkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);}final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);if ((scanFlags & SCAN_NEW_INSTALL) == 0) {if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");final boolean extractNativeLibs = !pkg.isLibrary();derivePackageAbi(pkg, scanFile, cpuAbiOverride, extractNativeLibs,mAppLib32InstallDir);Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);// Some system apps still use directory structure for native libraries// in which case we might end up not detecting abi solely based on apk// structure. Try to detect abi based on directory structure.if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&pkg.applicationInfo.primaryCpuAbi == null) {setBundledAppAbisAndRoots(pkg, pkgSetting);setNativeLibraryPaths(pkg, mAppLib32InstallDir);}} else {// This is not a first boot or an upgrade, don't bother deriving the// ABI during the scan. Instead, trust the value that was stored in the// package setting.pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;setNativeLibraryPaths(pkg, mAppLib32InstallDir);if (DEBUG_ABI_SELECTION) {Slog.i(TAG, "Using ABIS and native lib paths from settings : " +pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +pkg.applicationInfo.secondaryCpuAbi);}}} else {if ((scanFlags & SCAN_MOVE) != 0) {// We haven't run dex-opt for this move (since we've moved the compiled output too)// but we already have this packages package info in the PackageSetting. We just// use that and derive the native library path based on the new codepath.pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;}// Set native library paths again. For moves, the path will be updated based on the// ABIs we've determined above. For non-moves, the path will be updated based on the// ABIs we determined during compilation, but the path will depend on the final// package path (after the rename away from the stage path).setNativeLibraryPaths(pkg, mAppLib32InstallDir);}// This is a special case for the "system" package, where the ABI is// dictated by the zygote configuration (and init.rc). We should keep track// of this ABI so that we can deal with "normal" applications that run under// the same UID correctly.if (mPlatformPackage == pkg) {pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];}// If there's a mismatch between the abi-override in the package setting// and the abiOverride specified for the install. Warn about this because we// would've already compiled the app without taking the package setting into// account.if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +" for package " + pkg.packageName);}}// CPU描述pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;pkgSetting.cpuAbiOverrideString = cpuAbiOverride;// Copy the derived override back to the parsed package, so that we can// update the package settings accordingly.pkg.cpuAbiOverride = cpuAbiOverride;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName+ " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="+ pkg.applicationInfo.nativeLibraryRootRequiresIsa);}// Push the derived path down into PackageSettings so we know what to// clean up at uninstall time.pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Abis for package[" + pkg.packageName + "] are" +" primary=" + pkg.applicationInfo.primaryCpuAbi +" secondary=" + pkg.applicationInfo.secondaryCpuAbi);}// SIDE EFFECTS; removes DEX files from disk; move elsewhereif ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {// We don't do this here during boot because we can do it all// at once after scanning all existing packages.//// We also do this *before* we perform dexopt on this package, so that// we can avoid redundant dexopts, and also to make sure we've got the// code and package path correct.adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);}if (mFactoryTest && pkg.requestedPermissions.contains(android.Manifest.permission.FACTORY_TEST)) {pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;}if (isSystemApp(pkg)) {pkgSetting.isOrphaned = true;}// 更新安装时间// Take care of first install / last update times.final long scanFileTime = getLastModifiedTime(pkg, scanFile);if (currentTime != 0) {if (pkgSetting.firstInstallTime == 0) {pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {pkgSetting.lastUpdateTime = currentTime;}} else if (pkgSetting.firstInstallTime == 0) {// We need *something*. Take time time stamp of the file.pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;} else if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {if (scanFileTime != pkgSetting.timeStamp) {// A package on the system image has changed; consider this// to be an update.pkgSetting.lastUpdateTime = scanFileTime;}}pkgSetting.setTimeStamp(scanFileTime);if ((scanFlags & SCAN_CHECK_ONLY) != 0) {if (nonMutatedPs != null) {synchronized (mPackages) {mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);}}} else {final int userId = user == null ? 0 : user.getIdentifier();// Modify state for the given package setting// 该方法回设置很多东西包括services,providers,// 更新lib时杀掉依赖此lib的进程// 对framework-res.apk单独处理//更新设置apk的provider并更新到数据中// 广播接受者receivers,activities,permissionGroups,permissions,Instrumentation信息commitPackageSettings(pkg, pkgSetting, user, scanFlags(policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);if (pkgSetting.getInstantApp(userId)) {mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);}}return pkg;}
``` scanPackageDirtyLI流程总结如下:
- framework-res.apk单独处理
- 初始化代码路径和资源路径
- 创建共享库
- 验证签名信息的合法性
- 验证新的包的Provider不会与现有包冲突
- 是否需要获得其他包的权限
- 确定进程名称
- Lib库的更新设置操作等
- 跟新Settings参数
- 更新安装的时间
- 四大组件、权限、Instrumentation、把解析的这些信息注册到PMS
当然上面有不少是在commitPackageSettings方法中实现的.
到此apk就安装完成了,在PackageManagerService的构造方法.接下来的方法就是使用一下两个方法把
// 为申请了特定的资源访问权限的应用程序分配相应的Linux用户组ID
updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
// 将前面获取到的应用程序安装信息保存在本地的一个配置文件中,以便下一次再安装这些应用程序时
// 可以将需要保持一致的应用程序信息恢复回来
mSettings.writeLPr();
