1 引言
在文章Android Xapk安装(一)中,讨论了xapk
包的组成以及安装方式,并分析了使用adb install-multiple
方式安装xapk
的过程,在该文章中,我们知道,adb install-multiple
命令安装xapk
,实际上是在Android
系统中运行execc:cmd package install-create
,exec:cmd package install-write
,exec:cmd package install-commit
三个命令,而这三个命令最终会进入到PackageManagerService
的onShellCommand
方法去执行。本文接着前文,分析在PackageManagerService
中的执行过程,本文的源码基于Android 10.
2 PackageManagerService中执行过程
2.1 onShellCommand
根据前文分析,exec:cmd package
指令最后会进入到PackageManagerService
(源码位置:/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
)的onShellCommand
方法中,其源码如下:
Java
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
onShellCommand
方法中,会创建一个PackageManagerShellCommand
对象,然后调用其exec方法.PackageManagerShellCommand
源码位于于/frameworks/base/services/core/java/com/android/server/pm/
目录,PackageManagerShellCommand
继承自ShellCommand
(源码位于/frameworks/base/core/java/android/os/ShellCommand.java
),exec
方法实现在ShellCommand
类中,在该方法中,实际调用的是onCommand
方法,而该方法是由各个子类去实现,所以实际上最后代码会进入大PackageManagerShellCommand
的onCommand
方法中,该方法用来解析和PackageManagerService
有关的相关命令,如pm dump
,pm list
这些命令最终也会进入到该方法。和本次安装xapk
有关的处理代码如下
Java
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
...
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
case "install-commit":
return runInstallCommit();
case "install-create":
return runInstallCreate();
case "install-write":
return runInstallWrite();
...
}
} catch (RemoteException e) {
pw.println("Remote exception: " + e);
}
return -1;
}
从代码中可以看到,install-create
,install-write
,install-commit
这三个命令分别由runInstallCreate
,runInstallWrite
,runInstallCommit
三个方法执行,下面分别来看这三个方法
2.2 runInstallCreate
runInstallCreate
方法源码如下
Java
private int runInstallCreate() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final InstallParams installParams = makeInstallParams();
final int sessionId = doCreateSession(installParams.sessionParams,
installParams.installerPackageName, installParams.userId);
// NOTE: adb depends on parsing this string
pw.println("Success: created install session [" + sessionId + "]");
return 0;
}
在该方法中,先是构造了一个InstallParams
对象,然后使用该对象做为参数,调用doCreateSession
方法生成session
并返回sessionId
. InstallParams
是PackageManagerShellCommand
的一个内部类
Java
private static class InstallParams {
SessionParams sessionParams;
String installerPackageName;
int userId = UserHandle.USER_ALL;
}
从该类定义可以看到,InstallParams
有三个成员变量:sessionParams
:与session
有关的参数,installerPackageName
:调用安装程序或命令的应用,userId
:本次安装的应用,对哪些用户可见,默认是所有用户. 了解了InstallParam
的结构后,下面看构造InstallParam
对象的makeInstallParams
方法。在该方法中,首先创建了一个SessionParams
对象,并将安装模式设为SessionParams.MODE_FULL_INSTALL
,创建该对象后,创建一个InstallParams
对象,并将SessionParams
对象赋予其成员变量sessionParams
.在完成这两步工作后,InstallParams
对象就创建完成了。
Java
private InstallParams makeInstallParams() {
final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final InstallParams params = new InstallParams();
params.sessionParams = sessionParams;
// Whitelist all permissions by default
//PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS 是一个用于 Android 的 //PackageManager 类的常量。该常量指示安装包管理器在安装应用程序时,是否将该应用程序添加到所有白名单受限权限的白名单中,默认是添加
sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
方法剩下的部分就是为SessionParams
对象赋值,首先为本次安装的应用赋予白名单权限,然后解析传递过来的参数,执行相应的策略,回顾前文, install-create
指令:
lua
exec:cmd package install-create -S size au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk
在该指令中,有一个-S
选项参数,在本方法中,和-S
参数有关的代码如下
Java
...
switch (opt) {
....
case "-S":
final long sizeBytes = Long.parseLong(getNextArg());
if (sizeBytes <= 0) {
throw new IllegalArgumentException("Size must be positive");
}
sessionParams.setSize(sizeBytes);
break;
...
从代码中可以看到,对于选项-S
,就是把本次安装的包总大小写入到SessionParam
的size
属性中。至此,InstallParams
对象就构造完成了,注意到,在该对象的三个属性中,installerPackageName
属性为空。在完成InstallParams
对象构造的分析后,下面分析doCreateSession
方法,该方法是用来实际生成Session
并返回sessionId
:
Java
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {
userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
}
final int sessionId = mInterface.getPackageInstaller()
.createSession(params, installerPackageName, userId);
return sessionId;
}
在该方法中,首先对userId
做了一些检查,主要也还是权限方面,之后调用PackageInstallerService
的createSession
方法创建session
,并返回sessionId
。
2.2.1 PackageInstallerService#createSession
PackageInstallerService
源码位于frameworks/services/core/java/com/android/server/pm/PackageInstallerService
,createSession
源码如下:
Java
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
在createSession
中,实际调用的是createSessionInternal
方法完成具体的生成session
工作,createSessionInsternal
方法中和生成session
有关的代码如下
Java
...
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
final int activeCount = getSessionCount(mSessions, callingUid);
if (activeCount >= MAX_ACTIVE_SESSIONS) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
sessionId = allocateSessionIdLocked();
}
final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if (!params.isMultiPackage) {
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
stageDir = buildSessionDir(sessionId, params);
} else {
stageCid = buildExternalStageCid(sessionId);
}
}
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId,
installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
false, false, null, SessionInfo.INVALID_ID, false, false, false,
SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
if (params.isStaged) {
mStagingManager.createSession(session);
}
if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
}
writeSessionsAsync();
return sessionId;
}
在生成Session
时,首先会检查当前系统中活跃的sessionId
个数,有多少个sessionId
就意味着现在系统有多个在安装的应用,所以需要控制sessionId
个数,避免过多的应用同时安装拖垮系统;然后调用allocateSessionIdLocked
方法生成一个随机的sessionId
,在生成sessionId
后,调用buildSessionDir
和buindExternalStageCid
方法分别生成stageDir
和stagCid
,其中stageDir
是用来临时存放apk
文件的目录,其目录类似/data/app/vmdl{sessionId}.tmp
;在生成stageDir
和stageCid
后,创建一个PackageInstallerSession
对象作为本次安装的Session
并将其和SessionId
放在map
表mSessions
中,这样就将sessionId
和Session
关联起来了,PackageInstallerSession
的源码位于frameworks/services/core/java/com/android/server/pm/PackageInstallerSession
,在构造方法中将这些参数保存到其成员变量中;在createSessionInternal
方法的最后,调用writeSessionsAsync
方法将session
数据持久化保存到目录/data/system/install_session.xml
中,该文件保存着系统所有尚未完成安装的session
.若系统重启,PackageManagerService
启动时会从该文件中读取session
数据,继续完成安装 。 至此,install-create
就完成了session
的创建,并返回其对应的sessionId
,下面看install-write
的执行方法runInstallWrite
2.3 runInstallWrite
在执行完install-create
指令创建好session
后,接下来就是执行install-write
方法写入apk
文件, 回顾前文写入指令
perl
exec:cmd package install-write -S apkSize sessionId au.com.metrotrains.dwtd4.apk -
install-write
指令对应的执行方法就是runInstallWrite
方法:
Java
private int runInstallWrite() throws RemoteException {
long sizeBytes = -1;
String opt;
while ((opt = getNextOption()) != null) {
if (opt.equals("-S")) {
sizeBytes = Long.parseLong(getNextArg());
} else {
throw new IllegalArgumentException("Unknown option: " + opt);
}
}
final int sessionId = Integer.parseInt(getNextArg());
final String splitName = getNextArg();
final String path = getNextArg();
return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
}
在该方法中,首先获取install-write
命令携带的-S
选项,该选项携带了本次写入apk
文件的大小,而且该参数是必须的,获取到该参数后,再依次获取本次操作的sessionId
和apk
文件的路径,获取到这些参数后,调用doWriteSplit
方法完成写入:
Java
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
try {
final PrintWriter pw = getOutPrintWriter();
//1. 根据输入路径,打开对应的文件,其中STDIN_PATH为'-''
final ParcelFileDescriptor fd;
if (STDIN_PATH.equals(inPath)) {
fd = ParcelFileDescriptor.dup(getInFileDescriptor());
} else if (inPath != null) {
fd = openFileForSystem(inPath, "r");
if (fd == null) {
return -1;
}
sizeBytes = fd.getStatSize();
if (sizeBytes < 0) {
getErrPrintWriter().println("Unable to get size of: " + inPath);
return -1;
}
} else {
fd = ParcelFileDescriptor.dup(getInFileDescriptor());
}
//2. 检查本次写入的文件的大小
if (sizeBytes <= 0) {
getErrPrintWriter().println("Error: must specify a APK size");
return 1;
}
//3. 根据seesionId打开对应的PackageInstallerSession ,并调用其write方法
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
session.write(splitName, 0, sizeBytes, fd);
if (logSuccess) {
pw.println("Success: streamed " + sizeBytes + " bytes");
}
return 0;
} catch (IOException e) {
getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
return 1;
} finally {
IoUtils.closeQuietly(session);
}
}
可以看到,该方法主要分为3步:
- 根据指令传入的
apk
文件写入路径,打开对应的文件,在上文中的指令中,传入的写入文件路径为-
,其对应的就是STDIN_PATH
,即写入标准写入中, - 检查本次写入的
apk
文件大小,写入的大小应该大于0 - 根据传入的
seesionId
,打开对应的Session
,并调用其write
方法完成写入Session
在系统中对应的是PackageInstallerSession
对象,所以实际上就是调用PackageInstallerSession
的write
方法。下面进入到PackageInstallerSession
中,分析其write
方法
2.3.1 PackageInstallerSession#write
write
方法源码如下
Java
@Override
public void write(String name, long offsetBytes, long lengthBytes,
ParcelFileDescriptor fd) {
try {
doWriteInternal(name, offsetBytes, lengthBytes, fd);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
在write
方法中,实际上是调用doWriteInternal
方法完成实际的写入操作:
Java
private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
ParcelFileDescriptor incomingFd) throws IOException {
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
final RevocableFileDescriptor fd;
final FileBridge bridge;
final File stageDir;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("openWrite");
//1.获取写入安装包的stageDir,该值在创建session时创建,其路径类似/data/app/vmdl{sessionId}.tmp
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd = new RevocableFileDescriptor();
bridge = null;
mFds.add(fd);
} else {
fd = null;
bridge = new FileBridge();
mBridges.add(bridge);
}
stageDir = resolveStageDirLocked();
}
try {
// Use installer provided name for now; we always rename later
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
//2. 创建要写入的目标文件,一般就是stageDir/apkName.apk
final File target;
final long identity = Binder.clearCallingIdentity();
try {
target = new File(stageDir, name);
Slog.i("InstallTrace","PackageInstallerSeiion#dowriteInternal write apk in session: targetPath="+target.getAbsolutePath());
} finally {
Binder.restoreCallingIdentity(identity);
}
// TODO: this should delegate to DCS so the system process avoids
// holding open FDs into containers.
final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
Os.chmod(target.getAbsolutePath(), 0644);
// If caller specified a total length, allocate it for them. Free up
// cache space to grow, if needed.
if (stageDir != null && lengthBytes > 0) {
mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
PackageHelper.translateAllocateFlags(params.installFlags));
}
if (offsetBytes > 0) {
Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
}
if (incomingFd != null) {
switch (Binder.getCallingUid()) {
case android.os.Process.SHELL_UID:
case android.os.Process.ROOT_UID:
case android.os.Process.SYSTEM_UID:
break;
default:
throw new SecurityException(
"Reverse mode only supported from shell or system");
}
// In "reverse" mode, we're streaming data ourselves from the
// incoming FD, which means we never have to hand out our
// sensitive internal FD. We still rely on a "bridge" being
// inserted above to hold the session active.
try {
final Int64Ref last = new Int64Ref(0);
//3.将apk文件从标准输入(STDIN_FIENO)copy到目标文件中
FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
Runnable::run, (long progress) -> {
if (params.sizeBytes > 0) {
final long delta = progress - last.value;
last.value = progress;
addClientProgress((float) delta / (float) params.sizeBytes);
}
});
} finally {
IoUtils.closeQuietly(targetFd);
IoUtils.closeQuietly(incomingFd);
// We're done here, so remove the "bridge" that was holding
// the session active.
synchronized (mLock) {
if (PackageInstaller.ENABLE_REVOCABLE_FD) {
mFds.remove(fd);
} else {
bridge.forceClose();
mBridges.remove(bridge);
}
}
}
return null;
} else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
fd.init(mContext, targetFd);
return fd.getRevocableFileDescriptor();
} else {
bridge.setTargetFile(targetFd);
bridge.start();
return new ParcelFileDescriptor(bridge.getClientSocket());
}
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
可以看到,在该方法中,主要分为三步
- 获取写入安装包的临时目录
stageDir
该目录在install-create
阶段生成,一般就是/data/app/vmdl{sessionId}.tmp
目录 - 创建要写入的目录文件,一般就是
stageDir/fileName.apk
- 调用
FileUtils.copy
方法将apk
文件从标准输入中copy
到目标文件中,完成本次的intall-write
在对所有的apk
执行上述操作后,本次安装的xapk
包就完成了install-write
操作,下面分析install-commit
的执行方法runInstallCommit
2.4 runInstallCommit
在写入apk
文件成功后,执行install-commit
指令提交本次安装
Java
private int runInstallCommit() throws RemoteException {
final int sessionId = Integer.parseInt(getNextArg());
return doCommitSession(sessionId, true /*logSuccess*/);
}
在runInstallCommit
方法中,实际是调用doCommitSession
完成commit
操作而在doCommitSession
中,实际上是调用PacakgeInstallerSession
的commit
方法并返回结果:
Java
private int doCommitSession(int sessionId, boolean logSuccess)
throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
if (!session.isMultiPackage() && !session.isStaged()) {
// Sanity check that all .dm files match an apk.
// (The installer does not support standalone .dm files and will not process them.)
try {
DexMetadataHelper.validateDexPaths(session.getNames());
} catch (IllegalStateException | IOException e) {
pw.println(
"Warning [Could not validate the dex paths: " + e.getMessage() + "]");
}
}
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
if (logSuccess) {
pw.println("Success");
}
} else {
pw.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
return status;
} finally {
IoUtils.closeQuietly(session);
}
}
下面进入到PacakgeInstallerSession
中继续分析commit
方法
2.4.1 PackageInstallerSession#commit
commit
源码如下
Java
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
if (mIsPerfLockAcquired && mPerfBoostInstall != null) {
mPerfBoostInstall.perfLockRelease();
mIsPerfLockAcquired = false;
}
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
+ mParentSessionId + " and may not be committed directly.");
}
//做一些准备工作,校验一下安装包,然后将应用标记为已提交
if (!markAsCommitted(statusReceiver, forTransfer)) {
return;
}
//判断是否是安装多个ap,adb install-multiple_packages命令,该值为true
//adb install-mulitple命令安装的还是一个apk,只是这个apk由多个子包构成,所以该值为false
if (isMultiPackage()) {
final SparseIntArray remainingSessions = mChildSessionIds.clone();
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
RuntimeException commitException = null;
boolean commitFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
try {
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
.markAsCommitted(childIntentSender, forTransfer)) {
commitFailed = true;
}
} catch (RuntimeException e) {
commitException = e;
}
}
if (commitException != null) {
throw commitException;
}
if (commitFailed) {
return;
}
}
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}
在该方法中,首先会调用markAsCommitted
方法对需要安装的应用做一些校验,主要是一些参数检查工作,并且在该阶段,会解析出安装的应用的包名并赋值给当前PackageInstallerSession
的mPackageName
属性,markAsComitted
方法经过一系列调用后,最终会调用validateApkInstallLocked
,该方法用来检测安装应用的各个参数,并完成一些安装的准备工作.该方法较长,下面只列出本次安装需要关注的地方:
Java
private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
throws PackageManagerException {
ApkLite baseApk = null;
mPackageName = null;
mVersionCode = -1;
mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
...
final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
for (File addedFile : addedFiles) {
final ApkLite apk;
try {
//1.解析出apk的ApkLite
apk = PackageParser.parseApkLite(
addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
if (!stagedSplits.add(apk.splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + apk.splitName + " was defined multiple times");
}
// Use first package to define unknown values
//2. 获取apk的packageName和versionCode,对于xapk,每一个子apk的包的packName,versionCode都是一样的
if (mPackageName == null) {
mPackageName = apk.packageName;
mVersionCode = apk.getLongVersionCode();
}
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
mSigningDetails = apk.signingDetails;
}
assertApkConsistentLocked(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
final String targetName;
//3.主包改名为base.apk,非主包前面加上split_前缀
if (apk.splitName == null) {
targetName = "base" + APK_FILE_EXTENSION;
} else {
targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
}
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + targetName);
}
final File targetFile = new File(mResolvedStageDir, targetName);
resolveAndStageFile(addedFile, targetFile);
// Base is coming from session
//4.将主包(base包保存下来)
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
baseApk = apk;
}
....
}
}
在该方法中,会遍历stagedDir
,即写入apk文件的目录,对每一个apk做如下操作:
- 解析出apk的ApkLite
- 获取apk的packageName和versionCode,对于xapk,每一个子apk的包的packName,versionCode都是一样的
- 主包改名为base.apk,非主包前面加上split_前缀
- 如果是主包,则将主包路径保存下来 从代码中可以看到,关键的步骤是第1步,即调用
packageParser.parseApkLite
方法生成ApkLite
对象,ApkLite
位于源码/frameworks/base/core/java/android/content/pm/PackageParser.java
主要是存放AndroidMainfest.xml
文件中跟安装应用有关的属性,如packageName
,versionCode
等
java
public static class ApkLite {
public final String codePath;
public final String packageName;
public final String splitName;
public boolean isFeatureSplit;
public final String configForSplit;
public final String usesSplitName;
public final int versionCode;
public final int versionCodeMajor;
public final int revisionCode;
public final int installLocation;
public final int minSdkVersion;
public final int targetSdkVersion;
public final VerifierInfo[] verifiers;
public final SigningDetails signingDetails;
public final boolean coreApp;
public final boolean debuggable;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
public final boolean isSplitRequired;
public final boolean useEmbeddedDex;
}
在完成应用的参数检查后,下一步判断是否是MutilPackage
,即批量安装多个应用的场景,一般是false
,所以在该方法中,实际上是向Handler
中发送一个MSG_COMMIT
的Messgae
,在Handler
中处理该消息的代码如下
Java
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_COMMIT:
handleCommit();
break;
//省略其他case
}
return true;
}
};
在处理MSG_COMMIT
时会调用handleCommit
方法,该方法源码如下
Java
private void handleCommit() {
//设备管理和设备安全相关,确认当前用户是否有权限安装应用
if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallerPackageName)
.write();
}
//该apk是否分段传输,如果分段传输,需要指定--staged选项,一般不用
if (params.isStaged) {
mStagingManager.commitSession(this);
destroyInternal();
dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
return;
}
//是否安装apex
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
destroyInternal();
dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"APEX packages can only be installed using staged sessions.", null);
return;
}
// For a multiPackage session, read the child sessions
// outside of the lock, because reading the child
// sessions with the lock held could lead to deadlock
// (b/123391593).
//多应用安装场景,获取所有的chidlSessions,单应用安装不用关注
List<PackageInstallerSession> childSessions = getChildSessions();
try {
synchronized (mLock) {
//提交所有的session
commitNonStagedLocked(childSessions);
}
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
}
在该方法中,做一些必要的校验后,如果是安装非stag
应该和非apex
,最后会进入到commitNonStagedLocked
方法中方法中,对于单应用来说,childSessions
参数为null
,下面看commitNonStagedLocked
方法
Java
@GuardedBy("mLock")
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
//1.创建一个committingSession
final PackageManagerService.ActiveInstallSession committingSession =
makeSessionActiveLocked();
if (committingSession == null) {
return;
}
//处理多应用情况
if (isMultiPackage()) {
Slog.i("InstallTrace", " PackageInstallerSeiion#commitNonStagedLocked commit session: isMultiPackage");
List<PackageManagerService.ActiveInstallSession> activeChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
PackageManagerException failure = null;
for (int i = 0; i < childSessions.size(); ++i) {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession activeSession =
session.makeSessionActiveLocked();
if (activeSession != null) {
activeChildSessions.add(activeSession);
}
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
try {
mRemoteObserver.onPackageInstalled(
null, failure.error, failure.getLocalizedMessage(), null);
} catch (RemoteException ignored) {
}
return;
}
mPm.installStage(activeChildSessions);
} else {
//2.调用instllStage方法
mPm.installStage(committingSession);
}
}
在该方法中,实际上就做了两件事:
- 调用
makeSessionActiveLocked
方法生成ActiveInstallSession
对象 - 调用
PackageManagerService
的installStage
方法完成安装
下面先看makeSessionActiveLocked
方法
Java
@GuardedBy("mLock")
private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
throws PackageManagerException {
if (mRelinquished) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session relinquished");
}
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
if (!mSealed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
}
final IPackageInstallObserver2 localObserver;
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
localObserver = null;
} else {
Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: isMultipackage="+params.isMultiPackage);
if (!params.isMultiPackage) {
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSigningDetails);
Preconditions.checkNotNull(mResolvedBaseFile);
//1.是否还需要询问授权安装,对于adb安装一般不用
if (needToAskForPermissionsLocked()) {
// User needs to confirm installation;
// give installer an intent they can use to involve
// user.
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
try {
mRemoteObserver.onUserActionRequired(intent);
} catch (RemoteException ignored) {
}
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
closeInternal(false);
return null;
}
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
//2.安装mode是否继承已有的package,adb install 一般是SessionParams.MODE_FULL_INSTALL
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = resolveStageDirLocked();
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
throw new IllegalStateException("mInheritedFilesBase == null");
}
if (isLinkPossible(fromFiles, toDir)) {
if (!mResolvedInstructionSets.isEmpty()) {
final File oatDir = new File(toDir, "oat");
createOatDirs(mResolvedInstructionSets, oatDir);
}
// pre-create lib dirs for linking if necessary
if (!mResolvedNativeLibPaths.isEmpty()) {
for (String libPath : mResolvedNativeLibPaths) {
// "/lib/arm64" -> ["lib", "arm64"]
final int splitIndex = libPath.lastIndexOf('/');
if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
Slog.e(TAG,
"Skipping native library creation for linking due"
+ " to invalid path: " + libPath);
continue;
}
final String libDirPath = libPath.substring(1, splitIndex);
final File libDir = new File(toDir, libDirPath);
if (!libDir.exists()) {
NativeLibraryHelper.createNativeLibrarySubdir(libDir);
}
final String archDirPath = libPath.substring(splitIndex + 1);
NativeLibraryHelper.createNativeLibrarySubdir(
new File(libDir, archDirPath));
}
}
linkFiles(fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
copyFiles(fromFiles, toDir);
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to inherit existing install", e);
}
}
// TODO: surface more granular state from dexopt
mInternalProgress = 0.5f;
computeProgressLocked(true);
// Unpack native libraries
//3. 提取出lib库
Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: mResolvedStageDir="+mResolvedStageDir+"params.abi="+params.abiOverride);
extractNativeLibraries(mResolvedStageDir, params.abiOverride,
mayInheritNativeLibs());
}
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
localObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
throw new IllegalStateException();
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
destroyInternal();
dispatchSessionFinished(returnCode, msg, extras);
}
};
}
//4 确定use,一般是UserHandle.ALL
final UserHandle user;
if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
//5.返回AcitveInstallSession对象
mRelinquished = true;
Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: mPackageName="+mPackageName+" stageDir="+stageDir+" mInstallerPackageName=" + mInstallerPackageName+" uid="+ mInstallerUid);
return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
localObserver, params, mInstallerPackageName, mInstallerUid, user,
mSigningDetails);
}
在makeSessionActiveLocked
方法中,主要有下面5个步骤
- 是否还需要询问授权安装,对于
adb
安装一般不用 - 安装
mode
是否继承已有的package
,adb install
一般是SessionParams.MODE_FULL_INSTALL
- 提取出
lib
库 - 确定
use
,一般是UserHandle.ALL
- 返回
AcitveInstallSession
对象
这5个步骤中,1,2是进行安装前的一些检查,4是确认应用的用户权限,5是返回生成的ActiveInstallSession
对象,这四个步骤都比较简单,比较重要的是第3步,提取应用的lib库,因为xapk
的lib库和apk
应用的lib库提取有一些差异,这部分内容放在下文单独分析,我们先继续看commitNonStagedLocked
方法。
在调用makeSessionActiveLocked
生成ActiveInstallSession
后,commitNonStagedLocked
方法下一步调用PackageManagerService
的installStage
完成应用安装。在Android 10中,无论是在应用商店下载应用自动安装,还是下载apk手动安装,亦或是使用pm install
或adb install
这些命令的方式安装,最终都会调用PackageManagerService#installStage
方法完成应用安装,所以该方法不只是和xapk
安装有关系。对于该方法的分析,我们放在下一篇中单独分析。
在进入下一篇分析installStage
方法时,先看看xapk
中比较重要的提取lib
库的方法:PackageInstallSession
中的extractNativeLibraries
方法
3 Android应用提取lib库
3.1 Android 应用提取lib库整体流程
在android应用,尤其是游戏中,一般会包含许多的动态so库,若这些游戏是apk形式的安装包,其so库一般会打包到apk的lib目录下,在安装时,会将这些so库从apk中提取到游戏的lib目录下。而对于包含多个apk的xapk游戏安装包,其so库可能会和普通apk游戏安装包一样,将so库打包到xapk的主apk中,这样使用adb multiple-install
安装时,会将so库解析到应用的lib目录;而更常见的情况是,应用会将so库打包成一个单独的apk,在运行游戏时,会将这个apk加载到内存中,以此来完成so库动态加载,对于这种情况,一般是不能将so库解压到应用的lib目录。这样,对于xapk的应用在安装时,就需要考虑是否将so库抽取到lib目录下,前文提到,在安装应用时,会调用PackageInstallSession
中的extractNativeLibraries
方法提取so库,下面看看该方法代码:
Java
private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
throws PackageManagerException {
//libDir:即lib目录
final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
packageDir.getAbsolutePath()+";abioverride="+abiOverride+";inherit="+inherit);
//inherit一般为false
if (!inherit) {
// Start from a clean slate
NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
}
NativeLibraryHelper.Handle handle = null;
try {
//根据packageDir目录生成对应的Handle
handle = NativeLibraryHelper.Handle.create(packageDir);
//解析so库到lib目录
final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
abiOverride);
if (res != PackageManager.INSTALL_SUCCEEDED) {
throw new PackageManagerException(res,
"Failed to extract native libraries, res=" + res);
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed to extract native libraries", e);
} finally {
IoUtils.closeQuietly(handle);
}
}
从代码中可以看到,该方法主要在于NativeLibraryHelper.Handle.create
和 NativeLibraryHelper.copyNativeBinariesWithOverride
这两个方法的逻辑上,NativeLibraryHelper
源码位于frameworks/base/core/java/com/android/internal/content/NativeLibraryHelper.java
,Handle
是其一个内部类,该类的create
方法为:
java
public static class Handle implements Closeable {
private final CloseGuard mGuard = CloseGuard.get();
private volatile boolean mClosed;
final long[] apkHandles;
final boolean multiArch;
final boolean extractNativeLibs;
final boolean debuggable;
public static Handle create(File packageFile) throws IOException {
try {
final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
return create(lite);
} catch (PackageParserException e) {
throw new IOException("Failed to parse package: " + packageFile, e);
}
}
....
在该方法中,会先调用PackageParser.parsePackageLite
方法,该方法和前文的packageParser.parseApkLite
类似,会生成一个PackageLite
对象,和ApkLite
用来记录一个apk
的关键信息不同,PackageLite
用来记录整个安装包的关键信息,包括该安装包有多少个apk等信息:
Java
/**
* Lightweight parsed details about a single package.
*/
public static class PackageLite {
@UnsupportedAppUsage
public final String packageName;
public final int versionCode;
public final int versionCodeMajor;
@UnsupportedAppUsage
public final int installLocation;
public final VerifierInfo[] verifiers;
/** Names of any split APKs, ordered by parsed splitName */
public final String[] splitNames;
/** Names of any split APKs that are features. Ordered by splitName */
public final boolean[] isFeatureSplits;
/** Dependencies of any split APKs, ordered by parsed splitName */
public final String[] usesSplitNames;
public final String[] configForSplit;
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
public final String codePath;
/** Path of base APK */
public final String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
/** Revision code of base APK */
public final int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
public final int[] splitRevisionCodes;
public final boolean coreApp;
public final boolean debuggable;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
在生成PackageLite
对象后,再调用其他create
方法完成Handle创建,最终会将Handle的系统属性由PackageLite
对象中获取到,比如其extractNativeLibs
属性就是PackageLite
对象的extractNativeLibs
属性。其apkHandles
就是PackageLite
对象中保存的所有apk文件路径的文件描述符. 在生成了Handle
对象后,下一步就是调用NativeLibraryHelper.copyNativeBinariesWithOverride
方法完成so库的拷贝,在该方法中,最终会调用copyNativeBinaries
方法完成so库拷贝
Java
/**
* Copies native binaries to a shared library directory.
*
* @param handle APK file to scan for native libraries
* @param sharedLibraryDir directory for libraries to be copied to
* @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
* error code from that class if not
*/
public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
for (long apkHandle : handle.apkHandles) {
int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
handle.extractNativeLibs, handle.debuggable);
if (res != INSTALL_SUCCEEDED) {
return res;
}
}
return INSTALL_SUCCEEDED;
}
在该方法中,会调用nativeCopyNativeBinaries
方完成拷贝操作,该方法中的参数 handle.extractNativeLibs
是用来标识是否需要提取so库,其值即为前文说的PackageLite
对象的extractNativeLibs
属性值。nativeCopyNativeBinaries
是一个JNI
方法,其实现位于/frameworks/base/core/jni/com_android_internal_content_NativeLibraryHelper.cpp#com_android_internal_content_NativeLibraryHelper_copyNativeBinaries
,在该方法中,最终会调用其该类的copyFileIfChanged
方法,下面列出来该方法部分代码
cpp
copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
void** args = reinterpret_cast<void**>(arg);
jstring* javaNativeLibPath = (jstring*) args[0];
jboolean extractNativeLibs = *(jboolean*) args[1];
ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
uint32_t uncompLen;
uint32_t when;
uint32_t crc;
uint16_t method;
off64_t offset;
if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
ALOGE("Couldn't read zip entry info\n");
return INSTALL_FAILED_INVALID_APK;
}
//如果extractNativeLibs属性为false,则只是做一些校验然后返回,不会copy so库
if (!extractNativeLibs) {
// check if library is uncompressed and page-aligned
if (method != ZipFileRO::kCompressStored) {
ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
fileName);
return INSTALL_FAILED_INVALID_APK;
}
if (offset % PAGE_SIZE != 0) {
ALOGE("Library '%s' is not page-aligned - will not be able to open it directly from"
" apk.\n", fileName);
return INSTALL_FAILED_INVALID_APK;
}
return INSTALL_SUCCEEDED;
}
//extractNativeLibs属性为true,解析所有apk的so库
// Build local file path
const size_t fileNameLen = strlen(fileName);
char localFileName[nativeLibPath.size() + fileNameLen + 2];
在该方法中,会判断extractNativeLibs
参数的值,该参数就是PackageLite
对象中的extractNativeLibs
属性值,若该值为true,则解析所有apk文件的so库,若该值为false,则不解析so库
至此,Android 应用提取so库的逻辑就分析完成了,总结起来就是:根据生成的PackageLite
对象的extractNativeLibs
属性来判断是否需要将应用的so库解析到lib目录,若该值为true,则解析到lib目录,若该值为false,则不解析。下面我们继续分析PackageLite
对象的创建过程。
3.2 PackageLite
对象创建过程
前文提到,create
方法中的PackageLite
对象是通过PackageParser#parsePackageLite
方法得到的,该方法源码如下
Java
public static PackageLite parsePackageLite(File packageFile, int flags)
throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackageLite(packageFile, flags);
} else {
return parseMonolithicPackageLite(packageFile, flags);
}
}
若packageFile
参数不是一个目录(即是一个apk文件),则执行parseMonolithicPackageLite
方法:
Java
private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
throws PackageParserException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
}
可以看到,在该方法中,就是调用parseApkLite
解析apk文件,然后利用该apkLite
生成一个PackageLite
对象。 若packageFile
参数是一个目录,则执行方法parseClusterPackageLite
方法,对于xapk
来说,该参数一般就是一个目录,下面看该方法代码
Java
static PackageLite parseClusterPackageLite(File packageDir, int flags)
throws PackageParserException {
//1.获取该目录下所有apk文件
final File[] files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
String packageName = null;
int versionCode = 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
for (File file : files) {
//2.遍历apk文件
if (isApkFile(file)) {
//3.调用parseApkLite生成ApkLite
final ApkLite lite = parseApkLite(file, flags);
// Assert that all package names and version codes are
// consistent with the first one we encounter.
if (packageName == null) {
packageName = lite.packageName;
versionCode = lite.versionCode;
} else {
//4.检查packageName和version,对于xapk包,每一个apk的packageName和versionCode应该一致
if (!packageName.equals(lite.packageName)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent package " + lite.packageName + " in " + file
+ "; expected " + packageName);
}
if (versionCode != lite.versionCode) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent version " + lite.versionCode + " in " + file
+ "; expected " + versionCode);
}
}
//5.将splitName和对应的ApkLite放入map表,注意,主包的splitName为null
// Assert that each split is defined only once
if (apks.put(lite.splitName, lite) != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Split name " + lite.splitName
+ " defined more than once; most recent was " + file);
}
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
//6. 移除掉主包,并报错主包的ApkLite
final ApkLite baseApk = apks.remove(null);
if (baseApk == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
}
// Always apply deterministic ordering based on splitName
final int size = apks.size();
//7.获取splitNames等参数
String[] splitNames = null;
boolean[] isFeatureSplits = null;
String[] usesSplitNames = null;
String[] configForSplits = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
String[] splitClassLoaderNames = null;
if (size > 0) {
splitNames = new String[size];
isFeatureSplits = new boolean[size];
usesSplitNames = new String[size];
configForSplits = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
splitNames = apks.keySet().toArray(splitNames);
Arrays.sort(splitNames, sSplitNameComparator);
for (int i = 0; i < size; i++) {
final ApkLite apk = apks.get(splitNames[i]);
usesSplitNames[i] = apk.usesSplitName;
isFeatureSplits[i] = apk.isFeatureSplit;
configForSplits[i] = apk.configForSplit;
splitCodePaths[i] = apk.codePath;
splitRevisionCodes[i] = apk.revisionCode;
}
}
//8.获取codePath,即packageDir的绝对路径(stagedDir的路径)
final String codePath = packageDir.getAbsolutePath();
//9.调用对应的构造方法生成PackageLite对象
return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
configForSplits, splitCodePaths, splitRevisionCodes);
}
如代码注释所示,该方法主要有9个步骤,在这9个步骤中,也包含调用parseApkLite
方法生成ApkLite
对象的步骤,下面先看看该方法:
Java
public static ApkLite parseApkLite(File apkFile, int flags)
throws PackageParserException {
return parseApkLiteInner(apkFile, null, null, flags);
}
private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName,
int flags) throws PackageParserException {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
XmlResourceParser parser = null;
ApkAssets apkAssets = null;
try {
try {
apkAssets = fd != null
? ApkAssets.loadFromFd(fd, debugPathName, false, false)
: ApkAssets.loadFromPath(apkPath);
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
//读取apk的AndroidManifest.xml文件
parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package((String) null);
final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
collectCertificates(tempPkg, apkFile, skipVerify);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
signingDetails = tempPkg.mSigningDetails;
} else {
signingDetails = SigningDetails.UNKNOWN;
}
final AttributeSet attrs = parser;
//解析AndroidManifest.xml文件中属性
return parseApkLite(apkPath, parser, attrs, signingDetails);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
if (apkAssets != null) {
try {
apkAssets.close();
} catch (Throwable ignored) {
}
}
// TODO(b/72056911): Implement AutoCloseable on ApkAssets.
}
}
在parseApkLite
方法中,实际调用的是parseApkLiteInner
,在该方法中,会读取该apk文件的AndroidManifest.xml
文件,然后调用重载的parseApkLite
方法生成ApkLite
对象:
Java
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
SigningDetails signingDetails)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int versionCodeMajor = 0;
int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
boolean isSplitRequired = false;
boolean useEmbeddedDex = false;
String configForSplit = null;
String usesSplitName = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("versionCodeMajor")) {
versionCodeMajor = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("revisionCode")) {
revisionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("coreApp")) {
coreApp = attrs.getAttributeBooleanValue(i, false);
} else if (attr.equals("isolatedSplits")) {
isolatedSplits = attrs.getAttributeBooleanValue(i, false);
} else if (attr.equals("configForSplit")) {
configForSplit = attrs.getAttributeValue(i);
} else if (attr.equals("isFeatureSplit")) {
isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
} else if (attr.equals("isSplitRequired")) {
isSplitRequired = attrs.getAttributeBooleanValue(i, false);
}
}
// Only search the tree when the tag is the direct child of <manifest> tag
int type;
final int searchDepth = parser.getDepth() + 1;
final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getDepth() != searchDepth) {
continue;
}
if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
final VerifierInfo verifier = parseVerifier(attrs);
if (verifier != null) {
verifiers.add(verifier);
}
} else if (TAG_APPLICATION.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("debuggable".equals(attr)) {
debuggable = attrs.getAttributeBooleanValue(i, false);
}
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
}
if ("use32bitAbi".equals(attr)) {
use32bitAbi = attrs.getAttributeBooleanValue(i, false);
}
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
if ("useEmbeddedDex".equals(attr)) {
useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
}
}
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
continue;
}
usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name");
if (usesSplitName == null) {
throw new PackageParserException(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"<uses-split> tag requires 'android:name' attribute");
}
} else if (TAG_USES_SDK.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("targetSdkVersion".equals(attr)) {
targetSdkVersion = attrs.getAttributeIntValue(i,
DEFAULT_TARGET_SDK_VERSION);
}
if ("minSdkVersion".equals(attr)) {
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
}
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
minSdkVersion, targetSdkVersion);
}
可以看到,在该方法中就是解析AndroidManifest.xml
的主要属性,然后使用这个属性值生成ApkLite
属性,其中需要注意这些属性的默认值,其中extractNativeLibs
属性的默认值为true
,表示需要将动态so库解析到lib目录,若该值为false
,则表示不能把so库解析到lib目录。
在分析了parseApkLite
方法后,继续看parseClusterPackageLite
,在该方法的最后,会调用PackageLite
的构造方法生成一个PackageLite
的对象
Java
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.versionCodeMajor = baseApk.versionCodeMajor;
this.installLocation = baseApk.installLocation;
this.verifiers = baseApk.verifiers;
this.splitNames = splitNames;
this.isFeatureSplits = isFeatureSplits;
this.usesSplitNames = usesSplitNames;
this.configForSplit = configForSplit;
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
this.baseRevisionCode = baseApk.revisionCode;
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.debuggable = baseApk.debuggable;
this.multiArch = baseApk.multiArch;
this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
this.isolatedSplits = baseApk.isolatedSplits;
}
在该构造方法中,主要关注属性extractNativeLibs
,该属性是主包(baseApk
)的extractNativeLibs
属性的值。该值默认是ture,若安装包的主包的AndroidManifest.xml
文件中包含android:extractNativeLibs
属性值,则使用其值,若不包含该属性,则使用默认值true。 至此,我们就明白了PackageLite
对象的extractNativeLibs
属性是怎么来的了,其实就是解析主包的AndroidManifest.xml
文件的android:extractNativeLibs
属性值,若不包含该属性,则默认为true.在只包含一个apk的应用中(普通apk包或apk+obb形式的xapk包),其主包的AndroidManifest.xml
文件中一般不会包含android:extractNativeLibs
属性,而对于多个apk组成的xapk安装包,其主包的AndroidManifest.xml
中一般都会明确指定android:extractNativeLibs
属性的值。
3.3 总结
通过对extractNativeLibraries
方法的分析,我们得出了下面的结论:
- 若安装包主包的
AndroidManifest.xml
文件中不包含android:extractNativeLibs
属性或该属性值为true,系统会解析出so库到lib目录 - 若安装包主包的
AndroidManifest.xml
文件中包含android:extractNativeLibs
属性且该属性为false,系统不会解析so库到lib目录
分析完了解析so库的逻辑,adb install-multiply
的三个核心方法runInstallCreate
, runInstallWrite
, runInstallCommit
三个方法整体流程就分析完成了,在下一篇,我们将继续分析PackageManagerService#installStage
方法,该方法完成最终的安装操作