PackageMangerService(简称PMS)是Android系统中负责管理所有Apk的安装、卸载更新等工作,其运行在SystemServer进程,维护管理系统中所有的Apk包管理,那它是如何管理众多APK的呢?它怎么知道系统中有哪些APK应用,这些Apk在什么时候安装、卸载和更新,PMS如何感知?就算它知道了系统中所有APK的存在,如何去建立一个维护呢?
带着以上诸多疑问,从源码的角度,来探寻这些问题,感悟PMS的精髓所在!
管理对象 — 静态存储在磁盘上的APK文件
管理者 — PMS管理器,系统中是唯一的
目标对象 — 活的应用程序,界面上能显示,能交互
首先,我作为PMS管理者,系统开机BOOT时,要扫描系统中的文件,寻找哪些是APK文件,然后针对处理,但是不可能扫描系统中所有的路径,肯定要事先做好约束,APK文件只能放在某些特定的目录下;还有一点,系统开机后,用户自行安装apk文件时,这个时候PMS不可能又去扫描一遍,最好的方法是提供一个命令接口,用户安装时通过调用这个命令接口,这样PMS就可以拿到用户安装的APK文件,然后分析之,以下几个路径是PMS开机启动时会扫描的几个路径:
system/app 系统自带的应用程序
data/app 用户程序安装的目录
vendor/app 厂商定制app目录
system/priv-app/ 系统私有App,其中service可以保活,一旦被杀死就会立即恢复,缺点无法正常升级
其次,PMS拿到APK文件后,要解析分享APK内容,拿到其中重要的东西,然后再保存到内存或数据库中,内存中用一系列赋值的数据模型建立好每个APK文件的映射,提供给Launcher程序,Launcher根据数据模型可以清楚指定有哪些应用,将其绘制到桌面上去,难点在于解析APK这块,你需要知道APK需要哪些权限、四大组件有哪些,lib动态库,使用的资源res文件等,解析后用内存数据模型维护好它们
实质上分析APK内容主要就是AndroidManifest.xml文件,然后把其中的内容映射到PackageParser.Package、PackageSetting中
最后,PMS在自己的进程里面,需要提供安装、卸载更新接口,其他用户进程通过binder跨进程来调用即可
按照我们上面的设计内容,从源码中去看看它是如何工作的?
位于SystemServer的run方法中,其中调用startBootstrapServices,启动Boot相关的服务:
private void startBootstrapServices() {
....
mOnlyCore表示是加密参数,true表示只解析加密的APK
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
....
}
进入main方法:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 自检初始化配置参数
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
把PMS加入到binder服务列表中去,名为package
ServiceManager.addService("package", m);
return m;
}
这里就正式进入PackageManager的领域了!
为什么要讲他的构造方法?首先,大量的工作内容都是在构造方法中完成的;其次,后续的安装、更新工作也是按照构造方法中这个套路的来执行的,所以它构造方法很重要!
构造方法中,有以下几个事件阶段,如下:
START阶段做了很多事情,依次阐述,源码如下,精简了部分:
Settings初始化工作,PMS中相关的配置工作类
Settings是整个系统的APK配置管理类,里面维护了所有apk的内存存储模型,mPakcages作为参数传递
进去,mPackages类型为ArrayMap<String, PackageParser.Package>,key为packagename
mSettings = new Settings(mPackages);
添加sharedId内容,拥有相同sharedid可以共享资源,也可以在同一个进程运行,后面会讲到
mSettings.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);
SystemConfig systemConfig = SystemConfig.getInstance();
解析相应文件(/system/etc/permission和(framework/base/data/etc/下的文件),
包括platform.xml和系统支持的各种硬件模块的feature,
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
SystemConfig主要工作内容是:
建立底层的uid和 group id同行上层permission之间的映射,可以指定一个权限与几个id的对应。当一个APK被授予这个权限时,它也同时属于这几个组。
例如:platform.xml文件内容有
即字符串蓝牙权限属于用户组net_bt,SystemConfig读取到蓝牙权限时,会用PermissionEntry实体保存,
name为android.permission.BLUETOOTH,而gid是字符串,会用Process将gid字符串转换为整型数字保存在
gids数组;也就是如下:
public static final class PermissionEntry {
public final String name;
public int[] gids;}
当我们应用程序在AndroidManifest.xml声明了BLUETOOTH权限时,实质就是将我们加入到gids中某个用户
组里面,这样就能够访问相应的文件
给一些底层用户分配权限,如给shell授予各种permission权限,把一个权限赋予uid,当进程只是用这个uid运行时,就具备了这个权限。
libary,系统增加的一些应用需要link扩展的jar。
feature,系统每增加一个硬件,都要添加相应的feature,将解析结果放入mSystemPermissions,mShareLibrariest,mSettings.mPermissions,mAvailableFeatures等几个集合供系统查询和权限配置使用。
然后看看Settings是个什么东西?其构造方法如下:
//传递进入mPackage当做一把锁
Settings(Object lock) {
参数一返回的/data目录
this(Environment.getDataDirectory(), lock);
}
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);
创建一些xml文件
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目录下创建一些xml文件,并且后续扫描解析apk后,会把apk信息保存到这些文件中去,
packages.xml 记录了系统中所有安装的应用信息,包括基本信息、签名和权限
packages-backup.xml 对packages.xml文件的备份文件
packages.list 存储了系统中应用的安装信息,主要包含包名、安装位置、UID、Debuggable属性等
packages-stopped.xml 记录系统强行停止的应用信息
packages-stopped-backup.xml 顾名思义,对packages-stopped文件的备份
插入一个SharedId知识点:
即AndroidManifest.xml里面的sharedUserId,两个应用声明相同的sharedUserId,就可以共享资源,如果签名相同还会运行在同一个进程下面;UID即用户User ID,Android中每个应用程序都有自己的一个UID,不同的UID应用程序之间是进程隔离的,但是如果我们在AndroidManifest.xml里面manifest标签里面声明相同的shareuserid,并且使用相同的签名,则两个应用程序是可以共享资源,并且运行在同一进程里面;
回到本文源码mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED),我们跟进去看看:
final ArrayMap<String, SharedUserSetting> mSharedUsers =
new ArrayMap<String, SharedUserSetting>();
//存放大于10000小于19999的UID的sharedUserSetting
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
//同上,只是保存小于10000的UIDsharedUidSetting
private final SparseArray<Object> mOtherUserIds =
new SparseArray<Object>();
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
map获取value
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
userId是SharedUserSetting成员,就是UID,这里指如果添加进来的UID和保存的相同就直接返回
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
创建一个新的SharedUserSetting
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
并且把这个uid的应用添加到mOtherUserIds或mUserIds结构体中去
if (addUserIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
private boolean addUserIdLPw(int uid, Object obj, Object name) {
last为19999
if (uid > Process.LAST_APPLICATION_UID) {
return false;
}
first为10000 如果UID大于10000的应用,添加到mUserIds结构中
if (uid >= Process.FIRST_APPLICATION_UID) {
int N = mUserIds.size();
final int index = uid - Process.FIRST_APPLICATION_UID;
while (index >= N) {
mUserIds.add(null);
N++;
}
if (mUserIds.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate user id: " + uid
+ " name=" + name);
return false;
}
mUserIds.set(index, obj);
小于10000的属于系统应用,添加到mOtherUserIds结构体中去
} else {
if (mOtherUserIds.get(uid) != null) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared id: " + uid
+ " name=" + name);
return false;
}
mOtherUserIds.put(uid, obj);
}
return true;
}
总结一下,Settings类里面有三个总要成员:
ArrayMap
ArrayList
我们用ps -ef查看我们应用进程时,其UID一般是Ux_axx的形式(如U0_a856),这是描述符,U表示userid,a表示appid,使用Process.myUID获取当前应用的UID,前者的x取值为UID/10000,后者为UID-10000
这里有一个问题?相同sharedID的不同应用在这三个数据模型内是如何保存的?答案就在SharedUserSetting
如上图,userId是用户ID值,name是其描述,ArraySet
扫描启动阶段,它会扫描某些特定目录,解析目录下存在的APK文件,并解析至,获得PackageParser.Package,每个APK对应一个PackageParser.Package,最后在转换生成PackageSetting存储在Settings里面去;过程就是这样,难点在于他如何去解析每一个APK,APK还会分单个APK和集群式APK,分别有不同的解析方法,解析完成后还涉及与旧版本APK对比更新等等!
扫描的路径主要是:
/vendor/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/data/app
/data/app-private
集群式APK其APK文件不止一个,一般由一个base.apk做基础,其他功能模块由不同split.apk组成,如Google提出App bundle技术就是这类型的apk,无论是单个Apk还是集群式Apk最终解析到内存数据模型都是为一个PackageParser.Package对象
认清目标,解析对象是什么?最终的结果是什么?如下图:
源码中,解析流程主要是:
a. PackageManagerService.scanDirTracedLI() -> ParallelPackageParser.submit() -> PackageParser.parsePackage()
b. return PackageParser.Package到PackageManagerService
c. PackageManagerService.scanPackageLI(PackageParser.Package) 并得到PackageSetting
d. Settings保存PackageSetting
重点看一下a步骤里面PackageParser.parsePackage() 方法,方法内部又调用了PackageParser.parsePackage方法:
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
有缓存就取缓存
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (parsed != null) {
return parsed;
}
如果file是一个目录,说明是集群式APP,使用parseClusterPackage去解析
if (packageFile.isDirectory()) {
parsed = parseClusterPackage(packageFile, flags);
} else {
单个App使用parseMonolithicPackage解析
parsed = parseMonolithicPackage(packageFile, flags);
}
缓存结果
cacheResult(packageFile, flags, parsed);
return parsed;
}
这里我们看parseClusterPackage是如何实现的?
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
解析APK精简内容,只解析apk的Androidmanifest中manifest、Application、
use-split和package-verify标签下的属性
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
// Build the split dependency tree.
SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
try {
每个包apk依赖了其他另外的包,建立好他们的映射关系,即用到了use-split标签
splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
}
} else {
assetLoader = new DefaultSplitAssetLoader(lite, flags);
}
try {
//就是当前基础路径packageDir
final File baseApk = new File(lite.baseCodePath);
//解析baseApk中的AndroidManifest所有标签
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
pkg.applicationInfo.splitNames = pkg.splitNames;
pkg.applicationInfo.splitDependencies = splitDependencies;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
//依次解析每个分包apk的AndroidManiesf文件,split apk中主要解析四大组件,其他如权限方面在parseBaseApk中已经解析
//并且base apk和splitapk中组件都是放在Package的成员中,没有特地分开
parseSplitApk(pkg, i, splitAssets, flags);
}
}
pkg.setCodePath(packageDir.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
在看看parseBaseApk方法是什么样子?
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
try {
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
//解析出来的是基础包名和分包包名
pkgName = packageSplit.first;
splitName = packageSplit.second;
//基础包base-apk的分包包名必须为空
if (!TextUtils.isEmpty(splitName)) {
outError[0] = "Expected base APK, but found split " + splitName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
if (mCallback != null) {
//回调到PackageManagerService获取到overlay资源,加入到自己的资源路径,用于替换
//overlay机制见https://blog.csdn.net/azhengye/article/details/49050631
String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
if (overlayPaths != null && overlayPaths.length > 0) {
for (String overlayPath : overlayPaths) {
res.getAssets().addOverlayPath(overlayPath);
}
}
}
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
//解析基础包的manifest标签下的基本属性code和name等
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
一下方法主要是解析AndroidMainfest.xml中的四大组件权限等标签
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
上面mCallback.getOverlayPaths方法,主要是为了实现Android的Overlay机制,实现动态替换资源,具体Overlay简介请点击,最后parseBaseApkCommon就主要是机械AndroidMainfest中相通的一些内容,如四大组件以及权限等
小结:
从源码上来看解析集群式APK主要是解析每个APK的AndroidManifest.xml中的内容,并建立好各个模块APK之间的依赖关系,解析四大组件以及权限等、读取代码位置、安装路径等,最后存放到Package变量中去,所以,最终我们理解透这个Package中的内容即可,它属于PackageParser的内部类,如下
public final static class Package {
//表示包名
public String packageName;
// 表示"拆包"的包名,是个数组,每个元素代表一个"拆分"包名
public String[] splitNames;
//对应一个volume的uid
public String volumeUuid;
// 表示代码的路径,如果是单个包,则表示"base"的APK的路径,如果是"集群"包,则表示的"集群"包的目录。
public String codePath;
// "base APK"的路径
public String baseCodePath;
// "拆分 APK"的路径
public String[] splitCodePaths;
// "base APK"的调整版本号
public int baseRevisionCode;
//"拆分APK"的调整版本号
public int[] splitRevisionCodes;
// "拆分APK"的标志数组
public int[] splitFlags;
// "拆分APK"的私有标志数组
public int[] splitPrivateFlags;
// 是否支持硬件加速
public boolean baseHardwareAccelerated;
// 对应ApplicationInfo对象 对应AndroidManifest里面的Application
public final ApplicationInfo applicationInfo = new ApplicationInfo();
// APK安装包中 AndroidManifest里面的
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
// APK安装包中 AndroidManifest里面的
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
// APK安装包中 AndroidManifest里面的,这里面的Activity是不是我们通常说的Activity,而是PackageParse的内部类Activity
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
// APK安装包中 AndroidManifest里面的,这里面的Activity是不是我们通常说的Activity,而是PackageParse的内部类Activity
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
// APK安装包中 AndroidManifest里面的,这里面的Provider是不是我们通常说的Provider,而是PackageParse的内部类Provider
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
// APK安装包中 AndroidManifest里面的,这里面的Service是不是我们通常说的Service,而是PackageParse的内部类Service
public final ArrayList<Service> services = new ArrayList<Service>(0);
// APK安装包中 AndroidManifest里面的,这里面的Instrumentation是不是我们通常说的Instrumentation,而是PackageParse的内部类Instrumentation
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
// APK安装包中请求的权限
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
// APK安装包中 保内广播的Action
public ArrayList<String> protectedBroadcasts;
// APK安装包中 依赖库的名字
public ArrayList<String> libraryNames = null;
// APK安装包中 使用库的名字
public ArrayList<String> usesLibraries = null;
// APK安装包中 使用选项库的名字
public ArrayList<String> usesOptionalLibraries = null;
// APK安装包中 使用库的路径数组
public String[] usesLibraryFiles = null;
// APK安装包中 某个Activity信息的集合,在AndroidManifest里面的标签(不在receiver里面)
public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
// APK安装包中 AndroidManifest中对应"original-package"的集合
public ArrayList<String> mOriginalPackages = null;
// 是真实包名,通常和mOriginalPackages一起使用
public String mRealPackage = null;
// APK安装包中 AndroidManifest中对应"adopt-permissions"集合
public ArrayList<String> mAdoptPermissions = null;
// 我们独立的存储应用程序元数据,以避免多个不需要的引用
public Bundle mAppMetaData = null;
// 版本号
public int mVersionCode;
// 版本名
public String mVersionName;
// 共享id
public String mSharedUserId;
// 共享用户标签
public int mSharedUserLabel;
// 签名
public Signature[] mSignatures;
// 证书
public Certificate[][] mCertificates;
// dexopt的位置,以便PackageManagerService跟踪要执行dexopt的位置
public int mPreferredOrder = 0;
// 需要进行的dexopt的集合
public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);
// 最后一次使用pakcage的时间的
public long mLastPackageUsageTimeInMills;
// 附加数据 用于指向PackageSetting
public Object mExtras;
// 硬件配置信息,对一个AndroidManifest里面的 标签
public ArrayList<ConfigurationInfo> configPreferences = null;
//特性信息,对一个AndroidManifest里面的 标签
public ArrayList<FeatureInfo> reqFeatures = null;
//特性组信息,对一个AndroidManifest里面的 标签
public ArrayList<FeatureGroupInfo> featureGroups = null;
// 安装的属性
public int installLocation;
// 是否是核心
public boolean coreApp;
// 是否是全局必要,所有用户都需要的应用程序,无法为用户卸载
public boolean mRequiredForAllUsers;
// 受限账户的 验证类型
public String mRestrictedAccountType;
// 账户的类型
public String mRequiredAccountType;
// 对应的AndroidManifest项目摘要清单
public ManifestDigest manifestDigest;
//AndroidManifest中 对应 标签
public String mOverlayTarget;
//overlay对应的优先级
public int mOverlayPriority;
// 是否是受信任的Overlay
public boolean mTrustedOverlay;
// 下面是用来给KeySetManagerService的数据
public ArraySet<PublicKey> mSigningKeys; // 签名
public ArraySet<String> mUpgradeKeySets; //升级
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; //公钥
// 如果有abi的话,abi覆盖
public String cpuAbiOverride;
上诉代码中提到的Activity、Service等并不是我们开发中使用到的,而是PackageParser的内部类,想想也可以理解,这里如Activity需要保存Activity的所有信息,如名字、启动模式、Intent过滤等以及和ApplicationInfo之间的关系,这些都不是一个Activity所能保存的;
在重点看一下cd步骤,如何处理解析后的Package变量? 解析内容太过于复杂,重点参考这篇文章,会先判断apk的签名,是否已经安装等,最后在保存到Settings实例中去,保存到Settings主要是一个PackageSetting对象,主要涉及文章前面提到的mPackages、mSharedUser、mOtherUserIds和mUserIds这三个成员中,调用最终逻辑在:
PackageManagerService.commitPackageSettings() -> Settings.insertPackageSettingLPw() -> Settings.addPackageSettingLPw()将把此次APK的PackageSetting添加到Settings的mPackages里面去,同时根据sharedId描述符添加到对应的mSharedUser里面去,最后根据当前APK的appId大小,将SharedUserSetting替换对应的mOtherUserIds和mUserIds成员中去
这里也是重点看一下PackageSetting是一个什么样的对象:
PackageSetting(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
int[] usesStaticLibrariesVersions) {
super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
usesStaticLibraries, usesStaticLibrariesVersions);
this.sharedUserId = sharedUserId;
}
从它的构造方法可以看出来,主要包含了一个APP的基本信息,如安装位置,lib位置、资源位置、版本信息、包名等
这个步骤和上一个步骤类似,扫描/data/app和/data/app-private目录下的APK
扫描结束,当前系统下所有APK文件已经写入到内存结构模型中去了,主要在Settings的mPackages、mSharedUser、mUserId和mOtherUserId几个成员,现在将要把相关信息写入到文档packages.xml中持久化,所以在PackageManagerService的BOOT_PROGRESS_PMS_SCAN_END步骤会有如下:
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
writeLPr将会写入信息到文档packages.xml
mSettings.writeLPr();
void writeLPr() {
....
try {
mSettingsFilename变量是Settings构造方法创建的package.xml文件
FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
BufferedOutputStream str = new BufferedOutputStream(fstr);
//XmlSerializer serializer = XmlUtils.serializerInstance();
XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(str, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, "packages");
xml序列化所有PackageSetting
for (final PackageSetting pkg : mPackages.values()) {
writePackageLPr(serializer, pkg);
}
for (final PackageSetting pkg : mDisabledSysPackages.values()) {
writeDisabledSysPackageLPr(serializer, pkg);
}
for (final SharedUserSetting usr : mSharedUsers.values()) {
serializer.startTag(null, "shared-user");
serializer.attribute(null, ATTR_NAME, usr.name);
serializer.attribute(null, "userId",
Integer.toString(usr.userId));
usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
writePermissionsLPr(serializer, usr.getPermissionsState()
.getInstallPermissionStates());
serializer.endTag(null, "shared-user");
}
str.flush();
FileUtils.sync(fstr);
str.close();
// New settings successfully written, old ones are no longer
// needed.
mBackupSettingsFilename.delete();
FileUtils.setPermissions(mSettingsFilename.toString(),
FileUtils.S_IRUSR|FileUtils.S_IWUSR
|FileUtils.S_IRGRP|FileUtils.S_IWGRP,
-1, -1);
return;
} catch(XmlPullParserException e) {
Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ "current changes will be lost at reboot", e);
} catch(java.io.IOException e) {
Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ "current changes will be lost at reboot", e);
}
}
以上就把所有apk配置信息写入到文件中去了,后续其他应用需要包信息都可以从这里找到,也可以从Settings的成员变量中找到
创建安装服务PackageInstallerService,接收来自安装程序如adb、packageinstaller等安装请求
第一步:拷贝文件到指定的目录:
在Android系统中,apk安装文件是会被保存起来的,默认情况下,用户安装的apk首先会被拷贝到/data/app目录下,/data/app目录是用户有权限访问的目录,在安装apk的时候会自动选择该目录存放用户安装的文件,而系统出场的apk文件则被放到了/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等等,该分区只有ROOT权限的用户才能访问,这也就是为什么在没有Root手机之前,我们没法删除系统出场的app的原因了。
第二步:解压缩apk,宝贝文件,创建应用的数据目录
为了加快app的启动速度,apk在安装的时候,会首先将app的可执行文件dex拷贝到/data/dalvik-cache目录,缓存起来。然后,在/data/data/目录下创建应用程序的数据目录(以应用的包名命名),存放在应用的相关数据,如数据库、xml文件、cache、二进制的so动态库等。
第三步:解析apk的AndroidManifest.xml文件
Android系统中,也有一个类似注册表的东西,用来记录当前所有安装的应用的基本信息,每次系统安装或者卸载了任何apk文件,
都会更新这个文件。这个文件位于如下目录:/data/system/packages.xml。系统在安装这个apk的过程中,会解析apk的
AndroidManifest.xml文件,提取出这个apk的重要信息写入到packages.xml文件中,这些信息包括:权限、应用包名、
APK的安装位置、版本、userID等等。由此,我们就知道了为什么一些应用市场和软件管理类的app能够很清楚地知道当前手机
所安装的所有app,以及这些app的详细信息了。另外一件事就是Linux的用户Id和用户组Id,以便他们可以获得合适的运行权限。
以上都是由PackageServcieManager完成的,后面我们会重点介绍PackageServiceManager。
第四步:显示快捷方式
如果这些应用程序在PackageManagerService服务注册好了,如果我们想要在Android桌米上看到这些应用程序,还需要有一个Home应用程序,负责从PackageManagerService服务中把这些安装好的应用程序取出来,并以友好的方式在桌面上展现出来,例如以快捷图标的形式。在Android系统中,负责把系统中已经安装的应用程序在桌面中展现出来的Home应用就是Launcher了。
PackageHandler属于PMS的内部类,是一个Handler,主要用于接收binder客户端,如PackageInstaller或adb的安装程序,发起跨进程调用;对于PMS暴露出来的安装接口,如:
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, int userId) {
final Message msg = mHandler.obtainMessage(INIT_COPY);
final VerificationInfo verificationInfo = new VerificationInfo(
null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
null /*packageAbiOverride*/, null /*grantedPermissions*/,
null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj));
发送给PackageHandler
mHandler.sendMessage(msg);
}
PackageHandler属于PMS的子线程,专门处理安装程序!PMS内部的安装流程就不在分析!
本文博客参考很多博主隔壁老李头博客,更多细节可以参考其博客!感谢!