应用端发起安装APK的代码一般如下:
Intent installintent = new Intent();
installintent.setAction(Intent.ACTION_VIEW);
installintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installintent.setDataAndType(xxx,"application/vnd.android.package-archive");
context.startActivity(installintent);
安装apk,本质上是通过系统的应用packageInstaller.apk来完成的。因此,我们需要查看的是packageInstaller的源码。打开 PackageInstaller 的AndroidManifest.xml文件,我们会发现跟上面Intent要启动的Activity匹配的是InstallStart,这也是PackageInstaller应用的入口:
<activity android:name=".InstallStart"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.INSTALL_PACKAGE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="content"/>
<data android:mimeType="application/vnd.android.package-archive"/>
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="package"/>
<data android:scheme="content"/>
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_INSTALL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
InstallStart.java
/frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
跳转InstallStart .java
主要工作:
-
判断是否需要展示一个确认安装的对话框,如果是,则跳转
PackageInstallerActivity
;如果否则跳转3final boolean isSessionInstall =PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
-
判断是否勾选"信任未知来源"选项,若未勾选,则判断版本是否小于Android 8.0,是则取消安装;否则判断版本是否大于Android 8.0且没有设置
REQUEST_INSTALL_PACKAGES
权限,是则取消安装isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
-
判断Uri的Scheme协议,若是
content://
则跳转InstallStaging
, 若是package://
则跳转PackageInstallerActivity
,但是实际上InstallStaging
中的StagingAsyncTask
会将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity
-
finish当前
InstallStart
界面
PackageInstallerActivity.java
主要工作:
-
构建确认安装对话框
protected void onCreate(Bundle icicle) {
...
bindUi();
} -
初始化安装需要用的各种对象,比如 PackageManager、IPackageManager、AppOpsManager、UserManager、PackageInstaller 等等
-
根据传过来的scheme协议(package/file)做不同处理,主要就是获取PackageInfo对象,PackageInfo包含了Android 应用的信息,例如应用名称,权限列表等,通过PackageInfo创建AppSnippet对象,里面包含待安装应用的图标和标题
boolean wasSetUp = processPackageUri(packageUri);
-
使用
checkIfAllowedAndInitiateInstall()
来检查APK来源,展示"未知来源APK安装"的对话框,当点击"settings"按钮后跳转到设置页Intent settingsIntent = new Intent();
settingsIntent.setAction(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
Uri packageUri = Uri.parse("package:" + argument);
settingsIntent.setData(packageUri);
try{
getActivity().startActivityForResult(settingsIntent, REQUEST_TRUST_EXTERNAL_SOURCE);
} catch(ActivityNotFoundException exc){
Log.e(TAG, "Settings activity not found for action: " + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
} -
打开"允许未知来源安装"设置选项后回到
PackageInstallerActivity
,在onActivityResult()
中,调用initiatenstall()
检查应用列表判断该应用是否已安装,若已安装则提示该应用已安装,由用户决定是否替换,然后显示确认安装界面 -
在安装界面,点击 OK 按钮确认安装后,会调用
startInstall
开始安装工作 -
startInstall
方法用来跳转到InstallInstalling
,并关闭掉当前的PackageInstallerActivity
InstallInstalling.java
主要工作:
-
获取
PackageManager
,然后获取其中的PackageInstaller
对象 -
在
PackageInstaller
中创建用于传输APK安装包以及安装信息的Session
,并返回SessionId
-
创建异步任务
InstallingAsyncTask
,并在异步任务中根据之前返回的SessionId
打开PackageInstaller
端Session
-
通过
Session
将APK安装包以及相关信息传递 -
在异步任务中
onPostExecute
方法中执行session.commit(pendingIntent.getIntentSender())
-
通过注册观察者
InstallEventReceiver
监听安装成功和失败的回调,跳转到对应结果页面<receiver android:name=".InstallEventReceiver" android:permission="android.permission.INSTALL_PACKAGES" android:exported="true"> <intent-filter android:priority="1"> <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" /> </intent-filter> </receiver>
PackageInstallerSession
session.commit()
方法通过binder跨进程调到了 PackageInstallerSession
服务中。
通过aidl文件定义的接口为:
/frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl
IPackageInstallerSession
的实现类为:
/frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
//PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
dispatchStreamValidateAndCommit();
}
private void dispatchStreamValidateAndCommit() {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
主要工作
-
发送了
MSG_STREAM_VALIDATE_AND_COMMIT
消息到mHandler
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_STREAM_VALIDATE_AND_COMMIT:
handleStreamValidateAndCommit(); //
break;
case MSG_INSTALL:
handleInstall(); //
break;
...
}
return true;
}
}; -
最后执行在
handleStreamValidateAndCommit()
中,然后里面又发送了消息MSG_INSTALL
,这个执行在handleInstall()
://PackageInstallerSession.java
public void handleStreamValidateAndCommit() {
...
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
rivate final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_STREAM_VALIDATE_AND_COMMIT:
handleStreamValidateAndCommit(); //
break;
case MSG_INSTALL:
handleInstall(); //
break;
...
}
return true;
}
};private void handleInstall() { ... List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); installNonStagedLocked(childSessions); ... } private void installNonStagedLocked(List<PackageInstallerSession> childSessions) { final PackageManagerService.ActiveInstallSession installingSession = makeSessionActiveLocked(); ... //安装走到了 PMS 中 mPm.installStage(installingSession); ... }
-
最后,安装过程走到了
PMS
的installStage()
。
目前为止,只是通过 PackageInstaller
维持了安装 Session
,把安装包写入到 Session
中,真正的安装过程是 PMS
来执行。
PackageManagerService.java
主要工作
-
installStage()
创建InstallParams
对象,传入message
消息;发送了INIT_COPY
消息到mHandler
。//PMS
void installStage(ActiveInstallSession activeInstallSession) {
...
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(activeInstallSession);
msg.obj = params;
mHandler.sendMessage(msg);
}
mHandler
是 PackageHandler
,这是在 PackageManagerService
构造方法中创建的。
mHandler
处理INIT_COPY
是 InstallParams.startCopy()
:
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
// 得到安装参数 HandlerParams
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
// 2 调用 startCopy() 方法
params.startCopy();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
break;
}
...
}
}
里面调用了 HandlerParams
的 startCopy()
方法。HandlerParams
是 PMS
的内部抽象类。PMS
的内部类 InstallParams
、MultiPackageInstallParams
是其实现类。
//HandlerParams.java
final void startCopy() {
handleStartCopy();
handleReturnCode();
}
handleStartCopy()、handleReturnCode()
都是是抽象的方法。因此,这里的对象是哪一个呢? 经过之前的分析我们知道是 InstallParams
。
-
handleStartCopy()扫描了apk的轻量信息、安装空间、apk完整性的校验等。创建 FileInstallArgs 对象,赋值返回code。
-
handleReturnCode()先是调用了 InstallArgs 的 copyApk()方法执行APK拷贝,然后执行安装APK;
void handleReturnCode() {
if (mVerificationCompleted && mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
...
//如果前面校验ok,这里执行apk拷贝
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = mArgs.copyApk();
}
//执行安装
processPendingInstall(mArgs, mRet);
}
}
}
FileInstallArgs
继承了 InstallArgs
类。 copyApk()
内部调用了 doCopyApk()
;
doCopyApk() 先获取了拷贝文件路径:/data/app,使用PackageManagerServiceUtils.copyPackage()进行APK拷贝,接着是 .so文件的拷贝。也就是说,把发送到 Session暂存目录 data/app-staging 的APK 拷贝到了 /data/app。
从/data/app-stagging
临时目录把apk拷贝到/data/app
目录。
//拷贝apk
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
接着看执行安装processPendingInstall(mArgs, mRet)
-
processPendingInstall()
调用了processInstallRequestsAsync()
,processInstallRequestsAsync()
进行安装前校验,开始安装APK,发送通知安装结果的广播private void processInstallRequestsAsync(boolean success, List<InstallRequest>installRequests) {
mHandler.post(() -> {
if (success) {
for (InstallRequest request : installRequests) {
//安装前检验:returnCode不为INSTALL_SUCCEEDED就移除拷贝的apk等
request.args.doPreInstall(request.installResult.returnCode);
}
synchronized (mInstallLock) {
//安装:解析apk
installPackagesTracedLI(installRequests);
}
for (InstallRequest request : installRequests) {
//同安装前检验
request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);
}
}
for (InstallRequest request : installRequests) {
//安装后续:备份、可能的回滚、发送安装完成先关广播
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, new PostInstallData(request.args, request.installResult, null));
}
});
}
这里我们先重点看安装过程,installPackagesTracedLI()
又走到 installPackagesLI()
:
安装过程分为四个阶段:
- 准备 ,分析当前安装状态,解析包 并初始校验:在 preparePackageLI() 内使用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息;使用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包最终路径 等。
prepareResult = preparePackageLI(request.args, request.installResult);- 扫描 ,根据准备阶段解析的包信息上下文 进一步解析:确认包名真实;根据解析出的信息校验包有效性(是否有签名信息等);搜集apk信息------PackageSetting、apk的静态库/动态库信息等。
final List<ScanResult> scanResults = scanPackageTracedLI( prepareResult.packageToScan, prepareResult.parseFlags, prepareResult.scanFlags, System.currentTimeMillis(), request.args.user);
- 核对 ,验证扫描后的包信息,确保安装成功:主要就是覆盖安装的签名匹配验证。
reconciledPackages = reconcilePackagesLocked( reconcileRequest, mSettings.mKeySetManagerService);
4. 提交 ,提交扫描的包、更新系统状态:添加 PackageSetting 到 PMS 的 mSettings、添加 AndroidPackage 到 PMS 的 mPackages 、添加 秘钥集 到系统、应用的权限添加到 mPermissionManager、四大组件信息添加到 mComponentResolver 。这是唯一可以修改系统状态的地方,并且要对所有可预测的错误进行检测。
commitRequest = new CommitRequest(reconciledPackages, mUserManager.getUserIds()); commitPackagesLocked(commitRequest);
安装完成后,调用了 executePostCommitSteps()
准备app数据、执行dex优化:
prepareAppDataAfterInstallLIF()
:提供目录结构/data/user/用户ID/包名/cache
(/data/user/用户ID/包名/code_cache)mPackageDexOptimizer.performDexOpt()
:dexopt 是对 dex 文件 进行 verification 和 optimization 的操作,其对 dex 文件的优化结果变成了 odex 文件,这个文件和 dex 文件很像,只是使用了一些优化操作码(譬如优化调用虚拟指令等)。
这两个操作最终都是使用 Installer 对应的方法来操作。前面介绍 PMS 创建时传入了 Installer
的实例,而 Installer
继承自 SystemService
也是一个系统服务。这里来看下:
- 在 Installer的 onStart()方法中 通过 installd 的Binder对象获取了 mInstalld 实例。可见这里是IPC操作,即 System_Server 进程中的 Installer IPC 到 具有root权限的守护进程。像目录 /data/user 的创建 必须要有root权限。
- PMS中使用了Installer的很多方法,Installer是Java层提供的Java API接口,Installd 则是在init进程启动的具有root权限的Daemon进程。
到这里安装完成,再回到 PMS 的 processInstallRequestsAsync()
,最后调用restoreAndPostInstall()
进行 备份、可能的回滚、发送安装完成先关广播:
private void restoreAndPostInstall(
int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
...
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
...
}
mHandler
还是 PackageHandler
对象。
mHandler
使用 PMS的 handlePackagePostInstall()
方法处理 POST_INSTALL
:
- 根据安装结果 发送
Intent.ACTION_PACKAGE_ADDED
等广播,桌面Launcher 收到广播后就会在桌上增加App的Icon - 调用
PackageInstallSession
中保存的IPackageInstallObserver2
实例的onPackageInstalled
()方法,最后发送安装成功的通知显示在通知栏,通过IntentSender
发送 在InstallInstalling
中就定义好的广播,最后InstallInstalling
页面 根据结果展示 安装成功/安装失败 。
到这里,APK的安装过程 就梳理完毕了,来回顾下:
- APK用写入Session且包信息和APK安装操作 都提交到了PMS;
- PMS中先把APK拷贝到 /data/app,然后使用PackageParser2解析APK 获取 四大组件、搜集签名、PackageSetting等信息,并进行校验确保安装成功;
- 接着提交信息包更新系统状态及PMS的内存数据;
- 然后使用 Installer 准备用户目录/data/user、进行 dexOpt;
- 最后发送安装结果通知UI层。