blob: a03a16ad7bc39aa47c8858b2f67756222061dc7e [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DROP_CACHE;
import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedArrayMap;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
/**
* Part of PackageManagerService that handles init and system packages. This class still needs
* further cleanup and eventually all the installation/scanning related logic will go to another
* class.
*/
final class InitAppsHelper {
private final PackageManagerService mPm;
private final List<ScanPartition> mDirsToScanAsSystem;
private final int mScanFlags;
private final int mSystemParseFlags;
private final int mSystemScanFlags;
private final InstallPackageHelper mInstallPackageHelper;
private final ApexManager mApexManager;
private final ExecutorService mExecutorService;
/* Tracks how long system scan took */
private long mSystemScanTime;
/* Track of the number of cached system apps */
private int mCachedSystemApps;
/* Track of the number of system apps */
private int mSystemPackagesCount;
private final boolean mIsDeviceUpgrading;
private final boolean mIsOnlyCoreApps;
private final List<ScanPartition> mSystemPartitions;
/**
* Tracks new system packages [received in an OTA] that we expect to
* find updated user-installed versions. Keys are package name, values
* are package location.
*/
private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
/* Tracks of any system packages that no longer exist that needs to be pruned. */
private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
// Tracks of stub packages that must either be replaced with full versions in the /data
// partition or be disabled.
private final List<String> mStubSystemApps = new ArrayList<>();
// TODO(b/198166813): remove PMS dependency
InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
InstallPackageHelper installPackageHelper,
List<ScanPartition> systemPartitions) {
mPm = pm;
mApexManager = apexManager;
mInstallPackageHelper = installPackageHelper;
mSystemPartitions = systemPartitions;
mDirsToScanAsSystem = getSystemScanPartitions();
mIsDeviceUpgrading = mPm.isDeviceUpgrading();
mIsOnlyCoreApps = mPm.isOnlyCoreApps();
// Set flag to monitor and not change apk file paths when scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
} else {
mScanFlags = scanFlags;
}
mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
mSystemScanFlags = mScanFlags | SCAN_AS_SYSTEM;
mExecutorService = ParallelPackageParser.makeExecutorService();
}
private List<File> getFrameworkResApkSplitFiles() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanFrameworkResApkSplits");
try {
final List<File> splits = new ArrayList<>();
final List<ApexManager.ActiveApexInfo> activeApexInfos =
mPm.mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
ApexManager.ActiveApexInfo apexInfo = activeApexInfos.get(i);
File splitsFolder = new File(apexInfo.apexDirectory, "etc/splits");
if (splitsFolder.isDirectory()) {
for (File file : splitsFolder.listFiles()) {
if (ApkLiteParseUtils.isApkFile(file)) {
splits.add(file);
}
}
}
}
return splits;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
scanPartitions.addAll(mSystemPartitions);
scanPartitions.addAll(getApexScanPartitions());
Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
return scanPartitions;
}
private List<ScanPartition> getApexScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
if (scanPartition != null) {
scanPartitions.add(scanPartition);
}
}
return scanPartitions;
}
private static @Nullable ScanPartition resolveApexToScanPartition(
ApexManager.ActiveApexInfo apexInfo) {
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
ScanPartition sp = SYSTEM_PARTITIONS.get(i);
if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
sp.getFolder().getAbsolutePath())
|| apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
sp.getFolder().getAbsolutePath() + File.separator)) {
int flags = SCAN_AS_APK_IN_APEX;
if (apexInfo.activeApexChanged) {
flags |= SCAN_DROP_CACHE;
}
return new ScanPartition(apexInfo.apexDirectory, sp, flags);
}
}
return null;
}
/**
* Install apps from system dirs.
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public OverlayConfig initSystemApps(PackageParser2 packageParser,
WatchedArrayMap<String, PackageSetting> packageSettings,
int[] userIds, long startTime) {
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
mApexManager.scanApexPackagesTraced(packageParser, mExecutorService);
scanSystemDirs(packageParser, mExecutorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(mPm.snapshotComputer(),
pkg -> consumer.accept(pkg, pkg.isSystem(),
apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
if (!mIsOnlyCoreApps) {
// do this first before mucking with mPackages for the "expecting better" case
updateStubSystemAppsList(mStubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
}
logSystemAppsScanningTime(startTime);
return overlayConfig;
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void logSystemAppsScanningTime(long startTime) {
mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
mPm.mSettings.pruneSharedUsersLPw();
mSystemScanTime = SystemClock.uptimeMillis() - startTime;
mSystemPackagesCount = mPm.mPackages.size();
Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
+ " ms, packageCount: " + mSystemPackagesCount
+ " , timePerPackage: "
+ (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
+ " , cached: " + mCachedSystemApps);
if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
mSystemScanTime / mSystemPackagesCount);
//CHECKSTYLE:ON IndentationCheck
}
}
/**
* Install apps/updates from data dir and fix system apps that are affected.
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds,
long startTime) {
if (!mIsOnlyCoreApps) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
mScanFlags | SCAN_REQUIRE_KNOWN,
packageParser, mExecutorService);
}
List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
if (!mIsOnlyCoreApps) {
fixSystemPackages(userIds);
logNonSystemAppScanningTime(startTime);
}
mExpectingBetter.clear();
mPm.mSettings.pruneRenamedPackagesLPw();
}
/**
* Clean up system packages now that some system package updates have been installed from
* the data dir. Also install system stub packages as the last step.
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void fixSystemPackages(@NonNull int[] userIds) {
mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
userIds, mScanFlags);
mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
// Uncompress and install any stubbed system applications.
// This must be done last to ensure all stubs are replaced or disabled.
mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void logNonSystemAppScanningTime(long startTime) {
final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- mCachedSystemApps;
final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ " ms, packageCount: " + dataPackagesCount
+ " , timePerPackage: "
+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ " , cached: " + cachedNonSystemApps);
if (mIsDeviceUpgrading && dataPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(
FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
dataScanTime / dataPackagesCount);
//CHECKSTYLE:OFF IndentationCheck
}
}
/**
* First part of init dir scanning
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(frameworkDir, null,
mSystemParseFlags,
mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags,
mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
}
@GuardedBy("mPm.mLock")
private void updateStubSystemAppsList(List<String> stubSystemApps) {
final int numPackages = mPm.mPackages.size();
for (int index = 0; index < numPackages; index++) {
final AndroidPackage pkg = mPm.mPackages.valueAt(index);
if (pkg.isStub()) {
stubSystemApps.add(pkg.getPackageName());
}
}
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
public boolean isExpectingBetter(String packageName) {
return mExpectingBetter.containsKey(packageName);
}
public List<ScanPartition> getDirsToScanAsSystem() {
return mDirsToScanAsSystem;
}
}