[pm] move storage and private app related code into a separate helper

Test: builds
BUG: 198177734
Change-Id: Ib9e12cafaa8f0f5e48f19dd434c3bfb1e2ec0035
This commit is contained in:
Songchun Fan
2021-09-09 14:00:35 -07:00
parent b765c352e1
commit 908f09e738
6 changed files with 2199 additions and 2032 deletions

View File

@@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
@@ -29,23 +28,18 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
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.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
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_NO_DEX;
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
@@ -54,10 +48,7 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
@@ -70,15 +61,12 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
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.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.WatchedArrayMap;
@@ -86,13 +74,9 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.IOException;
import java.security.DigestException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
/**
@@ -284,6 +268,7 @@ public class InitAndSystemPackageHelper {
}
if (!onlyCore) {
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
// 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.
@@ -316,7 +301,7 @@ public class InitAndSystemPackageHelper {
mPm.removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getPath());
mPm.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
scanPackageHelper.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
@@ -371,7 +356,7 @@ public class InitAndSystemPackageHelper {
mPm.mSettings.enableSystemPackageLPw(packageName);
try {
final AndroidPackage newPkg = mPm.scanPackageTracedLI(
final AndroidPackage newPkg = scanPackageHelper.scanPackageTracedLI(
scanFile, reparseFlags, rescanFlags, 0, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
@@ -456,6 +441,7 @@ public class InitAndSystemPackageHelper {
fileCount++;
}
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
@@ -471,7 +457,7 @@ public class InitAndSystemPackageHelper {
parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null, platformPackage, isUpgrade,
isPreNMR1Upgrade);
} catch (PackageManagerException e) {
@@ -650,8 +636,9 @@ public class InitAndSystemPackageHelper {
mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
}
mPm.removePackageLI(stubPkg, true /*chatty*/);
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
return mPm.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -869,8 +856,10 @@ public class InitAndSystemPackageHelper {
}
}
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
final AndroidPackage pkg =
mPm.scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
scanPackageHelper.scanPackageTracedLI(
codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -932,396 +921,4 @@ public class InitAndSystemPackageHelper {
}
}
}
/**
* 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.
*/
@GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
boolean isPreNMR1Upgrade) throws PackageManagerException {
final boolean scanSystemPartition =
(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
synchronized (mPm.mLock) {
renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
final String realPkgName = PackageManagerService.getRealPackageName(parsedPackage,
renamedPkgName);
if (realPkgName != null) {
PackageManagerService.ensurePackageRenamed(parsedPackage, renamedPkgName);
}
final PackageSetting originalPkgSetting = mPm.getOriginalPackageLocked(parsedPackage,
renamedPkgName);
final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
parsedPackage.getPackageName());
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists
? pkgSetting.name : parsedPackage.getPackageName();
if (scanSystemPartition && !pkgAlreadyExists
&& mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
// The updated-package data for /system apk remains inconsistently
// after the package data for /data apk is lost accidentally.
// To recover it, enable /system apk and install it as non-updated system app.
Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ disabledPkgName + ". To recover it, enable the system app"
+ "and install it as non-updated system app.");
mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
}
disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
if (scanSystemPartition) {
if (isSystemPkgUpdated) {
// we're updating the disabled package, so, scan it as the package setting
boolean isPlatformPackage = platformPackage != null
&& Objects.equals(platformPackage.getPackageName(),
parsedPackage.getPackageName());
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
null, parseFlags, scanFlags, isPlatformPackage, user, null);
PackageManagerService.applyPolicy(parsedPackage, scanFlags,
platformPackage, true);
final ScanResult scanResult =
mPm.scanPackageOnlyLI(request, mPm.mInjector,
mPm.mFactoryTest, -1L);
if (scanResult.mExistingSettingCopied
&& scanResult.mRequest.mPkgSetting != null) {
scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
}
}
}
}
final boolean newPkgChangedPaths = pkgAlreadyExists
&& !pkgSetting.getPathString().equals(parsedPackage.getPath());
final boolean newPkgVersionGreater =
pkgAlreadyExists && parsedPackage.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.
synchronized (mPm.mLock) {
// just remove the loaded entries from package lists
mPm.mPackages.remove(pkgSetting.name);
}
logCriticalInfo(Log.WARN,
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString()
+ " --> " + parsedPackage.getPath());
final InstallArgs args = mPm.createInstallArgsForExisting(
pkgSetting.getPathString(), getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
args.cleanUpResourcesLI();
synchronized (mPm.mLock) {
mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
}
}
// The version of the application on the /system partition is less than or
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
// In the case of a skipped package, commitReconciledScanResultLocked is not called to
// add the object to the "live" data structures, so this is the final mutation step
// for the package. Which means it needs to be finalized here to cache derived fields.
// This is relevant for cases where the disabled system package is used for flags or
// other metadata.
parsedPackage.hideAsFinal();
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getPath() + " ignored: updated version "
+ (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+ " better than this " + parsedPackage.getLongVersionCode());
}
// Verify certificates against what was last scanned. Force re-collecting certificate in two
// special cases:
// 1) when scanning system, force re-collect only if system is upgrading.
// 2) when scannning /data, force re-collect only if the app is privileged (updated from
// preinstall, or treated as privileged, e.g. due to shared user ID).
final boolean forceCollect = scanSystemPartition ? isUpgrade
: PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
if (DEBUG_VERIFY && forceCollect) {
Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
}
// 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.
// TODO(b/136132412): skip for Incremental installation
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(parsedPackage));
collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
isPreNMR1Upgrade);
// Reset profile if the application version is changed
maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
/*
* 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 (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
parsedPackage.getPackageName(),
"scanPackageInternalLI")) {
mPm.deletePackageLIF(parsedPackage.getPackageName(), null, true,
mPm.mUserManager.getUserIds(), 0, null, false);
}
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.
logCriticalInfo(Log.WARN,
"System package enabled;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString() + " --> "
+ parsedPackage.getPath());
InstallArgs args = mPm.createInstallArgsForExisting(
pkgSetting.getPathString(), getAppDexInstructionSets(
pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
synchronized (mPm.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;
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.getPathString() + " @ "
+ pkgSetting.versionCode
+ "; new: " + parsedPackage.getPath() + " @ "
+ parsedPackage.getPath());
}
}
final ScanResult scanResult = mPm.scanPackageNewLI(parsedPackage, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
if (scanResult.mSuccess) {
synchronized (mPm.mLock) {
boolean appIdCreated = false;
try {
final String pkgName = scanResult.mPkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult =
mPm.reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mPm.mSharedLibraries,
mPm.mPackages,
Collections.singletonMap(
pkgName,
mPm.getSettingsVersionForPackage(parsedPackage)),
Collections.singletonMap(pkgName,
mPm.getSharedLibLatestVersionSetting(scanResult))),
mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = mPm.optimisticallyRegisterAppId(scanResult);
mPm.commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
} catch (PackageManagerException e) {
if (appIdCreated) {
mPm.cleanUpAppIdCreation(scanResult);
}
throw e;
}
}
}
if (shouldHideSystemApp) {
synchronized (mPm.mLock) {
mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (pkgSetting != null && pkgSetting.isPackageLoading()) {
// Continue monitoring loading progress of active incremental packages
final IncrementalStatesCallback incrementalStatesCallback =
new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
}
return scanResult.mPkgSetting.pkg;
}
/**
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
return false;
}
// TODO: Allow base and splits to be verified individually.
String[] splitCodePaths = pkg.getSplitCodePaths();
if (!ArrayUtils.isEmpty(splitCodePaths)) {
for (int i = 0; i < splitCodePaths.length; i++) {
if (!canSkipForcedApkVerification(splitCodePaths[i])) {
return false;
}
}
}
return true;
}
/**
* Returns if forced apk verification can be skipped, depending on current FSVerity setup and
* whether the apk contains signed root hash. Note that the signer's certificate still needs to
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipForcedApkVerification(String apkPath) {
if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
return VerityUtils.hasFsverity(apkPath);
}
try {
final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
if (rootHashObserved == null) {
return false; // APK does not contain Merkle tree root hash.
}
synchronized (mPm.mInstallLock) {
// Returns whether the observed root hash matches what kernel has.
mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
return true;
}
} catch (Installer.InstallerException | IOException | DigestException
| NoSuchAlgorithmException e) {
Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
}
return false;
}
private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
throws PackageManagerException {
// When upgrading from pre-N MR1, verify the package time stamp using the package
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(parsedPackage.getPath()).lastModified()
: getLastModifiedTime(parsedPackage);
final Settings.VersionInfo settingsVersionForPackage =
mPm.getSettingsVersionForPackage(parsedPackage);
if (ps != null && !forceCollect
&& ps.getPathString().equals(parsedPackage.getPath())
&& ps.timeStamp == lastModifiedTime
&& !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !PackageManagerService.isRecoverSignatureUpdateNeeded(
settingsVersionForPackage)) {
if (ps.signatures.mSigningDetails.getSignatures() != null
&& ps.signatures.mSigningDetails.getSignatures().length != 0
&& ps.signatures.mSigningDetails.getSignatureSchemeVersion()
!= SigningDetails.SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
new SigningDetails(ps.signatures.mSigningDetails));
return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ (forceCollect ? " (forced)" : ""));
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
input, parsedPackage, skipVerify);
if (result.isError()) {
throw new PackageManagerException(
result.getErrorCode(), result.getErrorMessage(), result.getException());
}
parsedPackage.setSigningDetails(result.getResult());
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Clear the package profile if this was an upgrade and the package
* version was updated.
*/
private void maybeClearProfilesForUpgradesLI(
@Nullable PackageSetting originalPkgSetting,
@NonNull AndroidPackage pkg) {
if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
return;
}
if (originalPkgSetting.versionCode == pkg.getLongVersionCode()) {
return;
}
mPm.clearAppProfilesLIF(pkg);
if (DEBUG_INSTALL) {
Slog.d(TAG, originalPkgSetting.name
+ " clear profile due to version change "
+ originalPkgSetting.versionCode + " != "
+ pkg.getLongVersionCode());
}
}
}

