由以上代码可知,`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) xunfei app is in operator path and version is 6
//2) then adb install the upgrade verison 8
//3) uninstall it(version 8)
//4) install it version 6 again
//5) adb reboot
//6)result: can found xunfei apk verision 6
pkgAlreadyExists && pkg.getLongVersionCode() >= pkgSetting.versionCode;
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
// It's safe to assume the application on /system will correctly scan. If not,
// there won't be a working copy of the application.
// 更新安装包到 system 分区中
synchronized (mPackages) {
// just remove the loaded entries from package lists
mPackages.remove(pkgSetting.name);
}
...
// 创建安装参数 InstallArgs
final InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
mSettings.enableSystemPackageLPw(pkgSetting.name);
}
}
...
// Verify certificates against what was last scanned. If it is an updated priv app, we will
// force re-collecting certificate.
final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
disabledPkgSetting);
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
final boolean skipVerify = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) ||
(forceCollect && canSkipFullPackageVerification(pkg));
// 安装包校验
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
// Reset profile if the application version is changed
maybeClearProfilesForUpgradesLI(pkgSetting, pkg);
/*
* A new system app appeared, but we already had a non-system one of the
* same name installed earlier.
*
*/
boolean shouldHideSystemApp = false;
// A new application appeared on /system, but, we already have a copy of
// the application installed on /data.
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
pkg.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
...
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
// 如果两个 apk 签名不匹配,则调用 deletePackageLIF 方法清除 apk 文件及其数据
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
// The application on /system is newer than the application on /data.
// Simply remove the application on /data [keeping application data]
// and replace it with the version on /system.
...
// 更新系统 apk 程序
InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// The application on /system is older than the application on /data. Hide
// the application on /system and the version on /data will be scanned later
// and re-added like an update.
shouldHideSystemApp = true;
...
}
}
final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
// 如果新安装的系统APP 会被旧的APP数据覆盖,所以需要隐藏隐藏系统应用程序,并重新扫描 /data/app 目录
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scannedPkg;
}
```
### 4.2.3 scanPackageNewLI
开始解析 `PackageParser.Package:
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
PackageParser.Package mPlatformPackage;
...
// TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting
// the results / removing app data needs to be moved up a level to the callers of this
// method. Also, we need to solve the problem of potentially creating a new shared user
// setting. That can probably be done later and patch things up after the fact.
@GuardedBy("mInstallLock")
private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
final String 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 pkgSetting = mSettings.getPackageLPr(pkg.packageName);
final PackageSetting disabledPkgSetting =
mSettings.getDisabledSystemPkgLPr(pkg.packageName);
...
// 根据上述各种 PackageSetting 调整扫描 flag
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
synchronized (mPackages) {
// 应用扫描策略
applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
// 断言 package 有效
assertPackageIsValid(pkg, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
if (pkg.mSharedUserId != null) {
// SIDE EFFECTS; may potentially allocate a new shared user
sharedUserSetting = mSettings.getSharedUserLPw(
pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
...
}
boolean scanSucceeded = false;
try {
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
if (result.success) {
// 将 pkg 中的数据保存到对应的 PMS 变量中,用于以后的管理查询调用等
commitScanResultsLocked(request, result);
}
scanSucceeded = true;
...
}
}
return pkg;
}
```
### 4.2.4 commitScanResultsLocked
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an excpetion in the middle
* of committing the package, leaving the system in an inconsistent state.
* This needs to be fixed so, once we get to this point, no errors are
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy("mPackages")
private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
throws PackageManagerException {
final PackageParser.Package pkg = request.pkg;
final PackageParser.Package oldPkg = request.oldPkg;
final @ParseFlags int parseFlags = request.parseFlags;
final @ScanFlags int scanFlags = request.scanFlags;
final PackageSetting oldPkgSetting = request.oldPkgSetting;
...
final UserHandle user = request.user;
...
final PackageSetting pkgSetting = result.pkgSetting;
...
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
if (oldPkgSetting != null) {
synchronized (mPackages) {
mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
}
}
} else {
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
(parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
...
}
}
```
### 4.2.5 commitPackageSettings
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
// All available providers, for your resolving pleasure.
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
...
// Mapping from instrumentation class names to info about them.
final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
new ArrayMap<ComponentName, PackageParser.Instrumentation>();
...
// Broadcast actions that are only available to the system.
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
...
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
...
ComponentName mResolveComponentName;
...
/**
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
*/
private void commitPackageSettings(PackageParser.Package pkg,
@Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,
final @ScanFlags int scanFlags, boolean chatty) {
final String pkgName = pkg.packageName;
// 可配置的 ResloverActivity
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
setUpCustomResolverActivity(pkg);
}
// 针对包名为 "android" 的 apk 进行处理
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
pkg.mVersionCodeMajor = 0;
mAndroidApplication = pkg.applicationInfo;
// 配置系统默认的 ResloverActivity
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
...
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
}
}
...
synchronized (mPackages) {
...
// 在此之前,四大组件的信息都是 Package 对象的私有变量,通过下面的代码,
// 将他们注册到 PackageManagerService 里面,
// 这样 PackageManagerService 就有了所有的组件信息
int N = pkg.providers.size();
StringBuilder r = null;
int i;
// 注册 pkg 里面的 provider 到 PackageManagerService 上的 mProvider 上
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName);
mProviders.addProvider(p);
...
}
...
N = pkg.services.size();
r = null;
// 注册 pkg 中的 service 到 PackageManagerService 的 mServices 上
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName);
mServices.addService(s);
...
}
...
N = pkg.receivers.size();
r = null;
// 注册 pkg 里面的 receiver 到 PackageManagerService 上的 mReceivers 上
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mReceivers.addActivity(a, "receiver");
...
}
...
N = pkg.activities.size();
r = null;
// 注册 pkg 里面的 activity 到 PackageManagerService 上的 mActivities 上
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mActivities.addActivity(a, "activity");
...
}
...
N = pkg.instrumentation.size();
r = null;
// 注册 pkg 里面的 instrumentation 到 PackageManagerService 的 mInstrumentation 中
// Instrumentation 用来跟踪本应用内的 application 及 activity 的生命周期
for (i=0; i<N; i++) {
PackageParser.Instrumentation a = pkg.instrumentation.get(i);
a.info.packageName = pkg.applicationInfo.packageName;
...
mInstrumentation.put(a.getComponentName(), a);
...
}
...
// 如果有包内广播
if (pkg.protectedBroadcasts != null) {
N = pkg.protectedBroadcasts.size();
synchronized (mProtectedBroadcasts) {
for (i = 0; i < N; i++) {
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
}
...
}
...
}
```
## 4.3 PMS_DATA_SCAN_START
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
/** Directory where installed applications are stored */
private static final File sAppInstallDir =
new File(Environment.getDataDirectory(), "app");
...
/** Directory where code and non-resource assets of forward-locked applications are stored */
private static final File sDrmAppPrivateInstallDir =
new File(Environment.getDataDirectory(), "app-private");
...
/**
* Tracks high priority intent filters for protected actions. During boot, certain
* filter actions are protected and should never be allowed to have a high priority
* intent filter for them. However, there is one, and only one exception -- the
* setup wizard. It must be able to define a high priority intent filter for these
* actions to ensure there are no escapes from the wizard. We need to delay processing
* of these during boot as we need to look at all of the system packages in order
* to know which component is the setup wizard.
*/
private final List<PackageParser.ActivityIntentInfo> mProtectedFilters = new ArrayList<>();
/**
* Whether or not processing protected filters should be deferred.
*/
private boolean mDeferProtectedFilters = true;
...
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
final @Nullable String mSystemTextClassifierPackage;
...
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
...
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
if (!mOnlyCore) {
// 第三阶段开始,扫描非系统 package
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// 扫描 /data/app 目录
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 扫描 /data/app-private 目录
scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
// 这里检查用户目录下升级文件是否还存在,然后进行处理
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
// 从 mSettings.mDisabledSysPackages 变量中移除去此应用
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
final String msg;
// 用户目录中也没有升级包,则肯定是残留的应用信息,则把它的数据目录删除掉
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
msg = "Updated system package " + deletedAppName
+ " no longer exists; removing its data";
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// found an update; revoke system privileges
// 如果在用户空间找到了文件,则说明系统目录下的文件可能被删除了,
// 因此,把应用的系统属性去掉,以普通应用的方式运行
msg = "Updated system package + " + deletedAppName
+ " no longer exists; revoking system privileges";
// Don't do anything if a stub is removed from the system image. If
// we were to remove the uncompressed version from the /data partition,
// this is where it'd be done.
final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
// 报告系统发生了不一致的情况
logCriticalInfo(Log.WARN, msg);
}
/*
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
*/
// 处理 mExpectingBetter 列表,这个列表的应用是带有升级包的系统的应用,
// 前面把他们从 mPackages 列表中清除了并放到 mExpectingBetter 列表,
// 最后也对他们进行扫描处理,但不会放到 mPackages 中。
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i);
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
final @ParseFlags int reparseFlags;
final @ScanFlags int rescanFlags;
// 确保应用位于下面的系统应用目录中,如果不在,则不需要处理
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM;
...
} else {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
// 如果应用不在上面这些目录,继续循环,不要处理
continue;
}
mSettings.enableSystemPackageLPw(packageName);
try {
// 重新扫描一下这个文件,会添加一个 <update-package> 标签
scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
}
// Uncompress and install any stubbed system applications.
// This must be done last to ensure all stubs are replaced or disabled.
decompressSystemApplications(stubSystemApps, scanFlags);
final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
- cachedSystemApps;
// 计算扫描时间及数量
final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
final int dataPackagesCount = mPackages.size() - systemPackagesCount;
...
}
mExpectingBetter.clear();
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
// Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
// 只有开机向导具有高优先级
mSetupWizardPackage = getSetupWizardPackageName();
if (mProtectedFilters.size() > 0) {
...
for (ActivityIntentInfo filter : mProtectedFilters) {
if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {
...
// skip setup wizard; allow it to keep the high priority filter
continue;
}
...
filter.setPriority(0);
}
}
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
mDeferProtectedFilters = false;
mProtectedFilters.clear();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLPw(null);
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
try {
mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
} catch (InstallerException ignored) {
}
}
}
// Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
// SELinux domain.
setting.fixSeInfoLocked();
}
// Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(mPackages);
mCompilerStats.read();
// 至此,第三阶段结束
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
...
}
}
...
}
```
### 4.3.1 scanPackageTracedLI
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
// 解析 Package
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Static shared libraries have synthetic package names
if (pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(pkg);
}
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
```
本阶段主要工作:
- 扫描 /data/app 目录下的apk;
- 扫描 /data/app-private 目录下的apk;
## 4.4 PMS_SCAN_END
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
// 第四阶段开始
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
...
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
// 当sdk版本不一致时,需要更新权限
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
...
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
mPermissionCallback);
ver.sdkVersion = mSdkVersion;
...
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
// Note that we do *not* clear the application profiles. These remain valid
// across OTAs and are used to drive profile verification (post OTA) and
// profile compilation (without waiting to collect a fresh set of profiles).
// 当这是ota后的首次启动,或者正常启动则需要清除目录的缓存代码
if (mIsUpgrade && !onlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
checkDefaultBrowser();
// clear only after permissions and other defaults have been updated
// 当权限和其他默认项都完成更新,则清理相关信息
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
// All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// can downgrade to reader
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings");
// 信息写回 packages.xml 文件
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// 至此,第四阶段完成
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
...
}
}
...
}
```
到此阶段,apk 已安装完成,本阶段主要工作:
- 将 SCAN 阶段的信息写回 /data/system/packages.xml;
## 4.5 PMS_READY
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
final PackageInstallerService mInstallerService;
...
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps).
private final DexManager mDexManager;
...
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
...
// 第五阶段开始
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
...
// 创建 PackageInstallerService 服务
mInstallerService = new PackageInstallerService(context, this);
...
// Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
// data directory reconciled.
// At this point we know the code paths of the packages, so we can validate
// the disk file and build the internal cache.
// The usage file is expected to be small so loading and verifying it
// should take a fairly small time compare to the other activities (e.g. package
// scanning).
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
for (int userId : currentUserIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
mDexManager.load(userPackages);
...
} // synchronized (mPackages)
} // synchronized (mInstallLock)
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC");
Runtime.getRuntime().gc();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mPackages);
...
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
```
### 4.5.1 创建 PKIS 服务
> frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
```java {.line-numbers}
public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String TAG = "PackageInstaller";
...
private final Context mContext;
private final PackageManagerService mPm;
private final PermissionManagerInternal mPermissionManager;
...
private final HandlerThread mInstallThread;
private final Handler mInstallHandler;
private final Callbacks mCallbacks;
/**
* File storing persisted {@link #mSessions} metadata.
*/
private final AtomicFile mSessionsFile;
/**
* Directory storing persisted {@link #mSessions} metadata which is too
* heavy to store directly in {@link #mSessionsFile}.
*/
private final File mSessionsDir;
...
public PackageInstallerService(Context context, PackageManagerService pm) {
mContext = context;
mPm = pm;
mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
mInstallThread = new HandlerThread(TAG);
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
mCallbacks = new Callbacks(mInstallThread.getLooper());
mSessionsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "install_sessions.xml"),
"package-session");
mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
mSessionsDir.mkdirs();
}
}
```
本阶段主要工作:
- 创建 PackageInstallerService 服务;
# 五 Others
## 5.1 PKMS.updatePackagesIfNeeded
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
@Override
public void updatePackagesIfNeeded() {
enforceSystemOrRoot("Only the system can request package update");
// We need to re-extract after an OTA.
boolean causeUpgrade = isUpgrade();
// First boot or factory reset.
// Note: we also handle devices that are upgrading to N right now as if it is their
// first boot, as they do not have profile data.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
// We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
return;
}
List<PackageParser.Package> pkgs;
synchronized (mPackages) {
// 获取 dexopt 操作的 app 集合
pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
..
}
```
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
```java {.line-numbers}
/**
* Class containing helper methods for the PackageManagerService.
*
* {@hide}
*/
public class PackageManagerServiceUtils {
private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
...
// Sort apps by importance for dexopt ordering. Important apps are given
// more priority in case the device runs out of space.
public static List<PackageParser.Package> getPackagesForDexopt(
Collection<PackageParser.Package> packages,
PackageManagerService packageManagerService) {
ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
LinkedList<PackageParser.Package> result = new LinkedList<>();
ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
// Give priority to core apps.
// 将 pkgs 中的核心 app 添加到 sortTemp
applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
packageManagerService);
// Give priority to system apps that listen for pre boot complete.
// 获取监听 PRE_BOOT_COMPLETE 的系统 app 集合
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
sortTemp, packageManagerService);
// Give priority to apps used by other apps.
// 将被依赖的 app 添加到 sortTemp
DexManager dexManager = packageManagerService.getDexManager();
applyPackageFilter((pkg) ->
dexManager.getPackageUseInfoOrDefault(pkg.packageName)
.isAnyCodePathUsedByOtherApps(),
result, remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
Predicate<PackageParser.Package> remainingPredicate;
if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Looking at historical package use");
}
// Get the package that was used last.
PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
pkg2.getLatestForegroundPackageUseTimeInMills()));
if (DEBUG_DEXOPT) {
Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
}
long estimatedPreviousSystemUseTime =
lastUsed.getLatestForegroundPackageUseTimeInMills();
// Be defensive if for some reason package usage has bogus data.
if (estimatedPreviousSystemUseTime != 0) {
final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
remainingPredicate =
(pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
} else {
// No meaningful historical info. Take all.
remainingPredicate = (pkg) -> true;
}
sortPackagesByUsageDate(remainingPkgs, packageManagerService);
} else {
// No historical info. Take all.
remainingPredicate = (pkg) -> true;
}
// 将最近(一周)使用的 app 添加到 sortTemp
applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
packageManagerService);
...
return result;
}
...
}
```
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
@GuardedBy("mPackages")
private boolean mDexOptDialogShown;
...
/*
* Return the prebuilt profile path given a package base code path.
*/
private static String getPrebuildProfilePath(PackageParser.Package pkg) {
return pkg.baseCodePath + ".prof";
}
/**
* Performs dexopt on the set of packages in {@code packages} and returns an int array
* containing statistics about the invocation. The array consists of three elements,
* which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
final int compilationReason, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
int numberOfPackagesSkipped = 0;
int numberOfPackagesFailed = 0;
final int numberOfPackagesToDexopt = pkgs.size();
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
// 仅对出厂或 OTA 后首次开机时的系统 app
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
File profileFile = new File(getPrebuildProfilePath(pkg));
// Copy profile if it exists.
...
}
if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
...
numberOfPackagesSkipped++;
continue;
}
...
if (showDialog) {
try {
// 对话框显示"正在优化第 i 个应用(共 n 个)。“
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(R.string.android_upgrading_apk,
numberOfPackagesVisited, numberOfPackagesToDexopt), true);
} catch (RemoteException e) {
}
synchronized (mPackages) {
mDexOptDialogShown = true;
}
}
int pkgCompilationReason = compilationReason;
if (useProfileForDexopt) {
// Use background dexopt mode to try and use the profile. Note that this does not
// guarantee usage of the profile.
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
// checkProfiles is false to avoid merging profiles during boot which
// might interfere with background compilation (b/28612421).
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
if (compilationReason == REASON_FIRST_BOOT) {
// TODO: This doesn't cover the upgrade case, we should check for this too.
dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
}
// 执行 dexopt
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
pkgCompilationReason,
dexoptFlags));
switch (primaryDexOptStaus) {
case PackageDexOptimizer.DEX_OPT_PERFORMED:
numberOfPackagesOptimized++;
break;
case PackageDexOptimizer.DEX_OPT_SKIPPED:
numberOfPackagesSkipped++;
break;
case PackageDexOptimizer.DEX_OPT_FAILED:
numberOfPackagesFailed++;
break;
default:
Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
break;
}
}
return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
numberOfPackagesFailed };
}
```
## 5.2 PKMS.performFstrimIfNeeded
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
/**
* Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
* be run on this device. We use the value in the Settings.Global.MANDATORY_FSTRIM_INTERVAL
* settings entry if available, otherwise we use the hardcoded default. If it's been
* more than this long since the last fstrim, we force one during the boot sequence.
*
* This backstops other fstrim scheduling: if the device is alive at midnight+idle,
* one gets run at the next available charging+idle time. This final mandatory
* no-fstrim check kicks in only of the other scheduling criteria is never met.
*/
private static final long DEFAULT_MANDATORY_FSTRIM_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS;
...
@Override
public void performFstrimIfNeeded() {
// 确保只有 system 或者 root uid 才有权限执行该方法
enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
// 获取 MountService
IStorageManager sm = PackageHelper.getStorageManager();
if (sm != null) {
boolean doTrim = false;
// 默认 3 天
final long interval = android.provider.Settings.Global.getLong(
mContext.getContentResolver(),
android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
DEFAULT_MANDATORY_FSTRIM_INTERVAL);
if (interval > 0) {
final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
// 距离上次 fstrim 时间超过 3 天,则执行 fstrim
if (timeSinceLast > interval) {
doTrim = true;
Slog.w(TAG, "No disk maintenance in " + timeSinceLast
+ "; running immediately");
}
}
if (doTrim) {
final boolean dexOptDialogShown;
synchronized (mPackages) {
dexOptDialogShown = mDexOptDialogShown;
}
if (!isFirstBoot() && dexOptDialogShown) {
try {
// 对话框显示“正在优化存储空间”
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(
R.string.android_upgrading_fstrim), true);
} catch (RemoteException e) {
}
}
// 执行 fstrim
sm.runMaintenance();
}
} else {
Slog.e(TAG, "storageManager service unavailable!");
}
} catch (RemoteException e) {
// Can't happen; StorageManagerService is local
}
}
```
## 5.3 PKMS.systemReady
> frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java {.line-numbers}
...
private static final int[] EMPTY_INT_ARRAY = new int[0];
...
/**
* Messages for {@link #mHandler} that need to wait for system ready before
* being dispatched.
*/
private ArrayList<Message> mPostSystemReadyMessages;
...
volatile boolean mSystemReady;
...
@Override
public void systemReady() {
enforceSystemOrRoot("Only the system can claim the system is ready");
mSystemReady = true;
...
int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
synchronized (mPackages) {
// Verify that all of the preferred activity components actually
// exist. It is possible for applications to be updated and at
// that point remove a previously declared activity component that
// had been set as a preferred activity. We try to clean this up
// the next time we encounter that preferred activity, but it is
// possible for the user flow to never be able to return to that
// situation so here we do a sanity check to make sure we haven't
// left any junk around.
ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
removed.clear();
for (PreferredActivity pa : pir.filterSet()) {
if (mActivities.mActivities.get(pa.mPref.mComponent) == null) {
removed.add(pa);
}
}
if (removed.size() > 0) {
for (int r=0; r<removed.size(); r++) {
PreferredActivity pa = removed.get(r);
Slog.w(TAG, "Removing dangling preferred activity: "
+ pa.mPref.mComponent);
pir.removeFilter(pa);
}
mSettings.writePackageRestrictionsLPr(
mSettings.mPreferredActivities.keyAt(i));
}
}
for (int userId : UserManagerService.getInstance().getUserIds()) {
if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
grantPermissionsUserIds = ArrayUtils.appendInt(
grantPermissionsUserIds, userId);
}
}
}
// 多用户管理服务
sUserManager.systemReady();
// If we upgraded grant all default permissions before kicking off.
// 升级所有已获取的默认权限
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
// If we did not grant default permissions, we preload from this the
// default permission exceptions lazily to ensure we don't hit the
// disk on a new user creation.
mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions();
}
// Now that we've scanned all packages, and granted any default
// permissions, ensure permissions are updated. Beware of dragons if you
// try optimizing this.
synchronized (mPackages) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
}
// Kick off any messages waiting for system ready
// 处理所有等待系统准备就绪的消息
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages) {
msg.sendToTarget();
}
mPostSystemReadyMessages = null;
}
// Watch for external volumes that come and go over time
// 观察存储服务
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.registerListener(mStorageListener);
mInstallerService.systemReady();
mDexManager.systemReady();
mPackageDexOptimizer.systemReady();
StorageManagerInternal StorageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
StorageManagerInternal.addExternalStoragePolicy(
new StorageManagerInternal.ExternalStorageMountPolicy() {
@Override
public int getMountMode(int uid, String packageName) {
if (Process.isIsolated(uid)) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_DEFAULT;
}
if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
return Zygote.MOUNT_EXTERNAL_READ;
}
return Zygote.MOUNT_EXTERNAL_WRITE;
}
@Override
public boolean hasExternalStorage(int uid, String packageName) {
return true;
}
});
// Now that we're mostly running, clean up stale users and apps
sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.systemReady();
...
}
```
# 六 开机时间分析
使用命令:`adb shell cat /proc/bootprof` 查看开机时间:
```xml {.line-numbers}
$ adb shell cat /proc/bootprof [16:12:19]
----------------------------------------
0 BOOT PROF (unit:msec)
----------------------------------------
4354 : preloader
2814 : lk (Start->Show logo: 1142)
----------------------------------------
203.490846 : ON
347.638385 : 1-swapper/0 : initcall: pwrap_hal_init 38.437462ms
388.408308 : 1-swapper/0 : initcall: init_menu 20.046385ms
446.307077 : 1-swapper/0 : initcall: customize_machine 53.406384ms
...
2718.143083 : 1-swapper/0 : Kernel_init_done
3966.768624 : 268-init : INIT:early-init
4408.037856 : 268-init : INIT:late-init
4423.971471 : 268-init : INIT:Mount_START
4863.027626 : 268-init : INIT:Mount_END
5150.339396 : 268-init : INIT:post-fs
5528.991474 : 268-init : INIT:post-fs-data
5680.929013 : 268-init : post-fs-data: on modem start
6122.993014 : 268-init : INIT:boot
7028.273170 : 268-init : USB ready
7043.689247 : 401-Binder:313_3 : vold:decrypt_master_key:START
7100.255016 : 268-init : probe: probe=platform_drv_probe drv=fm(bf14a018) 27.782308ms
7100.401555 : 268-init : initcall: init_module [fmradio_drv] 30.018693ms
7411.948248 : 425-SurfaceFlinger: : BOOT_Animation:START
7713.793095 : 55-kworker/3:1 : USB configured
7726.765787 : 401-Binder:313_3 : vold:decrypt_master_key:END
8462.948020 : 401-Binder:313_3 : vold:cryptfs_restart_internal:START
8552.378635 : 268-init : INIT:vold.decrypt=trigger_post_fs_data
8787.473097 : 268-init : INIT:post-fs-data
8889.881405 : 268-init : post-fs-data: on modem start
9898.642023 : 401-Binder:313_3 : vold:cryptfs_restart_internal:END
9927.095792 : 268-init : INIT:vold.decrypt=trigger_restart_framework
10047.714716 : 491-zygote : boot_progress_start
11504.507642 : 491-main : Zygote:Preload Start
13570.089801 : 491-main : Zygote:Preload 6539 classes in 1916ms
13908.996494 : 491-main : Zygote:Preload 64 obtain resources in 79ms
13999.568648 : 491-main : Zygote:Preload 41 resources in 89ms
14568.986573 : 491-main : Zygote:Preload End
14937.230420 : 918-system_server : Android:SysServerInit_START
15920.134730 : 918-system_server : Android:PackageManagerService_Start
16432.177039 : 918-system_server : Android:PMS_scan_START
16465.812654 : 918-system_server : Android:PMS_scan_data:/vendor/overlay
16497.700500 : 918-system_server : Android:PMS_scan_data:/system/framework
16593.062347 : 918-system_server : Android:PMS_scan_data:/system/priv-app
17297.726502 : 918-system_server : Android:PMS_scan_data:/system/app
17736.394119 : 918-system_server : Android:PMS_scan_data:/vendor/app
17762.593965 : 918-system_server : Android:PMS_scan_data:/data/app
18010.729119 : 918-system_server : Android:PMS_scan_END
18030.269042 : 918-system_server : PMS:reconcileAppsDataLI
18577.189197 : 918-system_server : Android:PMS_READY
20504.176202 : 918-system_server : AMS:systemReady
20513.526202 : 918-system_server : AMS:AMS_READY
20710.391203 : 937-ActivityManager : AP_Init:[service]:[com.android.inputmethod.latin]:[com.android.inputmethod.latin/.LatinIME]:pid:1045
20745.502664 : 937-ActivityManager : AP_Init:[service]:[com.android.systemui]:[com.android.systemui/.SystemUIService]:pid:1063:(PersistAP)
20800.240049 : 918-system_server : SystemServer:NetworkStatsService systemReady
20928.649280 : 937-ActivityManager : AP_Init:[]:[WebViewLoader-armeabi-v7a]:pid:1093
20957.218434 : 918-system_server : SystemServer:ConnectivityService systemReady
20978.366665 : 918-system_server : SystemServer:NetworkPolicyManagerServ systemReady
21229.010665 : 918-system_server : SystemServer:PhaseThirdPartyAppsCanStart
21465.574974 : 918-system_server : Android:SysServerInit_END
21507.890204 : 937-ActivityManager : AP_Init:[added application]:[com.android.phone]:[com.android.phone]:pid:1179:(PersistAP)
21587.235666 : 937-ActivityManager : AP_Init:[activity]:[com.android.settings]:[com.android.settings/.FallbackHome]:pid:1207
21871.090821 : 937-ActivityManager : AP_Init:[service]:[android.ext.services]:[android.ext.services/.notification.Assistant]:pid:1258
22058.393206 : 935-ActivityManager : AMS:ENABLE_SCREEN
24001.307057 : 937-ActivityManager : AP_Init:[service]:[com.mediatek.ims]:[com.mediatek.ims/.MtkDynamicImsService]:pid:1440
24355.295057 : 937-ActivityManager : AP_Init:[broadcast]:[com.mediatek.ppl]:[com.mediatek.ppl/.PplReceiver]:pid:1468
26605.395217 : 380-Binder:354_1 : BOOT_Animation:END
26656.498294 : 937-ActivityManager : AP_Init:[service]:[com.android.bluetooth]:[com.android.bluetooth/.btservice.AdapterService]:pid:1513
26743.682909 : 937-ActivityManager : AP_Init:[broadcast]:[com.android.deskclock]:[com.android.deskclock/.AlarmInitReceiver]:pid:1522
26774.370448 : OFF
----------------------------------------
```
可以从上面的信息看到 `PKMS` 在开机的时候做的动作和时间分布,一般 app 装的越多,开机时间越长。
# 七 参考资料
- [PackageManager启动篇](http://gityuan.com/2016/11/06/packagemanager/)
- [PackageManagerService启动流程](https://maoao530.github.io/2017/01/10/packagemanager/)
- [PackageManagerService 系列](https://superandroid.pro/2019/01/01/%E3%80%90%E6%A0%B8%E5%BF%83%E6%9C%8D%E5%8A%A1%E3%80%91Android%20%E6%A1%86%E6%9E%B6%E6%9C%8D%E5%8A%A1%E7%AF%87%20--%20%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6%20PMS%20%E7%B3%BB%E5%88%97%EF%BC%881%EF%BC%89%E4%B9%8B%20%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/)