前言
上一篇文章跟着源码的脚步了解了Android系统在开机的过程中,对系统中安装的应用程序会进行apk文件解析,并且对在manifest中注册的四大组件进行解析,并且将相应组件的信息缓存在PKMS中,然后通过文件目录的分析,发现我们安装的应用程序都放在了/data/app文件目录下,并且每一个apk的命名统一标准化为base.apk,那么本文就再一次进入痛苦的源码追踪过程,去看看Android系统安装应用到底做了什么
应用安装部分分为了应用的拷贝到/data/app文件夹和后续的解析步骤,这一篇文章主要追踪apk文件是怎么拷贝到目标文件夹的过程
对于Android开发应该都了解过应用内安装更新应用的需求,通过下载apk文件,然后通知系统去执行安装步骤,会弹出一个系统的安装界面,显示部分应用的信息,当用户点击同意安装后,进入后续的安装流程,这个让用户选择的弹窗页面,就是系统提供的PackageInstallerActivity页面,位于com.android.packageinstaller包下的系统页面
public class PackageInstallerActivity extends AlertActivity {
@Override
protected void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
//拿到PKMS
mPm = getPackageManager();
//PKMS中的处理应用安装的对象
mInstaller = mPm.getPackageInstaller();
final Intent intent = getIntent();
mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
PackageInstaller.SessionParams.UID_UNKNOWN);
mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
? getPackageNameForUid(mOriginatingUid) : null;
final Uri packageUri;
if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
mSessionId = sessionId;
//这不拿到安装apk的路径,也就是去读取apk信息
packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
bindUi();
}
}
对应用开发者来说,这部分写在Actiivity中onCreate方法还是很熟悉的,给跟安装应用相关的成员变量进行了赋值操作,比如跟安装相关的PKMS服务,然后调用了bindUi去显示安装的选择页面,确认是否进行应用安装
private void bindUi() {
//进行安装页面的视图的数据绑定
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
//这个布局可以去源码查看
//frameworks/base/packages/PackageInstaller/res/layout/install_content_view.xml
mAlert.setView(R.layout.install_content_view);
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);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
//如果mSessionId存在,取消安装
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
}
以小米手机的这个页面为例子,除了显示应用相关的信息,厂商还额外去显示了厂商应用市场的数据,底部的主要事件就是确认安装和取消安装,这部分界面的显示和控件事件的绑定都还是很简单的,确认安装后就进入了startInstall()方法
//这个方法实例化了一个Intent对象,并通过传参跳转到下一个Activity
private void startInstall() {
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
//从这里可以看出,是跳转到了InstallInstalling这个Activity,
//这个Activity的命名不是太Activity
newIntent.setClass(this, InstallInstalling.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
//...省略部分参数的解析部分
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(newIntent);
finish();
}
虽然跳转到新的InstallInstalling页面命名上不是太Activity,但是从后面的执行方法,startActivity可以看出下一步又是一个新的页面,并且finish将当前页面关闭
//这确实是一个Activity,直接进入onCreate方法
public class InstallInstalling extends AlertActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();
if ("package".equals(mPackageURI.getScheme())) {
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
//根据传入的mPackageURI,去创建一个对应的文件
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
//显示安装软件相关的信息
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);
//如果savedInstanceState不为null,获取保存的mSessionId和mInstallId
if (savedInstanceState != null) {
//安装包的会话id
mSessionId = savedInstanceState.getInt(SESSION_ID);
//等待安装的事件id
mInstallId = savedInstanceState.getInt(INSTALL_ID);
try {
//根据mInstallId向InstallEventReceiver注册一个观察者
//launchFinishBasedOnResult会接收到安装事件的回调
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
}
} else {
//savedInstanceState为null,创建一个SessionParams对象,包装安装会话的参数
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setInstallAsInstantApp(false);
params.setInstallerPackageName(getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
//对对需要安装的apk对象路径创建文件对象
File file = new File(mPackageURI.getPath());
try {
//对apk文件进行解析,将解析的apk相关信息保存在params中
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
params.setAppPackageName(pkg.packageName);
params.setInstallLocation(pkg.installLocation);
params.setSize(
PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
}
try {
//类似的,需要向InstallEventReceiver注册观察者,并且会返回一个安装的会话id
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
}
try {
//与PKMS内部的installer对象通信,拿到一个安装的事件id
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
}
}
mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
mSessionCallback = new InstallSessionCallback();
}
}
}
这个页面也是对需要安装的apk文件解析出基本信息,让用户可视化预览需要安装的应用内容,并且会跟PKMS进行通信,调用到createSession()方法,创建一个mSessionId的事件id
而具体的安装事件还得继续在oncreate生命周期之后的onResume()中
//InstallInstalling.java
@Override
protected void onResume() {
super.onResume();
if (mInstallingTask == null) {
//应用安装的对象
PackageInstaller installer = getPackageManager().getPackageInstaller();
//这里通过上面创建的mSessionId去获取sessionInfo
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
//创建一个安装任务并且调用execute()方法
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
//AsyncTask这个常用的做异步任务的对象
private final class InstallingAsyncTask extends AsyncTask {
volatile boolean isDone;
//doInBackground的具体业务就是拿到包的Uri,通过文件IO流的方式将apk的内容写到Session中
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
//前面定义了sessionId,全程追踪标记
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
session.setStagingProgress(0);
try {
//构建File对象
File file = new File(mPackageURI.getPath());
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) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
//这里可以看到写入操作,将APK的信息通过IO读写写到了session中
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
//doInBackground完成后,在onPostExecute方法中,会调用commit方法进行安装
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
//session里面已经保存有apk的信息,调用commit进行后续安装流程
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {}
}
}
可以看到应用安装的就是将整个apk文件通过IO流的方式进行读取,并且保存在到安装器PackageInstaller对象的一次安装会话中,上面的流程除了解析apk部分概览信息,没有对apk有额外的操作
//PackageInstaller.java
public static class Session implements Closeable {
//IPackageInstallerSession是一个aidl文件,说要要通过这个对象进行跨进程通信
//要找实现功能的地方,就要找到这个类的Stub对象的实现,最终调用到了PackageInstallerSession对象中
protected final IPackageInstallerSession mSession;
public Session(IPackageInstallerSession session) {
mSession = session;
}
//继续调用
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
//PackageInstallerSession.java
//可以看出这个是AIDL生成的服务端Stub的实现类
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
//将传入参数进一步markAsSealed方法
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
//...
}
//可以看到就是通过handler发送一个MSG_STREAM_VALIDATE_AND_COMMIT的消息
dispatchStreamValidateAndCommit();
}
}
//看到handler就需要直接定位到handlemessage的位置
private void dispatchStreamValidateAndCommit() {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_STREAM_VALIDATE_AND_COMMIT:
//首次处理的message消息
handleStreamValidateAndCommit();
break;
case MSG_INSTALL:
//准备好后回调到这个消息进行安装操作
handleInstall();
break;
}
return true;
}
};
private void handleStreamValidateAndCommit() {
boolean allSessionsReady = false;
try {
//会对需要执行的安装会话session进行可用性检查
allSessionsReady = streamValidateAndCommit();
} catch (PackageManagerException e) {
unrecoverableFailure = e;
}
if (isMultiPackage()) {
//...Multi也就是拆分糊调用同样的方法
}
//处理完后继续发生Message消息
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
将apk文件信息保存在session后,通过跨进程的方式将任务继续分发,先对session内的数据和锁状态进行了检查操作,通过handler机制分发任务,最终执行到了handleInstall()方法去执行安装操作
//PackageInstaller.java
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {}
if (params.isStaged) {}
if (isApexInstallation()) {}
//处理multiPackage的情况
List childSessions = getChildSessionsNotLocked();
try {
synchronized (mLock) {
//继续往后执行
installNonStagedLocked(childSessions);
}
} catch (PackageManagerException e) {}
}
//这个方法中,会将处理好的session传给PKMS进行处理
private void installNonStagedLocked(List childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
makeSessionActiveLocked();
if (isMultiPackage()) {
List installingChildSessions =
new ArrayList<>(childSessions.size());
mPm.installStage(installingChildSessions);
} else {
//不管是不是multi得类型,最终都需要跨进程执行到PKMS的installStage方法
mPm.installStage(installingSession);
}
}
上一篇文章中就写了PKMS的主要职责只有就是负责apk的安装,从安装界面的开始,用户点击确认安装后,一直在对本次安装的apk文件进行处理封装到Session对象中,并对Session进行各种预处理,最终还是需要执行到PKMS的安装方法
//PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
//创建了一个INIT_COPY的消息对象
final Message msg = mHandler.obtainMessage(INIT_COPY);
//将前面封装的session对象进行处理成InstallParams对象,安装包的数据对象
final InstallParams params = new InstallParams(activeInstallSession);
msg.obj = params;
//贯穿整个Android系统的handler-message机制
mHandler.sendMessage(msg);
}
//处理部分
class PackageHandler extends Handler {
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
//应用的安装实际执行的是APK的拷贝动作
params.startCopy();
}
break;
}
}
}
}
//PackageManagerService#HandlerParams
//这是一个抽象类,有multi和普通的两个实现类
final void startCopy() {
handleStartCopy();//这一步做拷贝前的准备工作
handleReturnCode();//执行拷贝工作
}
// InstallParams extends HandlerParams
private InstallArgs mArgs;
void handleReturnCode() {
if (mVerificationCompleted
&& mIntegrityVerificationCompleted && mEnableRollbackCompleted) {
//前面准备工作OK了
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
//执行apk文件的拷贝,执行到InstallArgs的方法
mRet = mArgs.copyApk();
}
//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
//还有下一步
//文件拷贝结束后,执行应用的安装工作
processPendingInstall(mArgs, mRet);
//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
}
//InstallArgs也是抽象类
class FileInstallArgs extends InstallArgs {
//int copyApk() -> 调用 doCopyApk();
private int doCopyApk() {
//...继续
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
//这个文件路径通过源码跟踪,最终执行到了
//Environment.getDataAppDirectory(volumeUuid)
//也就是/data/app
codeFile = tempDir;
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
return ret;
}
}
//调用到PackageManagerServiceUtils工具类
public static int copyPackage(String packagePath, File targetDir) {
try {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
//将apk文件复制到目标路径data/app,并统一命名为base.apk
copyFile(pkg.baseCodePath, targetDir, "base.apk");
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i < pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], targetDir,
"split_" + pkg.splitNames[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
}
//具体的文件流操作,复制文件
private static void copyFile(String sourcePath, File targetDir, String targetName)
throws ErrnoException, IOException {
final File targetFile = new File(targetDir, targetName);
final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(targetFile.getAbsolutePath(), 0644);
FileInputStream source = null;
try {
source = new FileInputStream(sourcePath);
FileUtils.copy(source.getFD(), targetFd);
} finally {
IoUtils.closeQuietly(source);
}
}
通过一些列的操作,就将我们需要安装的apk安装包通过文件读写的方式,拷贝到了/data/app文件目录,并且重命名为base.apk,当拷贝完apk后安装流程还没有结束,就比如上一篇分析的,对应apk的manifest解析没有进行,如果要打开这个app应用,连四大组件的信息都找不到
在上面代码中也有标记,当copyapk结束后,会去执行processPendingInstall()方法,继续处理应用安装的和PKMS的解析,在后面的文章进行跟踪记录