2.4 APP执行代码
APP运行时可执行的代码,主要有三部分:
1) 虚拟机初始化时加载的系统jar包,主要包含framework.jar和libcore.jar,分别对应android framework代码和jdk代码
2) APP自身程序代码,也就是打包入APK的dex文件
3) APP程序运行需要额外加载的library,对应manifest里配置的uses-library字段
前两个没啥好讲的,重点讲下第三部分,app可以在manifest里头声明自身运行需要额外的library,但是,这里仅仅只是声明,最终还是要看系统有没有配置这个sharelibrary,配置的方式可查看2.2 初始化SystemConfig,如果app的uses-library在系统中不存在,apk会安装失败。
PMS初始化share library代码:
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries(); for (int i=0; i<libConfig.size(); i++) { mSharedLibraries.put(libConfig.keyAt(i), new SharedLibraryEntry(libConfig.valueAt(i), null)); } |
通过library path来初始化SharedLibraryEntry并保存到mSharedLibraries中。
在apk数据解析时,会调用updateSharedLibrariesLPw来将apk配置的user library更新到对应的package对象:
private void updateSharedLibrariesLPw(PackageParser.Package pkg, PackageParser.Package changingLib) throws PackageManagerException { if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) { final ArraySet<String> usesLibraryFiles = new ArraySet<>(); int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0; for (int i=0; i<N; i++) { final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i)); if (file == null) { throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + pkg.packageName + " requires unavailable shared library " + pkg.usesLibraries.get(i) + "; failing!"); } addSharedLibraryLPw(usesLibraryFiles, file, changingLib); } N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0; for (int i=0; i<N; i++) { final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i)); if (file == null) { Slog.w(TAG, "Package " + pkg.packageName + " desires unavailable shared library " + pkg.usesOptionalLibraries.get(i) + "; ignoring!"); } else { addSharedLibraryLPw(usesLibraryFiles, file, changingLib); } } N = usesLibraryFiles.size(); if (N > 0) { pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]); } else { pkg.usesLibraryFiles = null; } } } |
遍历usesLibraries和usesOptionalLibraries,调用addSharedLibraryLPw依次将share library添加到usesLibraryFiles,并最终保存到pkg.usesLibraryFiles。
private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file, PackageParser.Package changingLib) { if (file.path != null) { usesLibraryFiles.add(file.path); return; } PackageParser.Package p = mPackages.get(file.apk); if (changingLib != null && changingLib.packageName.equals(file.apk)) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); } } |
file.path不为空,直接添加到usesLibraryFiles即可
程序启动的时候,AcivityThread会通过app对应的application info来创建LoadedApk,然后LoadedApk内部根据dex和usesLibraryFiles来创建app对应的classloader。
2.5 APK文件扫描
APK扫描的过程,其实就是数据收集的过程,扫描的一些代码,其实在上头的内容中就有提到, PMS中调用scanPackageLI扫描一个apk文件,为了让整个思路更加清晰,接下去将基于首次扫描一个nonsystem apk来分析,至于其他的一些细节,这里就不做更多的描述,大家可自行阅读源码。
先介绍几个核心类:
从下到上,依次包含,详细介绍如下
1:PackageParser.Package 对应一个apk包完整的原始数据
2:PackageSetting 包含PackageParser.Package对象实例,说明它也对应一个apk包的数据,不同的是,它还包含apk相关配置数据,比如,apk内部哪些component是被disable等。
3:Settings 包含PackageSetting对象列表,也就是说它包含了系统所有apk数据
还有就是PackageParser,顾名思义,负责APK数据解析。
扫描新apk的流程描述:
1) 调用scanPackageLI,传入要解析的apk文件
2) 创建PackageParser对象,接着调用parsePackage来解析apk清单数据,返回
PackageParser.Package对象pkg,pkg包含了apk原始的清单数据
3) 接着调用collectCertificatesLI获取apk的签名数据并保存到pkg中,这个函数的详细介绍可看1.2.4 PMS中签名相关代码介绍
4) 调用scanPackageLI并传入pkg,接着调用scanPackageDirtyLI
5) 接着判断pkg.mSharedUserId是否为空,如果不为空,说明apk设置了share user id,那就要调用getSharedUserLPw创建新的share user id
6) 接着调用mSettings.getPackageLPw生成pkg对应的PackageSetting对象pkgSetting
7) 针对非系统app(parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR)== 0),调用updateSharedLibrariesLPw更新pkg的usesLibraryFiles
8) 如果是新安装app((scanFlags &SCAN_NEW_INSTALL) != 0), 则查看app内部声明的provider是否已经被其他已安装的app定义,如果是,抛出异常
9) 调用fixProcessName生成app对应的进程名
10)调用createDataDirsLI在/data/data/下创建app私有目录
11)调用NativeLibraryHelper.copyNativeBinariesForSupportedAbi拷贝so文件,接着调用setNativeLibraryPaths更新pkg.applicationinfo中的nativeLibraryDir等信息
12)调用performDexOptLI对apk中的dex做优化
13)调用mSettings.insertPackageSettingLPw(pkgSetting, pkg);将app对应的PackageSetting添加到Settings中
14)调用mPackages.put(pkg.applicationInfo.packageName, pkg);将app对应的Package添加到mPackages中
15)将app中定义的providers添加到mProvidersByAuthority,services添加到mServices,
receivers添加到mReceivers,activities添加到mActivities
16)将app中定义的permissionGroups添加到mPermissionGroups,permissions添加到permissionMap,instrumentation添加到mInstrumentation
很多细节代码在前面章节都有过描述,这里就不再一一展开。
2.5 APK安装
先来了解一个服务:
DefaultContainerService
PMS安装过程中APK文件的拷贝,都要通过它来完成,所以在安装时,需要连接到该服务
以获取对应的BinderProxy来远程调用其功能。
接着介绍几个类:
1) PackageHandler
对应变量mHandler,在PMS构造时会创建并绑定到HandlerThread,APK的安装相关代码会通过Message执行在这个工作线程。该Handler的主要Message ID如下:
ID | 描述 |
INIT_COPY | 将要安装APK信息放置于msg.obj并发送该Message到Handler,Handler执行后,会做两件事情: 1:判断当前是否已经连接到DefaultContainerService,如果未连接,调用connectToService进行连接 2:将要安装的APK信息添加到mPendingInstalls 3:如果mPendingInstalls长度为0,触发 MCS_BOUND消息 |
MCS_BOUND | 依次从mPendingInstalls取出HandlerParams来进行安装 |
POST_INSTALL | 安装成功后,会收到该消息来执行相关操作 |
2) HandlerParams
抽象类,如果要将安装操作通过INIT_COPY消息添加到PackageHandler,必须实现
HandlerParams,并将对应的实例放置于msg.obj中,MCS_BOUND命令在处理时,会将msg.obj转换成HandlerParams,接着调用其函数startCopy来触发拷贝,拷贝过程中,会回调如下状态:
abstract void handleStartCopy() throws RemoteException; abstract void handleServiceError(); abstract void handleReturnCode(); |
APK拷贝对应的类为:
class InstallParams extends HandlerParams { … } |
3) InstallArgs
也是一个抽象类,对应的实现类有两个, FileInstallArgs和AsecInstallArgs,分别对应APK安装到系统存储和SD卡,他两在实际APK数据解析上,没任何区别,唯一不同的就是拷贝目录和文件目录操作存在差异,为了简单起见,后续我们只基于FileInstallArgs来分析。
HanderParams是为了给PackageHandler传递参数并触发拷贝和安装行为的,所以,它的接口更多的为了配合PackageHandler来设计的
FileInstallArgs则是拷贝的执行者,同时还处理安装前和安装后的一些逻辑处理等,它的设计,是跟PMS深度结合在一起的。
2.5.1安装接口描述
程序可以通过调用PMS. installPackage并传入APK文件路径,IPackageInstallObserver2对应的binder proxy, install Flags等来启动安装,IPackageInstallObserver2是用于给调用方反馈安装状态,install Flag指定安装命令,比如是否是替换安装等。
installPackage什么都没做,直接转到installPackageAsUser:
@Override public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { try { if (observer != null) { observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null); } } catch (RemoteException re) { } return; }
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { installFlags |= PackageManager.INSTALL_FROM_ADB;
} else { // Caller holds INSTALL_PACKAGES permission, so we're less strict // about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB; installFlags &= ~PackageManager.INSTALL_ALL_USERS; }
UserHandle user; if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(userId); }
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath); final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, observer, installFlags, installerPackageName, verificationParams, user, packageAbiOverride); mHandler.sendMessage(msg); } |
函数开头先检查调用程序是否有INSTALL_PACKAGES权限,如果没有,直接抛出异常。
接着通过调用程度的user id来判断其是否被限制安装APK功能,如果是,直接返回
通过UID来判断是否是通过ADB来安装,接着基于传入的参数创建InstallParams实例并保存到INIT_COPY msg.obj中并发送到PackageHandler.
2.5.2完整的安装流程
安装从拷贝开始,拷贝由发送INIT_COPY到PackageHandler触发,完整流程图:
2.5.3安装包解析
从上面的流程可以看出,APK数据的解析,是通过installPackageLI来完成的,同样的,下面只基于新安装包这一情形来分析
看installPackageLI的代码后会发现,其实她实现的内容跟scanPackageL(File f…)基本上是一致的:
1) 创建PackageParser对象,接着调用parsePackage来解析apk清单数据,返回
PackageParser.Package对象pkg,pkg包含了apk原始的清单数据
2) 调用collectCertificates获取安装APK的签名数据
3) 调用installNewPackageLI并传入pkg
4) 调用scanPackageLI传入pkg进行apk数据扫描
5) 调用updateSettingsLI将apk数据更新到Settings
6) updatePermissionsLPw同步系统权限数据