所谓包,其实是一种文件格式,比如apk包、jar包等。包管理者一个职能就是识别不同的包,维护这些包的信息。当有一个新的包进入或离开Android世界,都需要向包管理者申报一下,其他管理部分要获取包的具体信息,也都需要向包管理者申请。包管理机制的核心是PackageManagerService(下文称PMS),它负责对包进行管理。
- 开机过程中安装:系统在每次开机会安装系统应用
- adb工具安装:adb命令安装apk
- 手动安装:平时下载的apk,通过系统安装器PackageInstaller(它是系统内置的应用程序,用于安装和卸载应用程序)来安装apk,是有安装界面的
- 商店应用安装:商店上安装app,没有安装界面
它是安卓的一个系统app,用来安装和卸载app的。
我们主要来分析一下手动安装apk的情况,系统会打开一个安装界面,我们可以继续安装和取消,应该大家都比较熟悉。这个界面就是PackageInstaller这个app中的PackageInstallerActivity。
当我们点击安装的时候,会跳转到另一个界面InstallInstalling,最终会将需要安装的apk信息通过PackageInstallerSession传给PMS。
//PackageInstallerSession.java
private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
makeSessionActiveLocked();
if (installingSession == null) {
return;
}
...
//这里的mPm是PMS
mPm.installStage(installingSession);
}
从这里开始,就来到了PMS,installStage()方法就是正式安装apk的过程了。整个apk的安装过程大致分为2步:
- 拷贝安装包
- 装载代码
直接杀到PMS的installStage方法,看看流程是什么样的
//PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
...
//注释1 Message的what是INIT_COPY
final Message msg = mHandler.obtainMessage(INIT_COPY);
//注释2 初始化一个InstallParams,并传入安装包的相关数据
final InstallParams params = new InstallParams(activeInstallSession);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
//注释3 Message的obj是InstallParams
msg.obj = params;
...
mHandler.sendMessage(msg);
}
注意到,上面的代码只不过是发了个消息,我们看看是怎么处理这条消息的
//PackageManagerService.java
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
//上面说了Message的obj是InstallParams
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
params.startCopy();
}
break;
}
...
}
}
把Message的obj取出来,实际上就是之前传入的InstallParams,直接去InstallParams的startCopy()方法
//PackageManagerService.java
private abstract class HandlerParams {
final void startCopy() {
handleStartCopy();
handleReturnCode();
}
abstract void handleStartCopy();
abstract void handleReturnCode();
}
原来InstallParams自己并没有实现startCopy方法,而是由父类HandlerParams实现的,里面只有2个抽象方法,这2个抽象方法都是子类去实现的。这里其实用到了一种设计模式,叫模板方法。下面我们继续看handleStartCopy()和handleReturnCode()的子类实现,先看handleStartCopy()。
//PackageManagerService.java
class InstallParams extends HandlerParams {
public void handleStartCopy() {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
//决定安装位置是内部存储空间还是sdcard中,从这里看来Android 11是只允许安装在内部存储空间里面了
//老的源码中还有一个PackageManager.INSTALL_EXTERNAL表示是安装在sdcard中,现在这个常量已经没了。
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
...
//创建安装参数对象 InstallArgs
final InstallArgs args = createInstallArgs(this);
mVerificationCompleted = true;
mIntegrityVerificationCompleted = true;
mEnableRollbackCompleted = true;
mArgs = args;
...
mRet = ret;
}
}
在handleStartCopy()方法中,我们只需要知道它创建了安装参数对象InstallArgs即可。然后在handleReturnCode()方法中会用到它
//PackageManagerService.java
class InstallParams extends HandlerParams {
@Override
void handleReturnCode() {
if (mVerificationCompleted
&& mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
String packageName = "";
ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
new ParseTypeImpl(
(changeId, packageName1, targetSdkVersion) -> {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.packageName = packageName1;
appInfo.targetSdkVersion = targetSdkVersion;
return mPackageParserCallback.isChangeEnabled(changeId,
appInfo);
}).reset(),
origin.file, 0);
if (result.isError()) {
Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(),
result.getException());
} else {
packageName = result.getResult().packageName;
}
try {
observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
return;
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
//调用InstallArgs的copyApk()方法,复制apk文件,具体实现在子类FileInstallArgs
mRet = mArgs.copyApk();
}
//安装流程
processPendingInstall(mArgs, mRet);
}
}
}
利用之前创建好的InstallArgs对象,实际是其子类FileInstallArgs类型,然后调用其copyApk方法进行安装包的拷贝操作。
//PackageManagerService.java
class FileInstallArgs extends InstallArgs {
// Example topology:
// /data/app/com.example/base.apk
// /data/app/com.example/split_foo.apk
// /data/app/com.example/lib/arm/libfoo.so
// /data/app/com.example/lib/arm64/libfoo.so
// /data/app/com.example/dalvik/arm/base.apk@classes.dex
int copyApk() {
return doCopyApk();
}
private int doCopyApk() {
...
//创建存储安装包的目标路径,实际上是/data/app/应用包名目录
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
//将安装包apk拷贝到目标路径中
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
...
//将apk中的动态库.so文件也拷贝到目标路径中
handle = NativeLibraryHelper.Handle.create(codeFile);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride, isIncremental);
return ret;
}
}
在FileInstallArgs的copyApk方法中调用doCopyApk方法,主要干的事情就是3个:首先创建安装包的目标路径,方便待会儿复制到这里来,它的实际路径是/data/app/com.example
;其次将安装包apk拷贝到目标路径中;最后是将apk中的动态库.so文件也拷贝到目标路径中。其实现都是一些IO的操作,就不继续往下看了。
安装包在这一步骤完成之后,就存在于/data/app/com.example/base.apk
中了,到这里安装包的拷贝工作是完成了。
在上面InstallParams#handleReturnCode()中,调用processPendingInstall方法处理安装:
//PackageManagerService.java
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
if (args.mMultiPackageInstallParams != null) {
args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
} else {
PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
//异步处理安装过程
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
}
// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mHandler.post(() -> {
if (success) {
//预安装,检查包状态,确保环境是ok的,如果不ok,那么会清理拷贝的文件
for (InstallRequest request : installRequests) {
request.args.doPreInstall(request.installResult.returnCode);
}
//安装,调用installPackageTracedLI进行安装
synchronized (mInstallLock) {
installPackagesTracedLI(installRequests);
}
//安装收尾工作
for (InstallRequest request : installRequests) {
request.args.doPostInstall(
request.installResult.returnCode, request.installResult.uid);
}
}
//内部会生成一个POST_INSTALL消息给PackageHanlder
for (InstallRequest request : installRequests) {
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
new PostInstallData(request.args, request.installResult, null));
}
});
}
安装过程是放子线程里面进行的,这里的mHandler用的不是主线程的Looper,而是一个HandlerThread里面的Looper。大致处理流程如下:
- 预安装:检查当前安装包的状态,确保安装环境正常,如果安装环境有问题会清理拷贝文件
- 安装:真正的安装阶段在installPackagesTracedLI方法中,在里面添加跟踪Trace,最后是调用的installPackageLI进行安装
- 安装收尾工作:检查状态,如果安装不成功,删除掉相关目录文件
- 通知操作:发送
POST_INSTALL
消息,然后通过广播、回调接口等方式通知系统中的其他组件,有新的Package安装或发生了改变
所以说installPackagesTracedLI是安装apk的核心代码:
//PackageManagerService.java
private void installPackagesTracedLI(List<InstallRequest> requests) {
...
installPackagesLI(requests);
}
private void installPackagesLI(List<InstallRequest> requests) {
...
//注释1 Prepare 准备:分析任何当前安装状态,分析包并对其进行初始验证.检查SDK版本、静态库等;检查签名;设置权限;
prepareResult = preparePackageLI(request.args, request.installResult);
...
//注释2 Scan 扫描:prepare中收集的上下文,询问已分析的包。
final List<ScanResult> scanResults = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
...
//注释3 Reconcile 调和:在彼此的上下文和当前系统状态中验证扫描的包,以确保安装成功。
ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
prepareResults,
mSharedLibraries,
Collections.unmodifiableMap(mPackages), versionInfos,
lastStaticSharedLibSettings);
...
//注释4 Commit 提交:提交所有扫描的包并更新系统状态。这是安装流中唯一可以修改系统状态的地方,必须在此阶段之前确定所有可预测的错误。
commitPackagesLocked(commitRequest);
...
//注释5 完成APK的安装
executePostCommitSteps(commitRequest);
}
installPackagesLI()方法内部非常复杂,上面简单调了一些主流程拿出来。
- Prepare准备:分析任何当前安装状态,分析包并对其进行初始验证。
- 在这一阶段首先是将apk文件解析出来,解析它的AndroidManifest.xml文件,将结果记录起来。我们平时在清单文件中声明的Activity等组件就是在这一步被记录到Framework中的,后续才能通过startActivity等方式启动来
- 然后是对签名信息进行验证
- 设置相关权限
- 生成安装包abi
- dex优化,实际为dex2oat操作,将apk中的dex文件转换为oat文件
- Scan扫描:考虑到prepare中收集的上下文,询问已分析的包
- Reconcile调和:在彼此的上下文和当前系统状态中验证扫描的包,以确保安装成功
- Commit提交:提交所有扫描的包并更新系统状态。这是安装流中唯一可以修改系统状态的地方,必须在此阶段之前确定所有可预测的错误
- 完成APK的安装,再次执行dex优化(如有必要),然后将apk的安装操作交给installed进程进行apk的安装
首先将apk的信息交给PMS进行处理,然后拷贝apk,然后装载代码,最后交给installed进程来完成安装。