内容简介:在本系列上一篇文章在前一篇文章末尾,我们讲过 commitLocked 方法,我们回顾下:正式进入 PMS 源码分析流程,我们看看 installStage 方法:
开篇
核心源码
| 关键类 | 路径 |
|---|---|
| PackageInstallerSession.java | frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java |
| PackageManagerService.java | frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java |
前言
在本系列上一篇文章 【深入研究 PackageManagerService 系列(5)之 PackageInstaller - APK 安装流程】 中,我们了解了 PackageInstaller 安装 APK 的流程,最后会将 APK 的信息交由 PMS 处理。那么 PMS 是如何处理的?这就是我们这篇文章需要分析的。
PackageHandler
commitLocked
在前一篇文章末尾,我们讲过 commitLocked 方法,我们回顾下:
private final PackageManagerService mPm;
private void commitLocked() throws PackageManagerException {
... ...
/**
* commitLocked 方法很长,我们主要关注这一行代码
* 调用 PackageManagerService 的 installStage 方法
* 这样安装 APK 的代码逻辑就进入了 PackageManagerService 中
*/
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
installStage
正式进入 PMS 源码分析流程,我们看看 installStage 方法:
void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
... ...
// 创建类型为 INIT_COPY 的消息
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
// 创建 InstallParams,它对应于包的安装数据
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
... ...
// 将 InstallParams 通过消息发送出去
mHandler.sendMessage(msg);
}
handleMessage
因为 PackageHandler 继承 Handler ,所以我们来看下 PackageHandler 的 HandlerMessage 方法:
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
// 设置了线程的优先级为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
INIT_COPY
接下来看下 INIT_COPY 消息的处理流程:
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>();
... ...
// 用于处理各个类型的消息
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
// 取出 InstallParams
HandlerParams params = (HandlerParams) msg.obj;
// idx 为当前需要安装的 APK 个数,mPendingInstalls 里面保存所有需要安装的 APK 解析出来的 HandlerParams 参数
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// mBound 用于标识是否绑定了服务(DefaultContainerService),
// 如果已经绑定了,则 mBound 为true,如果是第一次调用 mBound 为 false,默认值为 false。
if (!mBound) {
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
// 如果没有绑定服务,重新绑定,connectToService 方法内部如果绑定成功会将 mBound 置为 true
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
if (params.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
params.traceCookie);
}
// 绑定服务失败则 return
return;
} else {
// 绑定服务成功,将请求添加到 ArrayList 类型的 mPendingInstalls 中,等待处理
mPendingInstalls.add(idx, params);
}
} else {
// 已经绑定服务
mPendingInstalls.add(idx, params);
if (idx == 0) {
// 如果是第一个安装请求,则直接发送事件 MCS_BOUND 触发处理流程
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
... ...
}
connectToService
假设我们是第一次走流程,还没有绑定服务,则会调用 connectToService() 方法,我们看下流程:
class PackageHandler extends Handler {
private boolean connectToService() {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
/**
* bindServiceAsUser 方法会传入 mDefContainerConn,
* bindServiceAsUser 方法的处理逻辑和我们调用 bindService 是类似的,
* 服务建立连接后,会调用 onServiceConnected
*/
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 如果绑定 DefaultContainerService 成功,mBound 会置为 ture
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
这里可以看到 bind 到了一个 service ,这个 service 的 ComponentName 是 "DEFAULT_CONTAINER_COMPONENT" 这个常量,那我们就来看下这个 ComponentName。
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
所以我们知道 bind 的 service 是 DefaultContainerService 。绑定 DefaultContainerService 之后,设定进程的优先级为 THREAD_PRIORITY_DEFAULT。
然后等 bindServiceAsUser 这个方法执行完则又把线程的优先级设为 THREAD_PRIORITY_BACKGROUND。
我们这边要重点提到一个 mDefContainerConn 变量,研究一下:
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
这下我们知道了 mDefContainerConn 的类型是 DefaultContainerConnection ,那我们来看下 DefaultContainerConnection 这个类。
DefaultContainerConnection
// DefaultContainerConnection 实现了 ServiceConnection,所以在连接成功的时候会调用 onServiceConnected 方法
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
final IMediaContainerService imcs = IMediaContainerService.Stub
.asInterface(Binder.allowBlocking(service));
// 发送了 MCS_BOUND 类型的消息
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
上文我们提及到 mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM) 方法, 其实就是"绑定" DefaultContainerService。我们知道 bind 一个 Service ,其中负责通信的 ServiceConnection, 而本方法中负责通信的就是 mDefContainerConn。所以一旦绑定成功会执行 mDefContainerConn 的 onServiceConnected 方法。 而现实是当绑定成功后在 onServiceConnected 中将一个 IBinder 转换成了一个 IMediaContainerService。 这个就是 onServiceConnected 回调函数中根据参数传进来的 IMediaContainerService.Stub 的对象引用创建的一个远程代理对象, 后面 PacakgeManagerServic 通过该代理对象访问 DefaultContainerService 服务。
我们简单梳理一下以上代码所做的工作:
:sparkles: mBound 用于标识是否绑定了 DefaultContainerService,默认值为 false。
:sparkles: DefaultContainerService 是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此 DefaultContainerService 没有和 PMS 运行在同一进程中,它运行在 com.android.defcontainer 进程,通过 IMediaContainerService 和 PMS 进行 IPC 通信。
彼此之间的 IPC 通信如下图所示:
:sparkles: connectToService 方法用来绑定 DefaultContainerService。
:sparkles: mHandler.sendEmptyMessage(MCS_BOUND):发送 MCS_BOUND 类型的消息,触发处理第一个安装请求。
不知道你是否发现,有两个发送 MCS_BOUND 类型消息的方法:
// PackageHandler.doHandleMessage(已绑定服务) mHandler.sendEmptyMessage(MCS_BOUND); // 不带参数 // DefaultContainerConnection(未绑定服务 - 绑定服务) mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); // 带参数
MCS_BOUND 分析
不带参数
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
// 不带参数,则此条件不满足
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
// 走这边的逻辑
if (mContainerService == null) {
// 服务没有绑定,则走这边,但是之前我们讲解过,发送 MCS_BOUND 时,已经绑定了服务,这显然是不正常的
if (!mBound) {
// Something seriously wrong since we are not bound and we are not
// waiting for connection. Bail out.
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// 负责处理服务发生错误的情况
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
if (params.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
params.traceMethod, params.traceCookie);
}
return;
}
// 绑定失败,清空安装请求队列
mPendingInstalls.clear();
} else {
// 继续等待绑定服务
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {
... ...
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
带参数
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
// 带参数,此条件不满足
if (mContainerService == null) {
... ...
// 走这边的逻辑,安装请求队列不为空
} else if (mPendingInstalls.size() > 0) {
// 得到安装请求队列第一个请求 HandlerParams
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
// 如果 HandlerParams 不为 null 就会调用 HandlerParams 的 startCopy 方法,用于开始复制 APK 的流程
if (params.startCopy()) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind...");
// 如果 APK 安装成功,删除本次安装请求
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
// 如果没有安装请求了,发送解绑服务的请求
if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work");
// 如果还有其他的安装请求,接着发送 MCS_BOUND 消息继续处理剩余的安装请求
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// 如果安装请求数不大于 0 就会打印 “Empty queue”
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
上面的流程其实很简单,我们根据是否传入了 ims 这个参数,走两条流程,核心的方法就是最终的 startCopy()。
复制 APK
上面我们提过,Copy APK 的操作是调用 HandlerParams 的 startCopy 方法。HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。
startCopy
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
/**
* Number of times startCopy() has been attempted and had a non-fatal
* error.
*/
private int mRetries = 0;
... ...
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
/**
* mRetries 用于记录 startCopy 方法调用的次数,调用 startCopy 方法时会先自动加 1
* startCopy 方法尝试的次数超过了 4 次,就放弃这个安装请求
*/
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
// 发送 MCS_GIVE_UP 类型消息,将第一个安装请求(本次安装请求)从安装请求队列 mPendingInstalls 中移除掉
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy(); // :boom: :boom: :boom: 重点方法 :boom: :boom: :boom:
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
// 调用 handleReturnCode 抽象方法,这个方法会在 handleStartCopy 执行完拷贝相关行为之后,根据 handleStartCopy 做进一步的处理,主要返回状态码
handleReturnCode();
return res;
}
... ...
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
}
这边我们还是简单的看一下 MCS_GIVE_UP 和 MCS_RECONNECT 两种 message 的处理流程,逻辑相当简单:
MCS_RECONNECT
case MCS_RECONNECT: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
}
mPendingInstalls.clear();
}
}
break;
}
判断安装请求队列 mPendingInstalls 是否还有元素,如果有元素先断开绑定,则再次重新调用 connectToService 方法,我们知道 connectToService() 内部会再次执行绑定 DefaultContainerService,而在绑定成功后会再次发送一个 what 值为 MCS_BOUND 的 Message,从而又回到了 startCopy 里面。
MCS_GIVE_UP
case MCS_GIVE_UP: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
HandlerParams params = mPendingInstalls.remove(0);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
break;
}
直接删除了安装请求队列 mPendingInstalls 里面下标为 0 的元素,即取消本次安装请求。
handleStartCopy
我们发现 handleStartCopy 也是一个抽象的方法,那么它在哪实现?前面我们说过:HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。
class InstallParams extends HandlerParams {
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
... ...
/**
* 确定 APK 的安装位置
* onSd: 安装到 SD 卡
* onInt: 内部存储即 Data 分区
* ephemeral:安装到临时存储
*/
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
// APK 不能同时安装在 SD 卡和 Data 分区
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
// 安装标志冲突,Instant Apps 不能安装到 SD 卡中
} else if (onSd && ephemeral) {
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
/**
* 获取 APK 的少量的信息
* 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 getMinimalPackageInfo 方法,
* 该方法轻量解析 APK 并得到 APK 的少量信息,
* 轻量解析的原因是这里不需要得到 APK 的全部信息,APK 的少量信息会封装到 PackageInfoLite 中。
*/
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
if (DEBUG_EPHEMERAL && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
... ..
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// 判断安装的位置
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
... ...
} else {
installFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite,
mSettings.mPackages, getUser());
loc = installLocationPolicy(pkgLite);
... ...
}
}
/**
* 根据 InstallParams 创建 InstallArgs 对象
* InstallArgs 是一个抽象类,定义了 APK 的安装逻辑,比如"复制"和"重命名" APK 等
*
* abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
*
* InstallArgs 有 3 个子类,都被定义在 PMS 中:
* FileInstallArgs:用于处理安装到非 ASEC 的存储空间的 APK ,也就是内部存储空间(Data分区)
* AsecInstallArgs:用于处理安装到 ASEC 中(mnt/asec)即 SD 卡中的 APK
* MoveInstallArgs:用于处理已安装 APK 的移动的逻辑
*/
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
... ...
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
... ...
} else {
// 对 APK 进行检查后就会调用 InstallArgs 的 copyApk 方法进行安装
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
... ...
}
FileInstallArgs
OK,我们知道 InstallParams 有三个子类,不同的 InstallArgs 子类会有着不同的处理,那我们现在以 FileInstallArgs 为例跟踪学习一下具体的流程:
copyApk
/**
* Logic to handle installation of non-ASEC applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
private File codeFile;
... ...
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk(imcs, temp);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
doCopyApk
调用了 doCopyApk 方法:
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
... ...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp ,其中 18300388 是安装的 sessionId
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
... ...
int ret = PackageManager.INSTALL_SUCCEEDED;
/**
* 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 copyPackage 方法,
* 这个方法会在 DefaultContainerService 所在的进程中将 APK 复制到临时存储目录,
* 比如 /data/app/vmdl18300388.tmp/base.apk 。
*/
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 真正的文件拷贝
... ...
return ret;
}
安装 APK
handleReturnCode
我们回到 APK 的复制调用链的头部方法:HandlerParams 的 startCopy 方法,在最后 调用了 handleReturnCode 方法,进行 APK 的安装。
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
private int mRetries = 0;
... ...
final boolean startCopy() {
boolean res;
try {
if (++mRetries > MAX_RETRIES) {
... ...
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
... ...
}
// 处理复制 APK 后的安装 APK 逻辑
handleReturnCode(); // :boom: :boom: :boom: :boom: :boom: :boom:
return res;
}
... ...
abstract void handleReturnCode();
}
handleReturnCode 也是一个抽象方法,那么在哪里实现?同样,它的实现在 InstallParams 中。
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
// "装载代码"的入口是 processPendingInstall(InstallArgs,int) 方法
processPendingInstall(mArgs, mRet);
}
}
我们发现调用了 processPendingInstall 方法,继续跟!
processPendingInstall
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
/**
* 安装前处理
* 用于检查 APK 的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的 APK 文件
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res); // :boom: :boom: :boom: :boom: :boom: :boom:
}
/**
* 安装后收尾
* 用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件
args.doPostInstall(res.returnCode, res.uid);
}
... ...
}
});
}
installPackageTracedLI
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
installPackageLI(args, res);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
installPackageLI
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
... ...
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
// 解析 APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
... ...
// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// 检查 APK 是否存在
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// 获取没被改名前的包名
String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
// 设置标志位表示是替换安装
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
}
... ...
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
// 查看 Settings 中是否存有要安装的 APK 的信息,如果有就获取签名信息
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
PackageSetting signatureCheckPs = ps;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
if (libraryEntry != null) {
signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
}
}
// 检查签名的正确性
if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
... ...
}
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
// 遍历每个权限,对权限进行处理
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
... ...
}
}
if (systemApp || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) {
if (onExternal) {
// 系统APP不能在SD卡上替换安装
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
} else if (instantApp) {
// 系统 APP 不能被 Instant App 替换
res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
return;
}
}
... ...
// 重命名临时文件
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
if (!instantApp) {
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
// 替换安装
if (pkg.applicationInfo.isStaticSharedLibrary()) {
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+ "static-shared libs cannot be updated");
return;
}
}
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res, args.installReason);
} else {
// 安装新的 APK
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
// 更新应用程序所属的用户
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
... ...
}
}
installPackageLI 方法的代码很长,这里截取主要的部分,主要做了几件事:
:sparkles: 1、创建 PackageParser 解析 APK 。
:sparkles: 2、检查 APK 是否存在,如果存在就获取此前没被改名前的包名,赋值给 PackageParser.Package 类型的 pkg ,将标志位 replace 置为 true 表示是替换安装。
:sparkles: 3、如果 Settings 中保存有要安装的 APK 的信息,说明此前安装过该 APK ,则需要校验 APK 的签名信息,确保安全的进行替换。
:sparkles: 4、将临时文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名为 /data/app/包名-1/base.apk 。这个新命名的包名会带上一个数字后缀 1,每次升级一个已有的 App ,这个数字会不断的累加。
:sparkles: 5、系统 APP 的更新安装会有两个限制,一个是系统 APP 不能在 SD 卡上替换安装,另一个是系统 APP 不能被 Instant App 替换。
:sparkles: 6、根据 replace 来做区分,如果是替换安装就会调用 replacePackageLIF 方法,其方法内部还会对系统 APP 和非系统 APP 进行区分处理,如果是新安装 APK 会调用 installNewPackageLIF 方法。
installNewPackageLIF
我们以安装新 APK 为例,查看 installNewPackageLIF 的源码:
/*
* Install a non-existing package.
*/
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
... ...
try {
// 扫描 APK
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
// 更新 Settings 信息
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// 安装成功后,为新安装的应用程序准备数据
prepareAppDataAfterInstallLIF(newPackage);
} else {
// 安装失败则删除 APK
deletePackageLIF(pkgName, UserHandle.ALL, false, null,
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
installNewPackageLIF 主要做了以下 3 件事:
:sparkles: 1、扫描 APK,将 APK 的信息存储在 PackageParser.Package 类型的 newPackage 中,一个 Package 的信息包含了 1 个 base APK 以及 0 个或者多个 split APK 。
:sparkles: 2、更新该 APK 对应的 Settings 信息,Settings 用于保存所有包的动态设置。
:sparkles: 3、如果安装成功就为新安装的应用程序准备数据,安装失败就删除APK。
scanPackageTracedLI
调用 scanPackageTracedLI() 进行安装 :
public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
scanPackageLI - 01
scanPackageTracedLI() 调用了 scanPackageLI() 方法:
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
... ...
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
scanPackageLI - 02
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
boolean success = false;
try {
// scanPackageDirtyLI 实际安装 package 的方法
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
// DELETE_DATA_ON_FAILURES is only used by frozen paths
destroyAppDataLIF(pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
总结
本文主要讲解了 PMS 是如何处理 APK 安装的流程,主要有几个步骤:
:sparkles: PackageInstaller 安装 APK 时会将 APK 的信息交由 PMS 处理,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作。
:sparkles: PMS 发送 INIT_COPY 和 MCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService ,完成复制 APK 等工作。
:sparkles: 复制 APK 完成后,会开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作。
参考
以上所述就是小编给大家介绍的《Android 8.1 源码_核心篇 -- 深入研究 PMS 系列(6)之 APK 安装流程(PMS)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 深入理解 FilterChainProxy【源码篇】
- 深入理解 WebSecurityConfigurerAdapter【源码篇】
- 深入koa2源码
- 深入理解channel:设计+源码
- 深入浅出Semaphore源码解析
- 深入剖析Vue源码 - 组件基础
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!