View File

@@ -551,6 +551,7 @@ final class InstallParams extends HandlerParams {
new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
for (InstallRequest request : requests) {
@@ -579,7 +580,7 @@ final class InstallParams extends HandlerParams {
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
final ScanResult result = mPm.scanPackageTracedLI(
final ScanResult result = scanPackageHelper.scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -591,7 +592,8 @@ final class InstallParams extends HandlerParams {
+ " in multi-package install request.");
return;
}
createdAppId.put(packageName, mPm.optimisticallyRegisterAppId(result));
createdAppId.put(packageName,
scanPackageHelper.optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.pkg.getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.pkg));
if (result.mStaticSharedLibraryInfo != null) {
@@ -668,7 +670,7 @@ final class InstallParams extends HandlerParams {
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
false)) {
mPm.cleanUpAppIdCreation(result);
scanPackageHelper.cleanUpAppIdCreation(result);
}
}
// TODO(b/194319951): create a more descriptive reason than unknown

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,344 @@
/*
* 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.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import android.app.ResourcesManager;
import android.content.IIntentReceiver;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.UserHandle;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import com.android.internal.policy.AttributeCache;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/** Helper class to handle storage events and private apps loading */
public class StorageEventHelper extends StorageEventListener {
final PackageManagerService mPm;
// TODO(b/198166813): remove PMS dependency
public StorageEventHelper(PackageManagerService pm) {
mPm = pm;
}
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
if (vol.state == VolumeInfo.STATE_MOUNTED) {
final String volumeUuid = vol.getFsUuid();
// Clean up any users or apps that were removed or recreated
// while this volume was missing
mPm.mUserManager.reconcileUsers(volumeUuid);
reconcileApps(volumeUuid);
// Clean up any install sessions that expired or were
// cancelled while this volume was missing
mPm.mInstallerService.onPrivateVolumeMounted(volumeUuid);
loadPrivatePackages(vol);
} else if (vol.state == VolumeInfo.STATE_EJECTING) {
unloadPrivatePackages(vol);
}
}
}
@Override
public void onVolumeForgotten(String fsUuid) {
if (TextUtils.isEmpty(fsUuid)) {
Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
return;
}
// Remove any apps installed on the forgotten volume
synchronized (mPm.mLock) {
final List<PackageSetting> packages = mPm.mSettings.getVolumePackagesLPr(fsUuid);
for (PackageSetting ps : packages) {
Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
mPm.deletePackageVersioned(new VersionedPackage(ps.name,
PackageManager.VERSION_CODE_HIGHEST),
new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
AttributeCache.instance().removePackage(ps.name);
}
mPm.mSettings.onVolumeForgotten(fsUuid);
mPm.writeSettingsLPrTEMP();
}
}
private void loadPrivatePackages(final VolumeInfo vol) {
mPm.mHandler.post(() -> loadPrivatePackagesInner(vol));
}
private void loadPrivatePackagesInner(VolumeInfo vol) {
final String volumeUuid = vol.fsUuid;
if (TextUtils.isEmpty(volumeUuid)) {
Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
return;
}
final ArrayList<PackageFreezer> freezers = new ArrayList<>();
final ArrayList<AndroidPackage> loaded = new ArrayList<>();
final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
final Settings.VersionInfo ver;
final List<PackageSetting> packages;
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
synchronized (mPm.mLock) {
ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
}
for (PackageSetting ps : packages) {
freezers.add(mPm.freezePackage(ps.name, "loadPrivatePackagesInner"));
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
pkg = scanPackageHelper.scanPackageTracedLI(
ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
}
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
mPm.clearAppDataLIF(
ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
}
// Reconcile app data for all started/unlocked users
final StorageManager sm = mPm.mInjector.getSystemService(StorageManager.class);
UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal();
for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
}
try {
sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
synchronized (mPm.mInstallLock) {
mPm.reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
null);
}
} catch (IllegalStateException e) {
// Device was probably ejected, and we'll process that event momentarily
Slog.w(TAG, "Failed to prepare storage: " + e);
}
}
synchronized (mPm.mLock) {
final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
if (isUpgrade) {
logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint
+ " to " + Build.FINGERPRINT + "; regranting permissions for "
+ volumeUuid);
}
mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
// Yay, everything is now upgraded
ver.forceCurrent();
mPm.writeSettingsLPrTEMP();
}
for (PackageFreezer freezer : freezers) {
freezer.close();
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
sendResourcesChangedBroadcast(true, false, loaded, null);
synchronized (mPm.mLoadedVolumes) {
mPm.mLoadedVolumes.add(vol.getId());
}
}
private void unloadPrivatePackages(final VolumeInfo vol) {
mPm.mHandler.post(() -> unloadPrivatePackagesInner(vol));
}
private void unloadPrivatePackagesInner(VolumeInfo vol) {
final String volumeUuid = vol.fsUuid;
if (TextUtils.isEmpty(volumeUuid)) {
Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
return;
}
final int[] userIds = mPm.mUserManager.getUserIds();
final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
synchronized (mPm.mInstallLock) {
synchronized (mPm.mLock) {
final List<PackageSetting> packages =
mPm.mSettings.getVolumePackagesLPr(volumeUuid);
for (PackageSetting ps : packages) {
if (ps.pkg == null) continue;
final AndroidPackage pkg = ps.pkg;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.name, deleteFlags,
"unloadPrivatePackagesInner")) {
if (mPm.deletePackageLIF(ps.name, null, false, userIds, deleteFlags,
outInfo, false)) {
unloaded.add(pkg);
} else {
Slog.w(TAG, "Failed to unload " + ps.getPath());
}
}
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
AttributeCache.instance().removePackage(ps.name);
}
mPm.writeSettingsLPrTEMP();
}
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
sendResourcesChangedBroadcast(false, false, unloaded, null);
synchronized (mPm.mLoadedVolumes) {
mPm.mLoadedVolumes.remove(vol.getId());
}
// Try very hard to release any references to this path so we don't risk
// the system server being killed due to open FDs
ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());
for (int i = 0; i < 3; i++) {
System.gc();
System.runFinalization();
}
}
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
final int size = packages.size();
final String[] packageNames = new String[size];
final int[] packageUids = new int[size];
for (int i = 0; i < size; i++) {
final AndroidPackage pkg = packages.get(i);
packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
mPm.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
finishedReceiver);
}
/**
* Examine all apps present on given mounted volume, and destroy apps that
* aren't expected, either due to uninstallation or reinstallation on
* another volume.
*/
public void reconcileApps(String volumeUuid) {
List<String> absoluteCodePaths = collectAbsoluteCodePaths();
List<File> filesToDelete = null;
final File[] files = FileUtils.listFilesOrEmpty(
Environment.getDataAppDirectory(volumeUuid));
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
String absolutePath = file.getAbsolutePath();
boolean pathValid = false;
final int absoluteCodePathCount = absoluteCodePaths.size();
for (int i = 0; i < absoluteCodePathCount; i++) {
String absoluteCodePath = absoluteCodePaths.get(i);
if (absoluteCodePath.startsWith(absolutePath)) {
pathValid = true;
break;
}
}
if (!pathValid) {
if (filesToDelete == null) {
filesToDelete = new ArrayList<>();
}
filesToDelete.add(file);
}
}
if (filesToDelete != null) {
final int fileToDeleteCount = filesToDelete.size();
for (int i = 0; i < fileToDeleteCount; i++) {
File fileToDelete = filesToDelete.get(i);
logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete);
synchronized (mPm.mInstallLock) {
mPm.removeCodePathLI(fileToDelete);
}
}
}
}
private List<String> collectAbsoluteCodePaths() {
synchronized (mPm.mLock) {
List<String> codePaths = new ArrayList<>();
final int packageCount = mPm.mSettings.getPackagesLocked().size();
for (int i = 0; i < packageCount; i++) {
final PackageSetting ps = mPm.mSettings.getPackagesLocked().valueAt(i);
codePaths.add(ps.getPath().getAbsolutePath());
}
return codePaths;
}
}
}

View File

@@ -434,7 +434,8 @@ public class ScanTests {
final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
.addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
final ScanResult scanResult = mMockPackageManager.scanPackageOnlyLI(
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -482,7 +483,8 @@ public class ScanTests {
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
ScanResult result = mMockPackageManager.scanPackageOnlyLI(
final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
ScanResult result = scanPackageHelper.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,