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
办法中,其源码如下:
@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
有关的处理代码如下
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
办法源码如下
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
的一个内部类
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
方针就创立完结了。
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
指令:
exec:cmd package install-create -S size au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk
在该指令中,有一个-S
选项参数,在本办法中,和-S
参数有关的代码如下
...
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
:
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
源码如下:
@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
有关的代码如下
...
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
文件,
回忆前文写入指令
exec:cmd package install-write -S apkSize sessionId au.com.metrotrains.dwtd4.apk -
install-write
指令对应的履行办法便是runInstallWrite
办法:
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
办法完结写入:
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
办法源码如下
@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
办法完结实践的写入操作:
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
指令提交本次装置
private int runInstallCommit() throws RemoteException {
final int sessionId = Integer.parseInt(getNextArg());
return doCommitSession(sessionId, true /*logSuccess*/);
}
在runInstallCommit
办法中,实践是调用doCommitSession
完结commit
操作而在doCommitSession
中,实践上是调用PacakgeInstallerSession
的commit
办法并回来成果:
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
源码如下
@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
,该办法用来检测装置运用的各个参数,并完结一些装置的准备作业.该办法较长,下面只列出本次装置需求重视的当地:
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
等
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
中处理该消息的代码如下
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
办法,该办法源码如下
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
办法
@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
办法
@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库,下面看看该办法代码:
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
办法为:
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等信息:
/**
* 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库复制
/**
* 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
办法,下面列出来该办法部分代码
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
办法得到的,该办法源码如下
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
办法:
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
来说,该参数一般便是一个目录,下面看该办法代码
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
方针的过程,下面先看看该办法:
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
方针:
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
的方针
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
办法,该办法完结终究的装置操作