由以上代码可知,`PKMS` 扫描了很多目录,下面列举几个重点说明:
✨ /system/framework :该目录中的文件都是系统库,例如:framework.jar、services.jar、framework-res.apk 等。不过 scanDirTracedLI 只扫描 APK 文件,所以 framework-res.apk 是该目录中唯一被扫描的文件。
✨ /system/app :该目录下全是默认的系统应用。例如:Browser.apk、SettingsProvider.apk 等。
✨ /vendor/app :该目录中的文件由厂商提供,即全是厂商特定的 APK 文件。
### 4.2.1 scanDirTracedLI
`PKMS` 扫描目录统一调用 `scanDirTracedLI` 方法:
```java {.line-numbers}
public void scanDirTracedLI(File scanDir,
final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
```
重点转移到 `scanDirLI` 方法:
```java {.line-numbers}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
...
// ParallelPackageParser 是 Android O 新增的一个类,可以理解它其实就是一个队列,
// 收集系统 apk 文件,然后从这个队列里面一个个取出 apk ,调用 PackageParser 解析!
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
// 过滤掉非 apk 文件,如果不是则跳过继续扫描
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
/*
* Android P 调用 scanPackageChildLI,
* Android O 调用 scanPackageLI,
*
* 调用 scanPackageChildLI 方法扫描一个特定的 apk 文件,
* 返回值是 PackageParser 的内部类 Package,
* 该类的实例代表一个 APK 文件,所以它就是和 apk 文件对应的数据结构。
*/
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParser.PackageParserException) {
PackageParser.PackageParserException e = (PackageParser.PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// Delete invalid userdata apps
// 如果是非系统 apk 并且解析失败
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
// 非系统 Package 扫描失败,删除文件
removeCodePathLI(parseResult.scanFile);
}
}
}
}
```
看下 `ParallelPackageParser` 这个类:
> frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java
```java {.line-numbers}
/**
* Helper class for parallel parsing of packages using {@link PackageParser}.
* <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
* At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
*/
class ParallelPackageParser implements AutoCloseable {
private static final int QUEUE_CAPACITY = 10;
private static final int MAX_THREADS = 4;
private final String[] mSeparateProcesses;
private final boolean mOnlyCore;
private final DisplayMetrics mMetrics;
private final File mCacheDir;
private final PackageParser.Callback mPackageParserCallback;
private volatile String mInterruptedInThread;
// BlockingQueue 是一个 FIFO(先进先出)的 Queue(队列),是设计用来实现生产者-消费者队列的。
// 它提供了可阻塞的插入和移除方法,当队列容器已满,生产者线程会被阻塞,直到队列未满;
// 当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。
// ArrayBlockingQueue 是由数组实现的有界阻塞队列,创建的时候需要指定 capacity(容量,可以存储的最大的元素个数,因为它不会自动扩容)以及是否为公平锁。
private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
// 线程池
private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS,
"package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
mSeparateProcesses = separateProcesses;
mOnlyCore = onlyCoreApps;
mMetrics = metrics;
mCacheDir = cacheDir;
mPackageParserCallback = callback;
}
static class ParseResult {
PackageParser.Package pkg; // Parsed package
File scanFile; // File that was parsed
Throwable throwable; // Set if an error occurs during parsing
@Override
public String toString() {
return "ParseResult{" +
"pkg=" + pkg +
", scanFile=" + scanFile +
", throwable=" + throwable +
'}';
}
}
/**
* Take the parsed package from the parsing queue, waiting if necessary until the element
* appears in the queue.
* @return parsed package
*/
public ParseResult take() {
try {
if (mInterruptedInThread != null) {
throw new InterruptedException("Interrupted in " + mInterruptedInThread);
}
return mQueue.take();
} catch (InterruptedException e) {
// We cannot recover from interrupt here
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
/**
* Submits the file for parsing
* @param scanFile file to scan
* @param parseFlags parse falgs
*/
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
mQueue.put(pr);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// Propagate result to callers of take().
// This is helpful to prevent main thread from getting stuck waiting on
// ParallelPackageParser to finish in case of interruption
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
@VisibleForTesting
protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
int parseFlags) throws PackageParser.PackageParserException {
return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
}
...
}
```
`ParallelPackageParser` 是一个并行的 `PackageParser.Package` 解析器,内部使用线程池来完成并行的工作。具体解析操作后面再另行分析,这里先按照解析成功继续分析:
```java {.line-numbers}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
// If the package has children and this is the first dive in the function
// we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
// packages (parent and children) would be successfully scanned before the
// actual scan since scanning mutates internal state and we want to atomically
// install the package and its children.
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
// Scan the parent
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scannedPkg;
}
```
### 4.2.2 addForInitLI
```java {.line-numbers}
/**
* Adds a new package to the internal data structures during platform initialization.
* <p>After adding, the package is known to the system and available for querying.
* <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
* etc...], additional checks are performed. Basic verification [such as ensuring
* matching signatures, checking version codes, etc...] occurs if the package is
* identical to a previously known package. If the package fails a signature check,
* the version installed on /data will be removed. If the version of the new package
* is less than or equal than the version on /data, it will be ignored.
* <p>Regardless of the package location, the results are applied to the internal
* structures and the package is made available to the rest of the system.
* <p>NOTE: The return value should be removed. It's the passed in package object.
*/
private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
final boolean scanSystemPartition = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0)
// M:operator app also is removable and not system flag
|| sPmsExt.isRemovableSysApp(pkg.packageName);
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
...
// 判断系统应用是否需要更新
synchronized (mPackages) {
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
...
final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
? mSettings.getSharedUserLPw(pkg.mSharedUserId,
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
...
if (scanSystemPartition) {
// Potentially prune child packages. If the application on the /system
// partition has been updated via OTA, but, is still disabled by a
// version on /data, cycle through all of its children packages and
// remove children that are no longer defined.
// 更新子应用
if (isSystemPkgUpdated) {
...
}
...
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
final boolean newPkgVersionGreater =
//M: change ">" to ">=" can restore the same version apk, test step:
//1) xun