1 概述
关于session的处理流程,我们要先了解session是什么,它用来做什么
PackageInstaller.Session 是 Android 系统提供的一个类,用于在运行时安装应用程序包(APK)文件。通过 PackageInstaller.Session,开发者可以在应用程序中实现动态安装应用程序,而不需要依赖传统的安装方式(如通过应用商店或命令行)。
使用 PackageInstaller 和 PackageInstaller.Session 类,开发者可以进行应用程序的分阶段安装,管理安装过程中的权限请求、安装进度等操作。这样的安装方式可以提供更好的用户体验,同时也增加了安全性和灵活性。
通常情况下,PackageInstaller.Session 会被用于应用程序更新、应用内动态模块安装等场景中。通过该类,开发者可以控制应用程序的安装过程,并且可以处理各种安装过程中可能出现的情况。
2 代码流程
2.1 session的第一次处理
public void commit(@NonNull IntentSender statusReceiver) {
try {
//这里调用IPackageInstallerSession的commit方法
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
IPackageInstallerSession.aidl
interface IPackageInstallerSession
由此可以知道当次我们进行跨进程通信,systemserver进程
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
/**
* 参数 statusReceiver 用于组件通信
* 携带的数据 安装包名称 安装包id
* forTransfer false
*/
if (hasParentSessionId()) {
/**
* case 是否存在父session
*
* 抛出异常,当次session提交失效
*/
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
+ getParentSessionId() + " and may not be committed directly.");
}
//检查当次会话,无问题的话 确定当次session
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
/**
* case 多安装包情况
*
* 过程 遍历每一个安装包 并且标志 session确定
*
* 只要其中一个安装包session不确定,都会导致安装失败
*/
synchronized (mLock) {
boolean sealFailed = false;
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
sealFailed = true;
}
}
if (sealFailed) {
return;
}
}
}
//当会话确定完成 这里进行会话派发
dispatchSessionSealed();
}
}
总结: 系统应用进程封装session参数传入到system_server进程后,创建PackageinstallSession,开启异步任务向让session保存安装apk的文件数据,接下来执行session.commit跨进程来到PMS进行交互
当前主要进行session确定操作,检查当前系统frp是否开启且禁用安装功能,sessionid是否存在转移
检查完毕 封装会话,派发出去
private boolean markAsSealed(@Nullable IntentSender statusReceiver, boolean forTransfer) {
/**
* 参数 statusReceiver 不为null forTransfer false
*/
Preconditions.checkState(statusReceiver != null || hasParentSessionId(),
"statusReceiver can't be null for the root session");
assertCallerIsOwnerOrRoot();
synchronized (mLock) {
assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
assertNoWriteFileTransfersOpenLocked();
//获取SECURE_FRP_MODE的值 FRP 是一种安全功能,旨在保护设备免受未经授权的访问和数据泄露。
final boolean isSecureFrpEnabled =
(Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1);
if (isSecureFrpEnabled
&& !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
/**
* case frp开启且 frp禁止安装操作
*
* 过程 抛出异常,当次安装失败
*/
throw new SecurityException("Can't install packages while in secure FRP");
}
if (forTransfer) {
/**
* case 检查安装会话是否被修改
*
* 过程 修改 检查俩次安装是否相同,相同 抛出异常
*
* 未修改 不相同抛出异常
*/
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
if (mInstallerUid == mOriginalInstallerUid) {
throw new IllegalArgumentException("Session has not been transferred");
}
} else {
if (mInstallerUid != mOriginalInstallerUid) {
throw new IllegalArgumentException("Session has been transferred");
}
}
setRemoteStatusReceiver(statusReceiver);
// After updating the observer, we can skip re-sealing.
if (mSealed) {
return true;
}
try {
//设置mseal标志位定义当前会话已被seal确定
sealLocked();
} catch (PackageManagerException e) {
return false;
}
}
return true;
}
2.2 session的第二次处理
private void dispatchSessionSealed() {
//发送消息MSG_ON_SESSION_SEALED
mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
可以看到是通过handler消息机制来驱动的,在android系统中这种驱动方式很常见
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_SESSION_SEALED:
handleSessionSealed();
break;
上面session刚确认好,这里执行session确认完成流程
private void handleSessionSealed() {
assertSealed("dispatchSessionSealed");
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create.
/**
* 这里是一个回调操作,了解这个回调的机制流程
* mCallback 是 PackageInstallerSession的内部类 InternalCallback
* InternalCallback 执行该方法时
* 又会引入一个新变量mCallbacks 他实际是一个handler子类
* private class Callbacks extends Handler
*
* 通过handler来驱动消息,最后你会看到IPackageInstallerCallback来处理回调
* 这个一看就知道是aidl回调 那么就是会通知相关进程当前安装的一个状态
* 可以先看我这个了解下 文章最后我会举一个例子
*/
mCallback.onSessionSealedBlocking(this);
//接下来就是apk文件流的验证和提交
dispatchStreamValidateAndCommit();
}
private void dispatchStreamValidateAndCommit() {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
case MSG_STREAM_VALIDATE_AND_COMMIT:
handleStreamValidateAndCommit();
break;
private void handleStreamValidateAndCommit() {
try {
// This will track whether the session and any children were validated and are ready to
// progress to the next phase of install
//allSessionsReady 作为标志位 默认当前session都已经准备就绪
boolean allSessionsReady = true;
//这里如果是多包安装,需要对每个包的数据都进行验证
/**
* case 多安装包情况
* 过程 每个子安装包都要进行数据验证,最后确认Session是否准备就绪
*/
for (PackageInstallerSession child : getChildSessions()) {
allSessionsReady &= child.streamValidateAndCommit();
}
/**
* case 普通安装单包情况
*
* 过程 直接进行streamValidateAndCommit 数据包验证操作
* 验证成功会通过handler驱动下一次操作
*/
if (allSessionsReady && streamValidateAndCommit()) {
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
} catch (PackageManagerException e) {
//失败了也要通知相关进程或者服务
destroy();
String msg = ExceptionUtils.getCompleteMessage(e);
dispatchSessionFinished(e.error, msg, null);
maybeFinishChildSessions(e.error, msg);
}
}
总结 这里主要针对安装apk的数据进行一个验证,多安装包,分批检查,单安装包,直接检查
private void handleStreamValidateAndCommit() {
try {
// This will track whether the session and any children were validated and are ready to
// progress to the next phase of install
//allSessionsReady 作为标志位 默认当前session都已经准备就绪
boolean allSessionsReady = true;
//这里如果是多包安装,需要对每个包的数据都进行验证
/**
* case 多安装包情况
* 过程 每个子安装包都要进行数据验证,最后确认Session是否准备就绪
*/
for (PackageInstallerSession child : getChildSessions()) {
allSessionsReady &= child.streamValidateAndCommit();
}
/**
* case 普通安装单包情况
*
* 过程 直接进行streamValidateAndCommit 数据包验证操作
* 验证成功会通过handler驱动下一次操作
*/
if (allSessionsReady && streamValidateAndCommit()) {
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
} catch (PackageManagerException e) {
//失败了也要通知相关进程或者服务
destroy();
String msg = ExceptionUtils.getCompleteMessage(e);
dispatchSessionFinished(e.error, msg, null);
maybeFinishChildSessions(e.error, msg);
}
}
总结: session的派发中,第一次进行了session的条件判断确认,第二次进行了session内安装包的数据检查验证,接下来该来到session的第三次处理流程
case MSG_INSTALL:
handleInstall();
break;
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
//跟踪设备上应用程序的安装情况
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(getInstallSource().installerPackageName)
.write();
}
/**
* Stops the installation of the whole session set if one session needs user action
* in its belong session set. When the user answers the yes,
* {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
* handled to come back here to check again.
*/
if (sendPendingUserActionIntentIfNeeded()) {
return;
}
//根据参数params的isStaged检查当前是否分阶段验证
/**
* params 是packageInstallerSession构建的时候传入的 PackageInstaller.SessionParams
* 封装着安装包的相关信息
* case 是否分阶段
* 过程 分阶段使用mStagedSession
* 不分阶段使用verify
* 这里分析不分阶段
*/
if (params.isStaged) {
mStagedSession.verifySession();
} else {
//普通验证流程
verify();
}
}
private void verify() {
try {
/**
* 可以看到verify主要的工作就是解析安装包的文件数据
* case 多安装包
* 过程 遍历子安装包解析每一个子安装包的文件
*
* case 单安装包
*
* 直接解析
*/
List<PackageInstallerSession> children = getChildSessions();
if (isMultiPackage()) {
for (PackageInstallerSession child : children) {
child.prepareInheritedFiles();
child.parseApkAndExtractNativeLibraries();
}
} else {
prepareInheritedFiles();
parseApkAndExtractNativeLibraries();
}
//解析完成执行
verifyNonStaged();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
setSessionFailed(e.error, errorMsg);
onSessionVerificationFailure(e.error, errorMsg);
}
}
解析涉及代码不多,我们也看下解析相关
private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
synchronized (mLock) {
/**
* 在解析安装包文件前会进行一些case判断
* case 当前解析的目录/文件是否已经被使用了
*
* case mDestroyed = true
*
* case mSealed = false
*
* 这三种情况都会抛出异常,提示安装失败原因
*/
if (mStageDirInUse) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session files in use");
}
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session destroyed");
}
if (!mSealed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session not sealed");
}
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
final PackageLite result;
/**
* case 判断当次解析的session是否为apex
*
* 过程 这里我们分析apk安装 所以执行getOrParsePackageLiteLocked 解析拿到PackageLite
*
* 这个PackageLite是包含应用程序相关信息,当然不是完整的,后续会看到他的具体作用
*
* 最后解析apk包含的lib库文件
*/
if (!isApexSession()) {
// For mode inherit existing, it would link/copy existing files to stage dir in
// prepareInheritedFiles(). Therefore, we need to parse the complete package in
// stage dir here.
// Besides, PackageLite may be null for staged sessions that don't complete
// pre-reboot verification.
result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
} else {
result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
}
if (result != null) {
mPackageLite = result;
if (!isApexSession()) {
synchronized (mProgressLock) {
mInternalProgress = 0.5f;
computeProgressLocked(true);
}
extractNativeLibraries(
mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
}
}
总结:主要就是解析apk部分信息拿到ePackageLite,解析native lib库文件
解析完成开始下一步流程
2.3 session的第三次处理
private void verifyNonStaged()
throws PackageManagerException {
synchronized (mLock) {
markStageDirInUseLocked();
}
//这里主要进行session 验证 验证通过执行内部回调函数
/**
* 这里通过会话提供器获取会话验证器,来验证当前安装会话
*
* 结果 error 报错 msg 异常消息
*
* 验证安装会话后,会通过匿名函数回调的方式通过handler来给到主线程处理callback任务
*
* case 安装成功
*
* 执行会话验证完成回调
*
* case 安装失败
* 执行会话验证失败的回调
*/
mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
mHandler.post(() -> {
if (dispatchPendingAbandonCallback()) {
// No need to continue if abandoned
return;
}
if (error == INSTALL_SUCCEEDED) {
//这里执行该方法
onVerificationComplete();
} else {
onSessionVerificationFailure(error, msg);
}
});
});
}
总结 主要是对安装会话进行验证相关操作,sessionVerifier.verify的具体处理流程的话,后续单独出文章详细分析,先了解宏观的结构
private void onVerificationComplete() {
//Android应用安装过程中,引入了"staged"(暂存)阶段作为安装的一部分。在这个阶段,
//安装系统会将安装操作拆分成多个步骤来执行,而不是一次性完成整个安装过程。
//这种方式有助于提高安装的效率和稳定性,尤其是对于大型应用或多模块应用的安装
/**
* case 检查当次安装是否需要分阶段
* 过程 这里执行普通安装操作
*/
if (isStaged()) {
//分阶段安装操作
mStagingManager.commitSession(mStagedSession);
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
return;
}
//普通安装操作
install();
}
在查看安装操作时,我们需要先了解,这里的一个用到的一个java语法CompletableFuture 它也是一个异步任务的对象
由java8 引入的一个概念,我们可以管理异步任务的完成状态,并且可以对异步任务的处理结果进行相关处理
如何创建completableFuture对象
CompletableFuture.supplyAsync方法,或者CompletableFuture.runAsync方法
我们也可以组成多个completableFuture对象,通过使用thenApply,thenAccept,thenCombine方法来管理这些异步任务的执行顺序
eg
java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CompletableFutureDemo {
public static void main(String[] args) {
// 创建一个CompletableFuture来执行异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// 模拟耗时操作
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
// 定义在CompletableFuture完成后执行的操作
CompletableFuture<Void> resultFuture = future.thenAccept(result -> {
System.out.println("Result received: " + result);
});
// 等待异步任务完成
resultFuture.join();
System.out.println("CompletableFuture demo finished.");
}
}
如何处理completableFuture抛出的异常
使用exceptionally方法
eg
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionDemo {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟一个会抛出异常的异步任务
if (Math.random() < 0.5) {
throw new RuntimeException("Simulated exception");
}
return 42;
});
// 使用exceptionally()处理异常
CompletableFuture<Integer> resultFuture = future.exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return -1; // 返回一个备用结果
});
resultFuture.thenAccept(result -> {
System.out.println("Result: " + result);
});
try {
resultFuture.get(); // 等待异步任务完成
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用allof方法可以等待所有任务执行完成
eg
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureAllOfDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result from future1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result from future2");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Result from future3");
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);
// 等待所有CompletableFuture完成
allFutures.get();
System.out.println("All CompletableFuture completed.");
System.out.println("Result 1: " + future1.get());
System.out.println("Result 2: " + future2.get());
System.out.println("Result 3: " + future3.get());
}
接着我们接着看install函数
private CompletableFuture<Void> install() {
//获取异步任务集合里面存放着安装的结果
//从这里可以猜到installNonStaged可能是具体的安装操作
List<CompletableFuture<InstallResult>> futures = installNonStaged();
//根据集合的数量创建对应数组
CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
//这里当安装操作都完成后,获取安装的结果和异常,执行异步任务
return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
if (t == null) {
//没有异常的情况,处理异步任务完成的结果,派发通知当前安装会话完成
setSessionApplied();
for (CompletableFuture<InstallResult> f : futures) {
InstallResult result = f.join();
result.session.dispatchSessionFinished(
INSTALL_SUCCEEDED, "Session installed", result.extras);
}
} else {
//安装存在异常,设置会话失败,派发通知当前安装会话失败
PackageManagerException e = (PackageManagerException) t.getCause();
setSessionFailed(e.error,
PackageManager.installStatusToString(e.error, e.getMessage()));
dispatchSessionFinished(e.error, e.getMessage(), null);
maybeFinishChildSessions(e.error, e.getMessage());
}
});
}
installNonStaged函数返回异步任务集合
现看installNonStaged函数
private List<CompletableFuture<InstallResult>> installNonStaged() {
try {
//构建异步任务集合
List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
//首先构建异步任务加入集合
CompletableFuture<InstallResult> future = new CompletableFuture<>();
futures.add(future);
//将异步任务关联到InstallParams中
final InstallParams installingSession = makeInstallParams(future);
if (isMultiPackage()) {
//case1 多apk情况
//获取子会话
final List<PackageInstallerSession> childSessions = getChildSessions();
//创建子会话集合
List<InstallParams> installingChildSessions = new ArrayList<>(childSessions.size());
for (int i = 0; i < childSessions.size(); ++i) {
//遍历子会话,构建异步任务关联子会话,
final PackageInstallerSession session = childSessions.get(i);
future = new CompletableFuture<>();
futures.add(future);
final InstallParams installingChildSession = session.makeInstallParams(future);
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
}
//这里子会话不为空
if (!installingChildSessions.isEmpty()) {
//执行安装操作
installingSession.installStage(installingChildSessions);
}
} else if (installingSession != null) {
//对于普通安装 正常执行安装操作
installingSession.installStage();
}
return futures;
} catch (PackageManagerException e) {
List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
futures.add(CompletableFuture.failedFuture(e));
return futures;
}
}
总结: 在这里可以看到根据多包安装还是单包安装,我们会构建CompletableFuture对象,通过makeInstallParams函数将future关联到InstallParams中,当InstallParams准备好后,会调用installStage函数执行安装操作并且将InstallParams传递进去
接着回到install函数,当所有异步任务完成并且没有异常的话,设置安装session被使用,主线程等待异步任务完成,异步任务完成后执行dispatchSessionFinished派发出去通知已安装
到这里我们跟踪了installSession的整个处理流程
接下来我们会详细去看安装的流程细节,还有dispatchSessionFinished的派发最后通知了谁
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题