Apk的安装过程探究
有了APK的构建过程和APK的组成结构两篇铺垫,以及PackageManagerService基础。终于到了最后阶段:Apk的安装过程。这块内容比较多,抽空断断续续的看了一个星期摸清楚。通常Apk的安装方式有三种:
①手机上点击Apk文件,打开安装界面进行安装
②通过代码的方式静默安装,比如手机自带的应用商店安装应用。
③开发者使用adb命令安装apk文件
挨个探究以上三个方式,每种方式Apk的安装流程实现。本文所有流程图使用迅捷流程图完成,在线查看地址:Apk安装流程图。
1、PackageInstallerActivity界面安装Apk
先从第一种Apk安装方式讲起,这是用户经常使用的Apk安装方式。点击下载的Apk文件,会启动系统安装应用界面,执行Apk安装过程。
启动安装界面的常规代码如下:
File apkFile = new File(apkPath);
//构造Apk安装Intent, Intent.ACTION_INSTALL_PACKAGE已经废弃
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/**Android 7.0以上使用FileProvider,
* Android禁止将file://Uri暴露给其它应用,
* 否则会抛出FileUriExposedException异常
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 7.0+以上版本:content://Uri
Uri apkUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", apkFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
// file://Uri
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
}
//启动系统安装界面
context.startActivity(intent);1.1、启动安装界面
打开的安装界面是哪个应用的呢?很简单,用下面的命令就可以查看当前手机的应用包名和启动的Activity:
adb shell dumpsys window | findStr mCurrentFocus
当前手机屏幕显示的应用是com.android.packageinstaller,Activity界面是com.android.packageinstaller.PackageInstallerActivity。
mCurrentFocus=null
mCurrentFocus=Window{331c409 u0 com.android.packageinstaller/com.android.packageinstaller.PackageInstallerActivity}紧接着用后面两个命令将系统应用的程序包导出来,反编译Apk也是用这种方式将三方Apk文件导出,详见APK反编译。
adb shell pm path com.android.packageinstaller adb pull system/priv-app/PackageInstaller/PackageInstaller.apk
查看APK中的AndroidManifest文件,可以找到能够处理"application/vnd.android.package-archive"的intent-filter对应的Activity是InstallStart,没错这就是安装界面的入口。
<activity android:name=".InstallStart"> <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> ... </activity>
已经从AOSP中拿出packageinstaller相关的源码,单独上传到GitHub/packageinstaller方便查看。不同的手机厂商会基于AOSP开源的代码,定制各自ROM的应用安装界面。大体流程如下:
从InstallStart的onCreate()方法开始,启动PackageInstallerActivity后关闭自身。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Intent nextActivity = new Intent(intent);
nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
// 作为nextActivity的安装源认为此活动是源,因此显式设置原始UID和sourceInfo
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (isSessionInstall) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
if (packageUri != null && packageUri.getScheme().equals(
ContentResolver.SCHEME_CONTENT)) {
// [重要说明]此条路径已经废弃,但仍然可以使用。 仅可以添加必要的功能。
// 复制文件以防止在此过程中更改文件
nextActivity.setClass(this, InstallStaging.class);
} else if (packageUri != null && packageUri.getScheme().equals(
PackageInstallerActivity.SCHEME_PACKAGE)) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
...
nextActivity = null;
}
}
if (nextActivity != null) {
startActivity(nextActivity);
}
finish();
}PackageInstallerActivity就是安装Apk确认界面。举个例子,长这样:

onCreate()时通过bindUi()方法初始化界面。
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall();
}
}
}, null);
...
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);点击安装按钮mOk开始安装,执行startInstall()方法,启动InstallInstalling正在安装界面并关闭自身。
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.setClass(this, InstallInstalling.class);
...
startActivity(newIntent);
finish();
}进入InstallInstalling,在onCreate()时先创建安装会话Session
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
...
}
}随后在onResume()方法中,开启安装任务。
@Override
protected void onResume() {
super.onResume();
...
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
...
}Apk安装任务被封装在内部类InstallingAsyncTask中,继承自AsyncTask。
/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
...
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
session.setStagingProgress(0);
...
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
...
session.fsync(out);
}
}
}
return session;
}
protected void onPostExecute(PackageInstaller.Session session) {
...
session.commit(pendingIntent.getIntentSender());
...
}
}会后面的安装结果显示不同的界面,安装成功就显示com.android.packageinstaller.InstallSuccess,否则就实现com.android.packageinstaller.InstallFailed。
1.2、安装会话(Session)
最终通过Session将APK提交给PMS处理。
从PackageInstaller.Session的commit(IntentSender statusReceiver)方法继续往下看:
public void commit(@NonNull IntentSender statusReceiver) {
mSession.commit(statusReceiver, false);
}成员mSession为IPackageInstallerSession远程会话接口,PackageInstallerSession类实现了IPackageInstallerSession接口。先不管mSession是如何创建的,2.1节会讲到。
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}向Handler发送消息处理MSG_COMMIT类型的消息,内部Handler构造时使用了自定义的Handler.Callback,只会执行Callback的handleMessage(Message msg)方法,关于Handler.Callback详见Handler中Callback的作用。
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_COMMIT:
handleCommit();
break;
...
}
return true;
}
};通过Handler中执行handleCommit()方法:
private void handleCommit() {
...
try {
synchronized (mLock) {
commitNonStagedLocked(childSessions);
}
}
...
}内部再次调用commitNonStagedLocked(List<PackageInstallerSession> )方法,最终将任务交给mPm执行,也就是PMS。
@GuardedBy("mLock")
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions){
...
if (isMultiPackage()) {
...
mPm.installStage(activeChildSessions);
} else {
mPm.installStage(committingSession);
}
}
1.3、PMS执行安装
以下面执行的installStage(ActiveInstallSession )方法为例,了解PMS处理Apk安装的流程。
进入PMS的installStage(ActiveInstallSession )方法开始执行:
void installStage(ActiveInstallSession activeInstallSession) {
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(activeInstallSession);
msg.obj = params;
mHandler.sendMessage(msg);
}PackageManagerService类内部定义了PackageHandler用来处理各种Apk的处理请求。
class PackageHandler extends Handler {
PackageHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
doHandleMessage(msg);
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
params.startCopy();
}
break;
}
...
}
}
}执行关键代码params.startCopy(),这里的params是从Message携带过来的,刚刚在installStage(ActiveInstallSession )方法中创建的InstallParams实例。
final void startCopy() {
handleStartCopy();
handleReturnCode();
}
abstract void handleStartCopy();
abstract void handleReturnCode();InstallParams继承自内部抽象类HandlerParams,实现了handleStartCopy()和handleReturnCode()两个抽象方法,完成Apk的拷贝,解析等操作。代码比较复杂,流程图进行了简化。
2、PackageInstaller安装Apk流程
第二种方式就是代码的方式安装,这种方式现在仅限于系统应用,普通应用不能再进行任何后台静默安装操作。比如手机应用商店,下载应用后台静默安装,大都使用这种方式。
//Apk文件
File apkFile = new File(apkPath);
//获取PackageManager和PackageInstaller
PackageManager pm = getPackageManager();
PackageInstaller pi = pm.getPackageInstaller();
//创建SessionParams
PackageInstaller.SessionParams sp = new PackageInstaller
.SessionParams(MODE_FULL_INSTALL);
try {
//开启安装会话
int sessionID = pi.createSession(sp);
PackageInstaller.Session se = pi.openSession(sessionID);
//文件输入流
InputStream in;
in = new FileInputStream("apk_path");
//输出流
OutputStream out;
out = se.openWrite("apk_install_session", 0, apkFile.length());
int total = 0;
byte[] buffer = new byte[65536];
int len;
while ((len = in.read(buffer)) != -1) {
total += len;
out.write(buffer, 0, len);
}
se.fsync(out);
in.close();
out.close();
//创建PendingIntent
PendingIntent broadCastTest = PendingIntent.getBroadcast(
this,
sessionID,
new Intent("ACTION_INSTALL_COMPLETE"),
PendingIntent.FLAG_UPDATE_CURRENT);
//提交之前必须关闭所有流
se.commit(broadCastTest.getIntentSender());
se.close();
} catch (Exception ignore) {
}整体流程图简化如下:
2.1、开启会话
通过PackageManager获取PackageInstaller实例,实际实现方法在ApplicationPackageManager类中。
@Override
public PackageInstaller getPackageInstaller() {
if (mInstaller == null) {
mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
mContext.getPackageName(), getUserId());
return mInstaller;
}其次,构造“会话”参数PackageInstaller中的SessionParams实例,只需要一个mode参数,有两种,一般使用MODE_FULL_INSTALL。
/**
* Construct parameters for a new package install session.
*
* @param mode one of {@link #MODE_FULL_INSTALL} or
* {@link #MODE_INHERIT_EXISTING} describing how the session
* should interact with an existing app.
*/
public SessionParams(int mode) {
this.mode = mode;
}通过PackageInstaller开启会话,并获取会话id。
public int createSession(@NonNull SessionParams params) throws IOException {
...
return mInstaller.createSession(params, installerPackage, mUserId);
}用sessionID打开“会话”
public @NonNull Session openSession(int sessionId) throws IOException {
...
return new Session(mInstaller.openSession(sessionId));
}
2.2、写入Apk并提交
文件流的写入细节就不看了,实现非常复杂。跳到写完文件流提交的地方commit()方法:
public void commit(@NonNull IntentSender statusReceiver) {
...
mSession.commit(statusReceiver, false);
}执行IPackageInstallerSession的commit()方法,IPackageInstallerSession接口的实现类是PackageInstallerSession。
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}通过Handler执行handleCommit()方法,又调用commitNonStagedLocked(List<PackageInstallerSession> )方法
@GuardedBy("mLock")
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
...
if (isMultiPackage()) {
mPm.installStage(activeChildSessions);
} else {
mPm.installStage(committingSession);
}
}最后又到了PMS的installStage(...)中执行,详见1.3节。
3、adb命令安装Apk
最后要探索的这种安装方式,开发者接触的较多。使用adb命令安装Apk文件,关于adb命令的用法详见adb常用命令一文。
adb install file.apk
这种方式安装Apk,分为两个阶段:(1)adb命令传递处理阶段,(2)PMS执行命令阶段。
3.1、adb命令传递阶段
这部分adb相关的源码在/system/core/adb目录下,主体流程如下:
从adb的主函数入口开始探索:main.cpp。
int main(int argc, char* argv[], char* envp[]) {
...
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}执行commandline.cpp中的adb_commandline(...)方法,在该方法中,解析输入的install参数,并执行adb_install.cpp中的install_app(argc, argv)方法:
int install_app(int argc, const char** argv) {
...
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_DEFAULT:
default:
return 1;
}
} 以其中执行的install_app_legacy()方法为例,先将apk push到/data/local/tmp目录。
static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
bool use_localagent) {
...
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest =
"/data/local/tmp/" + android::base::Basename(argv[last_apk]);
...
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
result = pm_command(argc, argv);
...
} 再执行pm_command(argc, argv)方法,向命令中拼接一个pm命令参数:
static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";
while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
}
return send_shell_command(cmd);
}最后调用commandline.cpp中的send_shell_command(cmd)方法发送命令给adbd,后面的逻辑太复杂,简单概括步骤如下:
①命令通过socket发送给adbd
②adbd调用pm处理目录
③pm转交给cmd处理
④cmd解析package命令:cmdMain()方法中
⑤通过Binder调用package:调用IBinder::shellCommand()
⑥执行PMS.onShellCommand()
3.2、PMS执行命令
命令从adb紧接着进入PMS执行,执行主流程如下:
PackageManagerService中的onShellCommand(...)方法执行命令:
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this, mPermissionManagerService)).exec(
this, in, out, err, args, callback, resultReceiver);
}构造PackageManagerShellCommand实例并执行,PackageManagerShellCommand继承自ShellCommand。
public int exec(Binder target, ...) {
...
final int result = super.exec(target, in, out, err, args);
return result;
} ShellCommand又继承自BasicShellCommandHandler,执行它的exec(...)方法:
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args) {
String cmd;
...
res = onCommand(mCmd);
...
return res;
} PackageManagerShellCommand实现了BasicShellCommandHandler中的onCommand(String cmd)抽象方法。
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
try {
switch (cmd) {
case "path":
return runPath();
...
case "install":
return runInstall();
case "install-streaming":
return runStreamingInstall();
...
}
}
return -1;
} 解析命令行中的install参数,执行对应的runInstall()方法
private int runInstall() throws RemoteException {
return doRunInstall(makeInstallParams());
}调用doRunInstall(InstallParams params)执行最终的安装,分为三步:添加文件、写入文件、提交。
private int doRunInstall(final InstallParams params) throws RemoteException {
...
doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)
...
doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex)
...
doCommitSession(sessionId, false /*logSuccess*/)
...
}细节和前面2.2节提到的Session会话安装一样。
流程附件:
APK安装会话:PackageInstallerSession
-
Quibbler 2021-5-272楼Android的应用安装涉及到的几个目录
/data/app:存放用户安装apk的目录,安装时,把apk拷贝到这里
/data/app/~~-zcyVWv4fXg8poNL8-7Bsg==
/data/app/~~-zcyVWv4fXg8poNL8-7Bsg==/package-iIktCcF4ZfEkdjrHwRoPYg==/lib
/data/app/~~-zcyVWv4fXg8poNL8-7Bsg==/package-iIktCcF4ZfEkdjrHwRoPYg==/lib/arm
/data/app/~~-zcyVWv4fXg8poNL8-7Bsg==/package-iIktCcF4ZfEkdjrHwRoPYg==/oat
/data/app/~~-zcyVWv4fXg8poNL8-7Bsg==/package-iIktCcF4ZfEkdjrHwRoPYg==/oat/arm
system/app:系统自带app,访问需要有root权限
/system/app
/system/app/packagename/lib
/system/app/packagename/oat
vender/app:设备厂商提供的app
/vendor/app/
/vendor/app/
/data/data:应用安装完成后,在/data/data目录下自动生成和APK包名相同的文件夹,用户存放应用程序的数据
/data/data/packagename
/data/data/android.a.b/cache
/data/data/android.a.b/code_cache
/data/data/android.a.b/database
/data/data/android.a.b/shared_prefs
/data/data/android.a.b/lib
/data/dalivk-cache:存放apk的dex、odex、vdex、art相关文件,便于应用启动时直接执行
odex、vdex、art是优化的可执行二进制文件,加快启动速度
/data/dalvik-cache/arm
/data/dalvik-cache/arm64





