diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index fd0010666e739..bf241d405dbc3 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -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.
- *
After adding, the package is known to the system and available for querying.
- *
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.
- *
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.
- *
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 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 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());
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 4a8c76b07a91e..edb76a6fb6a1d 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -551,6 +551,7 @@ final class InstallParams extends HandlerParams {
new ArrayMap<>(requests.size());
final Map 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
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2dfd409eaf886..78ea422706f74 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -32,16 +32,10 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -70,7 +64,6 @@ import static android.content.pm.PackageManager.TYPE_SERVICE;
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -89,12 +82,8 @@ import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
@@ -114,7 +103,6 @@ import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.PendingIntent;
-import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
@@ -209,7 +197,6 @@ import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.result.ParseResult;
@@ -250,7 +237,6 @@ import android.os.incremental.IncrementalManager;
import android.os.incremental.PerUidReadTimeouts;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
-import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
@@ -288,8 +274,6 @@ import android.util.TimingsTraceLog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import android.util.apk.ApkSignatureVerifier;
-import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -302,7 +286,6 @@ import com.android.internal.content.PackageHelper;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.AttributeCache;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -341,7 +324,6 @@ import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -353,7 +335,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateImpl;
-import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
@@ -786,7 +767,7 @@ public class PackageManagerService extends IPackageManager.Stub
@VisibleForTesting
final File mAppLib32InstallDir;
- private static File getAppLib32InstallDir() {
+ static File getAppLib32InstallDir() {
return new File(Environment.getDataDirectory(), "app-lib");
}
@@ -923,7 +904,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final List mDirsToScanAsSystem;
- private final OverlayConfig mOverlayConfig;
+ final OverlayConfig mOverlayConfig;
@GuardedBy("itself")
final private ArrayList mPackageChangeObservers =
@@ -1416,7 +1397,7 @@ public class PackageManagerService extends IPackageManager.Stub
new WatchedSparseBooleanArray();
@Watched(manual = true)
- private ApplicationInfo mAndroidApplication;
+ ApplicationInfo mAndroidApplication;
@Watched(manual = true)
final ActivityInfo mResolveActivity = new ActivityInfo();
final ResolveInfo mResolveInfo = new ResolveInfo();
@@ -1448,7 +1429,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PermissionManagerServiceInternal mPermissionManager;
@Watched
- private final ComponentResolver mComponentResolver;
+ final ComponentResolver mComponentResolver;
// List of packages names to keep cached, even if they are uninstalled for all users
private List mKeepUninstalledPackages;
@@ -6248,58 +6229,6 @@ public class PackageManagerService extends IPackageManager.Stub
return StorageEnums.UNKNOWN;
}
- private final StorageEventListener mStorageListener = new StorageEventListener() {
- @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
- mUserManager.reconcileUsers(volumeUuid);
- reconcileApps(volumeUuid);
-
- // Clean up any install sessions that expired or were
- // cancelled while this volume was missing
- 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 (mLock) {
- final List packages = mSettings.getVolumePackagesLPr(fsUuid);
- for (PackageSetting ps : packages) {
- Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
- deletePackageVersioned(new VersionedPackage(ps.name,
- PackageManager.VERSION_CODE_HIGHEST),
- new 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);
- }
-
- mSettings.onVolumeForgotten(fsUuid);
- writeSettingsLPrTEMP();
- }
- }
- };
-
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.mReturnCode) {
@@ -6963,8 +6892,9 @@ public class PackageManagerService extends IPackageManager.Stub
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List changedAbiCodePath =
- applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/,
- mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
+ ScanPackageHelper.applyAdjustedAbiToSharedUser(
+ setting, null /*scannedPackage*/,
+ mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
@@ -7230,11 +7160,6 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, "Fix for b/169414761 is applied");
}
-
-
-
-
-
@GuardedBy("mLock")
void updateInstantAppInstallerLocked(String modifiedPackage) {
// we're only interested in updating the installer appliction when 1) it's not
@@ -10750,52 +10675,6 @@ public class PackageManagerService extends IPackageManager.Stub
logCriticalInfo(priority, msg);
}
-
-
-
-
- /**
- * Traces a package scan.
- * @see #scanPackageLI(File, int, int, long, UserHandle)
- */
- @GuardedBy({"mInstallLock", "mLock"})
- AndroidPackage 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
- */
- @GuardedBy({"mInstallLock", "mLock"})
- private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
- if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
- parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Static shared libraries have synthetic package names
- if (parsedPackage.isStaticSharedLibrary()) {
- renameStaticSharedLibraryPackage(parsedPackage);
- }
-
- final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
- return helper.addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- mPlatformPackage, mIsUpgrade, mIsPreNMR1Upgrade);
- }
-
// TODO:(b/135203078): Move to parsing
static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
// Derive the new package synthetic package name
@@ -11630,22 +11509,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @GuardedBy("mLock")
- private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
- + " to " + newPkg.getPackageName()
- + ": old package not in system partition");
- return false;
- } else if (mPackages.get(oldPkg.name) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
- + " to " + newPkg.getPackageName()
- + ": old package still exists");
- return false;
- }
- return true;
- }
-
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
if (codePath.isDirectory()) {
@@ -12106,221 +11969,6 @@ public class PackageManagerService extends IPackageManager.Stub
return resultList;
}
- private int getVendorPartitionVersion() {
- final String version = SystemProperties.get("ro.vndk.version");
- if (!version.isEmpty()) {
- try {
- return Integer.parseInt(version);
- } catch (NumberFormatException ignore) {
- if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
- }
- }
- return Build.VERSION_CODES.P;
- }
-
- @GuardedBy({"mInstallLock", "mLock"})
- ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
- final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
- try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- cpuAbiOverride);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Returns the actual scan flags depending upon the state of the other settings.
- * Updated system applications will not have the following flags set
- * by default and need to be adjusted after the fact:
- *
- * - {@link #SCAN_AS_SYSTEM}
- * - {@link #SCAN_AS_PRIVILEGED}
- * - {@link #SCAN_AS_OEM}
- * - {@link #SCAN_AS_VENDOR}
- * - {@link #SCAN_AS_PRODUCT}
- * - {@link #SCAN_AS_SYSTEM_EXT}
- * - {@link #SCAN_AS_INSTANT_APP}
- * - {@link #SCAN_AS_VIRTUAL_PRELOAD}
- * - {@link #SCAN_AS_ODM}
- *
- */
- private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
- PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
- AndroidPackage pkg) {
-
- // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
- // the correct isSystem value now that we don't disable system packages before scan.
- final PackageSetting systemPkgSetting =
- (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
- && pkgSetting != null && pkgSetting.isSystem()
- ? pkgSetting
- : disabledPkgSetting;
- if (systemPkgSetting != null) {
- // updated system application, must at least have SCAN_AS_SYSTEM
- scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
- scanFlags |= SCAN_AS_ODM;
- }
- }
- if (pkgSetting != null) {
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (pkgSetting.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (pkgSetting.getVirtulalPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
- }
-
- // Scan as privileged apps that share a user with a priv-app.
- final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
- && getVendorPartitionVersion() < 28;
- if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
- && !pkg.isPrivileged()
- && (pkg.getSharedUserId() != null)
- && !skipVendorPrivilegeScan) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
- 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- // TODO(b/72378145) Fix this exemption. Force signature apps
- // to allowlist their privileged permissions just like other
- // priv-apps.
- synchronized (mLock) {
- PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.signatures.mSigningDetails.getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- }
- }
- }
-
- return scanFlags;
- }
-
- // 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", "mLock"})
- ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
- final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
- final String renamedPkgName = mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());
- final PackageSetting disabledPkgSetting =
- mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
-
- if (mTransferredPackages.contains(parsedPackage.getPackageName())) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " was transferred to another, but its .apk remains");
- }
-
- scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
- synchronized (mLock) {
- boolean isUpdatedSystemApp;
- if (pkgSetting != null) {
- isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
- } else {
- isUpdatedSystemApp = disabledPkgSetting != null;
- }
- applyPolicy(parsedPackage, scanFlags, mPlatformPackage, isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
-
- SharedUserSetting sharedUserSetting = null;
- if (parsedPackage.getSharedUserId() != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
- }
- }
- String platformPackageName = mPlatformPackage == null
- ? null : mPlatformPackage.getPackageName();
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
- cpuAbiOverride);
- return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
- }
- }
-
- /**
- * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
- * the app ID required for reconcile.
- * @return {@code true} if a new app ID was registered and will need to be cleaned up on
- * failure.
- */
- boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
- throws PackageManagerException {
- if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- return mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
- }
- return false;
- }
-
- /**
- * Reverts any app ID creation that were made by
- * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
- * referenced method returned true.
- */
- void cleanUpAppIdCreation(@NonNull ScanResult result) {
- // iff we've acquired an app ID for a new package setting, remove it so that it can be
- // acquired by another request.
- if (result.mPkgSetting.appId > 0) {
- mSettings.removeAppIdLPw(result.mPkgSetting.appId);
- }
- }
-
/**
* Commits the package scan and modifies system state.
* WARNING: The method may throw an excpetion in the middle
@@ -12445,1024 +12093,6 @@ public class PackageManagerService extends IPackageManager.Stub
return pkg;
}
- /**
- * Returns the "real" name of the package.
- *
This may differ from the package's actual name if the application has already
- * been installed under one of this package's original names.
- */
- static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return AndroidPackageUtils.getRealPackageOrNull(pkg);
- }
- return null;
- }
-
- /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
- private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- return pkg.getOriginalPackages().contains(renamedPkgName);
- }
-
- /**
- * Returns the original package setting.
- *
A package can migrate its name during an update. In this scenario, a package
- * designates a set of names that it considers as one of its original names.
- *
An original package must be signed identically and it must have the same
- * shared user [if any].
- */
- @GuardedBy("mLock")
- @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return null;
- }
- for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
- final PackageSetting originalPs =
- mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
- if (originalPs != null) {
- // the package is already installed under its original name...
- // but, should we use it?
- if (!verifyPackageUpdateLPr(originalPs, pkg)) {
- // the new package is incompatible with the original
- continue;
- } else if (originalPs.sharedUser != null) {
- if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
- // the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.name
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.sharedUser.name
- + " differs from " + pkg.getSharedUserId());
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name " + originalPs.name);
- }
- return originalPs;
- }
- }
- return null;
- }
-
- /**
- * Renames the package if it was installed under a different name.
- *
When we've already installed the package under an original name, update
- * the new package so we can continue to have the old name.
- */
- static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
- @NonNull String renamedPackageName) {
- if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
- || parsedPackage.getPackageName().equals(renamedPackageName)) {
- return;
- }
- parsedPackage.setPackageName(renamedPackageName);
- }
-
- /**
- * Applies the adjusted ABI calculated by
- * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
- * relevant packages and settings.
- * @param sharedUserSetting The {@code SharedUserSetting} to adjust
- * @param scannedPackage the package being scanned or null
- * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
- * @return the list of code paths that belong to packages that had their ABIs adjusted.
- */
- private static List applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
- ParsedPackage scannedPackage, String adjustedAbi) {
- if (scannedPackage != null) {
- scannedPackage.setPrimaryCpuAbi(adjustedAbi);
- }
- List changedAbiCodePath = null;
- for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
- if (ps.primaryCpuAbiString != null) {
- continue;
- }
-
- ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null) {
- if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.getPathString());
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
- /**
- * Sets the enabled state of components configured through {@link SystemConfig}.
- * This modifies the {@link PackageSetting} object.
- *
- * TODO(b/135203078): Move this to package parsing
- **/
- static void configurePackageComponents(AndroidPackage pkg) {
- final ArrayMap componentsEnabledStates = SystemConfig.getInstance()
- .getComponentsEnabledStates(pkg.getPackageName());
- if (componentsEnabledStates == null) {
- return;
- }
-
- for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
- final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
- final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
- }
-
- /**
- * Just scans the package without any side effects.
- * Not entirely true at the moment. There is still one side effect -- this
- * method potentially modifies a live {@link PackageSetting} object representing
- * the package being scanned. This will be resolved in the future.
- *
- * @param injector injector for acquiring dependencies
- * @param request Information about the package to be scanned
- * @param isUnderFactoryTest Whether or not the device is under factory test
- * @param currentTime The current time, in millis
- * @return The results of the scan
- */
- @GuardedBy("mInstallLock")
- @VisibleForTesting
- @NonNull
- ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- Injector injector,
- boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
- final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- ParsedPackage parsedPackage = request.mParsedPackage;
- PackageSetting pkgSetting = request.mPkgSetting;
- final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
- final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
- final @ParseFlags int parseFlags = request.mParseFlags;
- final @ScanFlags int scanFlags = request.mScanFlags;
- final String realPkgName = request.mRealPkgName;
- final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
- final UserHandle user = request.mUser;
- final boolean isPlatformPackage = request.mIsPlatformPackage;
-
- List changedAbiCodePath = null;
-
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
- }
- }
-
- // Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getPath());
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if (!needToDeriveAbi) {
- if (pkgSetting != null) {
- // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
- // API info from existing package setting. However, stub packages currently do not
- // preserve ABI info, thus the special condition check here. Remove the special
- // check after we fix the stub generation.
- if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
- needToDeriveAbi = true;
- } else {
- primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
- secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
- }
- } else {
- // Re-scanning a system package after uninstalling updates; need to derive ABI
- needToDeriveAbi = true;
- }
- }
-
- boolean leavingSharedUser = false;
-
- if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
- if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
- leavingSharedUser = true;
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.sharedUser.name + " to " + ".");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.sharedUser != null
- ? pkgSetting.sharedUser.name : "")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "")
- + "; replacing with new");
- pkgSetting = null;
- }
- }
-
- String[] usesStaticLibraries = null;
- if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
- usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
- parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
- }
-
- final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
-
- // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
- // to avoid adding something that's unsupported due to lack of state, since it's called
- // with null.
- final boolean createNewPackage = (pkgSetting == null);
- if (createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-
- // Flags contain system values stored in the server variant of AndroidPackage,
- // and so the server-side PackageInfoUtils is still called, even without a
- // PackageSetting to pass in.
- int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
- int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
-
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
- originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryRootDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
- } else {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.pkg = parsedPackage;
-
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
- UserManagerService.getInstance(),
- usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
- }
- if (createNewPackage && originalPkgSetting != null) {
- // This is the initial transition from the original package, so,
- // fix up the new package's name now. We must do this after looking
- // up the package under its new name, so getPackageLP takes care of
- // fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.name);
-
- // File a report about this.
- String msg = "New package " + pkgSetting.realName
- + " renamed to replace old package " + pkgSetting.name;
- reportSettingsProblem(Log.WARN, msg);
- }
-
- final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
- // for existing packages, change the install state; but, only if it's explicitly specified
- if (!createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
- }
- // TODO(patb): see if we can do away with disabled check here.
- if (disabledPkgSetting != null
- || (0 != (scanFlags & SCAN_NEW_INSTALL)
- && pkgSetting != null && pkgSetting.isSystem())) {
- pkgSetting.getPkgState().setUpdatedSystemApp(true);
- }
-
- parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
- injector.getCompatibility()));
-
- if (parsedPackage.isSystem()) {
- configurePackageComponents(parsedPackage);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
- final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
-
- final File appLib32InstallDir = getAppLib32InstallDir();
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final Pair derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride, appLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
-
- String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !isUpdatedSystemApp
- && pkgRawPrimaryCpuAbi == null) {
- final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
- parsedPackage);
- abis.applyTo(parsedPackage);
- abis.applyTo(pkgSetting);
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
- .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
-
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
- parsedPackage.getPackageName() + " " +
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
- + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
- .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (isPlatformPackage) {
- parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?
- Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null) {
- Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
- " for package " + parsedPackage.getPackageName());
- }
- }
-
- pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
- pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir()
- + ", to dir=" + parsedPackage.getNativeLibraryDir()
- + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.primaryCpuAbiString
- + " secondary=" + pkgSetting.primaryCpuAbiString
- + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
- packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.sharedUser.packages, parsedPackage));
- }
-
- parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
- .contains(android.Manifest.permission.FACTORY_TEST));
-
- if (parsedPackage.isSystem()) {
- pkgSetting.setIsOrphaned(true);
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(parsedPackage);
- if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
- }
- } else if (pkgSetting.firstInstallTime == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
- } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.timeStamp) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.lastUpdateTime = scanFileTime;
- }
- }
- pkgSetting.setTimeStamp(scanFileTime);
- // TODO(b/135203078): Remove, move to constructor
- pkgSetting.pkg = parsedPackage;
- pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
- pkgSetting.pkgPrivateFlags =
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
- if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
- pkgSetting.versionCode = parsedPackage.getLongVersionCode();
- }
- // Update volume if needed
- final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
- Slog.i(PackageManagerService.TAG,
- "Update" + (pkgSetting.isSystem() ? " system" : "")
- + " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.volumeUuid
- + " to " + volumeUuid);
- pkgSetting.volumeUuid = volumeUuid;
- }
-
- SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo =
- AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
- }
- List dynamicSharedLibraryInfos = null;
- if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
- dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
- for (String name : parsedPackage.getLibraryNames()) {
- dynamicSharedLibraryInfos.add(
- AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
- }
- }
-
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */,
- leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
- }
-
- /**
- * Returns {@code true} if the given file contains code. Otherwise {@code false}.
- */
- private static boolean apkHasCode(String fileName) {
- StrictJarFile jarFile = null;
- try {
- jarFile = new StrictJarFile(fileName,
- false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
- return jarFile.findEntry("classes.dex") != null;
- } catch (IOException ignore) {
- } finally {
- try {
- if (jarFile != null) {
- jarFile.close();
- }
- } catch (IOException ignore) {}
- }
- return false;
- }
-
- /**
- * Enforces code policy for the package. This ensures that if an APK has
- * declared hasCode="true" in its manifest that the APK actually contains
- * code.
- *
- * @throws PackageManagerException If bytecode could not be found when it should exist
- */
- private static void assertCodePolicy(AndroidPackage pkg)
- throws PackageManagerException {
- final boolean shouldHaveCode = pkg.isHasCode();
- if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getBaseApkPath() + " code is missing");
- }
-
- if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
- for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
- final boolean splitShouldHaveCode =
- (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
- if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
- }
- }
- }
- }
-
- /**
- * Applies policy to the parsed package based upon the given policy flags.
- * Ensures the package is in a good state.
- *
- * Implementation detail: This method must NOT have any side effect. It would
- * ideally be static, but, it requires locks to read system state.
- */
- static void applyPolicy(ParsedPackage parsedPackage,
- final @ScanFlags int scanFlags, AndroidPackage platformPkg,
- boolean isUpdatedSystemApp) {
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- parsedPackage.setSystem(true);
- // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
- // is set during parse.
- if (parsedPackage.isDirectBootAware()) {
- parsedPackage.setAllComponentsDirectBootAware(true);
- }
- if (compressedFileExists(parsedPackage.getPath())) {
- parsedPackage.setStub(true);
- }
- } else {
- parsedPackage
- // Non system apps cannot mark any broadcast as protected
- .clearProtectedBroadcasts()
- // non system apps can't be flagged as core
- .setCoreApp(false)
- // clear flags not applicable to regular apps
- .setPersistent(false)
- .setDefaultToDeviceProtectedStorage(false)
- .setDirectBootAware(false)
- // non system apps can't have permission priority
- .capPermissionPriorities();
- }
- if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
- parsedPackage
- .markNotActivitiesAsNotExportedIfSingleUser();
- }
-
- parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
- .setOem((scanFlags & SCAN_AS_OEM) != 0)
- .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
- .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
- .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
- .setOdm((scanFlags & SCAN_AS_ODM) != 0);
-
- // Check if the package is signed with the same key as the platform package.
- parsedPackage.setSignedWithPlatformKey(
- (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
- || (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
- ) == PackageManager.SIGNATURE_MATCH))
- );
-
- if (!parsedPackage.isSystem()) {
- // Only system apps can use these features.
- parsedPackage.clearOriginalPackages()
- .clearAdoptPermissions();
- }
-
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
- }
-
- private
- void assertPackageProcesses(AndroidPackage pkg, List components,
- Map procs, String compName)
- throws PackageManagerException {
- if (components == null) {
- return;
- }
- for (int i = components.size() - 1; i >= 0; i--) {
- final ParsedMainComponent component = components.get(i);
- if (!procs.containsKey(component.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.getClassName()
- + "'s process attribute " + component.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the list");
- }
- }
- }
-
- /**
- * Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackageManagerException}.
- *
- * Implementation detail: This method must NOT have any side effects. It would
- * ideally be static, but, it requires locks to read system state.
- *
- * @throws PackageManagerException If the package fails any of the validation checks
- */
- private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags)
- throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
- assertCodePolicy(pkg);
- }
-
- if (pkg.getPath() == null) {
- // Bail out. The resource and code paths haven't been set.
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Code and resource paths haven't been set correctly");
- }
-
- // Check that there is an APEX package with the same name only during install/first boot
- // after OTA.
- final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
- final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if ((isUserInstall || isFirstBootOrUpgrade)
- && mApexManager.isApexPackage(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
- }
-
- // Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- ksms.assertScannedPackageValid(pkg);
-
- synchronized (mLock) {
- // The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
- if (mAndroidApplication != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " codePath=" + pkg.getPath());
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
- }
-
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
- }
-
- if (pkg.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // but we still want the base name to be unique.
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPackages.containsKey(pkg.getManifestPackageName())) {
- throw new PackageManagerException(
- "Duplicate static shared lib provider package");
- }
-
- // Static shared libraries should have at least O target SDK
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs must target O SDK or higher");
- }
-
- // Package declaring static a shared lib cannot be instant apps
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be instant apps");
- }
-
- // Package declaring static a shared lib cannot be renamed since the package
- // name is synthetic and apps can't code around package manager internals.
- if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be renamed");
- }
-
- // Package declaring static a shared lib cannot declare dynamic libs
- if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare dynamic libs");
- }
-
- // Package declaring static a shared lib cannot declare shared users
- if (pkg.getSharedUserId() != null) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare shared users");
- }
-
- // Static shared libs cannot declare activities
- if (!pkg.getActivities().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare activities");
- }
-
- // Static shared libs cannot declare services
- if (!pkg.getServices().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare services");
- }
-
- // Static shared libs cannot declare providers
- if (!pkg.getProviders().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare content providers");
- }
-
- // Static shared libs cannot declare receivers
- if (!pkg.getReceivers().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare broadcast receivers");
- }
-
- // Static shared libs cannot declare permission groups
- if (!pkg.getPermissionGroups().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permission groups");
- }
-
- // Static shared libs cannot declare attributions
- if (!pkg.getAttributions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare features");
- }
-
- // Static shared libs cannot declare permissions
- if (!pkg.getPermissions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permissions");
- }
-
- // Static shared libs cannot declare protected broadcasts
- if (!pkg.getProtectedBroadcasts().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare protected broadcasts");
- }
-
- // Static shared libs cannot be overlay targets
- if (pkg.getOverlayTarget() != null) {
- throw new PackageManagerException(
- "Static shared libs cannot be overlay targets");
- }
-
- // The version codes must be ordered as lib versions
- long minVersionCode = Long.MIN_VALUE;
- long maxVersionCode = Long.MAX_VALUE;
-
- WatchedLongSparseArray versionedLib = mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib != null) {
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(i);
- final long libVersionCode = libInfo.getDeclaringPackage()
- .getLongVersionCode();
- if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
- minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
- } else if (libInfo.getLongVersion()
- > pkg.getStaticSharedLibVersion()) {
- maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
- } else {
- minVersionCode = maxVersionCode = libVersionCode;
- break;
- }
- }
- }
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
- throw new PackageManagerException("Static shared"
- + " lib version codes must be ordered as lib versions");
- }
- }
-
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mExpectingBetter.containsKey(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
- } else {
- PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.getPath()
- + " and requiring known path " + known.getPathString());
- }
- if (!pkg.getPath().equals(known.getPathString())) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
- + " found at " + pkg.getPath()
- + " but expected at " + known.getPathString()
- + "; ignoring.");
- }
- } else {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
- + " not found; ignoring.");
- }
- }
- }
-
- // Verify that this new package doesn't have any content providers
- // that conflict with existing packages. Only do this if the
- // package isn't already installed, since we don't want to break
- // things that are installed.
- if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
- mComponentResolver.assertProvidersNotDefined(pkg);
- }
-
- // If this package has defined explicit processes, then ensure that these are
- // the only processes used by its components.
- final Map procs = pkg.getProcesses();
- if (!procs.isEmpty()) {
- if (!procs.containsKey(pkg.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because application tag's process attribute "
- + pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the list");
- }
- assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
- assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
- assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
- assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
- }
-
- // Verify that packages sharing a user with a privileged app are marked as privileged.
- if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(),
- 0, 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Apps that share a user with a " +
- "privileged app must themselves be marked as privileged. " +
- pkg.getPackageName() + " shares privileged user " +
- pkg.getSharedUserId() + ".");
- }
- }
- }
-
- // Apply policies specific for runtime resource overlays (RROs).
- if (pkg.getOverlayTarget() != null) {
- // System overlays have some restrictions on their use of the 'static' state.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // We are scanning a system overlay. This can be the first scan of the
- // system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay. Immutable overlays cannot be
- // upgraded.
- Objects.requireNonNull(mOverlayConfig,
- "Parsing non-system dir before overlay configs are initialized");
- if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static and cannot be upgraded.");
- }
- } else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
- if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of vendor"
- + " overlays (" + getVendorPartitionVersion() + ")."
- + " This will become an install error in a future release");
- }
- } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of system"
- + " overlays (" + Build.VERSION.SDK_INT + ")."
- + " This will become an install error in a future release");
- }
- }
- } else {
- // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
- // signed with the platform certificate. Check this in increasing order of
- // computational cost.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
- final PackageSetting platformPkgSetting =
- mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " must target Q or later, "
- + "or be signed with the platform certificate");
- }
- }
-
- // A non-preloaded overlay package, without , will
- // only be used if it is signed with the same certificate as its target OR if
- // it is signed with the same certificate as a reference package declared
- // in 'overlay-config-signature' tag of SystemConfig.
- // If the target is already installed or 'overlay-config-signature' tag in
- // SystemConfig is set, check this here to augment the last line of defense
- // which is OMS.
- if (pkg.getOverlayTargetOverlayableName() == null) {
- final PackageSetting targetPkgSetting =
- mSettings.getPackageLPr(pkg.getOverlayTarget());
- if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- // check reference signature
- if (mOverlayConfigSignaturePackage == null) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
- + pkg.getOverlayTarget() + " signed with"
- + " different certificates, and the overlay lacks"
- + " ");
- }
- final PackageSetting refPkgSetting =
- mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
- + "certificate than both the reference package and "
- + "target " + pkg.getOverlayTarget() + ", and the "
- + "overlay lacks ");
- }
- }
- }
- }
- }
- }
-
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
- }
- }
- }
-
@GuardedBy("mLock")
private void addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
if (nonStaticSharedLibExistsLocked(entry.name)) {
@@ -16302,6 +14932,7 @@ public class PackageManagerService extends IPackageManager.Stub
int[] allUsers;
final int freezeUser;
final SparseArray priorUserStates;
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
/** enabled state of the uninstalled application */
synchronized (mLock) {
uninstalledPs = mSettings.getPackageLPr(packageName);
@@ -16436,8 +15067,6 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- final InitAndSystemPackageHelper helper =
- new InitAndSystemPackageHelper(this);
helper.enableCompressedPackage(stubPkg, stubPs, mDefParseFlags,
mDirsToScanAsSystem);
} else if (DEBUG_COMPRESSION) {
@@ -18445,6 +17074,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
// More work for application enabled setting updates
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
@@ -18470,7 +17100,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
if (!helper.enableCompressedPackage(deletedPkg, pkgSetting, mDefParseFlags,
mDirsToScanAsSystem)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
@@ -19014,15 +17643,16 @@ public class PackageManagerService extends IPackageManager.Stub
mUserManager.systemReady();
// Watch for external volumes that come and go over time
+ final StorageEventHelper storageEventHelper = new StorageEventHelper(this);
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
- storage.registerListener(mStorageListener);
+ storage.registerListener(storageEventHelper);
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
// Now that we're mostly running, clean up stale users and apps
mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
- reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
+ storageEventHelper.reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.onSystemReady();
@@ -19892,29 +18522,13 @@ public class PackageManagerService extends IPackageManager.Stub
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
- private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
-
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList 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();
- }
- sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
- finishedReceiver);
- }
-
void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
sendResourcesChangedBroadcast(mediaStatus, replacing,
pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
int size = pkgList.length;
if (size > 0) {
@@ -19935,158 +18549,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void loadPrivatePackages(final VolumeInfo vol) {
- 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 freezers = new ArrayList<>();
- final ArrayList loaded = new ArrayList<>();
- final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
-
- final VersionInfo ver;
- final List packages;
- synchronized (mLock) {
- ver = mSettings.findOrCreateVersion(volumeUuid);
- packages = mSettings.getVolumePackagesLPr(volumeUuid);
- }
-
- for (PackageSetting ps : packages) {
- freezers.add(freezePackage(ps.name, "loadPrivatePackagesInner"));
- synchronized (mInstallLock) {
- final AndroidPackage pkg;
- try {
- pkg = 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)) {
- 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 = mInjector.getSystemService(StorageManager.class);
- UserManagerInternal umInternal = mInjector.getUserManagerInternal();
- for (UserInfo user : 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 (mInstallLock) {
- 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 (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);
- }
- mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
-
- // Yay, everything is now upgraded
- ver.forceCurrent();
-
- writeSettingsLPrTEMP();
- }
-
- for (PackageFreezer freezer : freezers) {
- freezer.close();
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- sendResourcesChangedBroadcast(true, false, loaded, null);
- synchronized (mLoadedVolumes) {
- mLoadedVolumes.add(vol.getId());
- }
- }
-
- private void unloadPrivatePackages(final VolumeInfo vol) {
- 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 = mUserManager.getUserIds();
- final ArrayList unloaded = new ArrayList<>();
- synchronized (mInstallLock) {
- synchronized (mLock) {
- final List packages = 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(this);
-
- try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
- "unloadPrivatePackagesInner")) {
- if (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);
- }
-
- writeSettingsLPrTEMP();
- }
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- sendResourcesChangedBroadcast(false, false, unloaded, null);
- synchronized (mLoadedVolumes) {
- 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 assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mLock) {
@@ -20107,17 +18569,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private List collectAbsoluteCodePaths() {
- synchronized (mLock) {
- List codePaths = new ArrayList<>();
- final int packageCount = mSettings.getPackagesLocked().size();
- for (int i = 0; i < packageCount; i++) {
- final PackageSetting ps = mSettings.getPackagesLocked().valueAt(i);
- codePaths.add(ps.getPath().getAbsolutePath());
- }
- return codePaths;
- }
- }
private void executeBatchLI(@NonNull Installer.Batch batch) {
try {
@@ -20127,57 +18578,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /**
- * Examine all apps present on given mounted volume, and destroy apps that
- * aren't expected, either due to uninstallation or reinstallation on
- * another volume.
- */
- private void reconcileApps(String volumeUuid) {
- List absoluteCodePaths = collectAbsoluteCodePaths();
- List 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 (mInstallLock) {
- removeCodePathLI(fileToDelete);
- }
- }
- }
- }
-
/**
* Reconcile all app data for the given user.
*
@@ -20205,7 +18605,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mInstallLock")
- private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
+ void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData, @Nullable ArraySet reconciledPackages) {
reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */,
reconciledPackages);
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
new file mode 100644
index 0000000000000..5fab84e189628
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -0,0 +1,1822 @@
+/*
+ * 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.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
+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_UPGRADE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+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.SCAN_UPDATE_TIME;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+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.utils.WatchedLongSparseArray;
+
+import dalvik.system.VMRuntime;
+
+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.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that handles package scanning logic
+ */
+public final class ScanPackageHelper {
+ final PackageManagerService mPm;
+
+ // TODO(b/198166813): remove PMS dependency
+ public ScanPackageHelper(PackageManagerService pm) {
+ mPm = pm;
+ }
+
+ /**
+ * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
+ * the app ID required for reconcile.
+ * @return {@code true} if a new app ID was registered and will need to be cleaned up on
+ * failure.
+ */
+ public boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
+ throws PackageManagerException {
+ if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
+ }
+ return false;
+ }
+
+ /**
+ * Reverts any app ID creation that were made by
+ * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
+ * referenced method returned true.
+ */
+ public void cleanUpAppIdCreation(@NonNull ScanResult result) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.mPkgSetting.appId > 0) {
+ mPm.mSettings.removeAppIdLPw(result.mPkgSetting.appId);
+ }
+ }
+
+ /**
+ * Traces a package scan.
+ * @see #scanPackageLI(File, int, int, long, UserHandle)
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public AndroidPackage 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);
+ }
+ }
+
+ /**
+ * Similar to the other scanPackageTracedLI but accepting a ParsedPackage instead of a File.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ try {
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
+ } 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
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
+ long currentTime, UserHandle user) throws PackageManagerException {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
+ parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Static shared libraries have synthetic package names
+ if (parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+ }
+
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ mPm.mPlatformPackage, mPm.mIsUpgrade, mPm.mIsPreNMR1Upgrade);
+ }
+
+ // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
+ // But, first, committing 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({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+
+ final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
+ if (realPkgName != null) {
+ ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
+ renamedPkgName);
+ final PackageSetting pkgSetting =
+ mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
+ final PackageSetting disabledPkgSetting =
+ mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
+
+ if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " was transferred to another, but its .apk remains");
+ }
+
+ scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
+ synchronized (mPm.mLock) {
+ boolean isUpdatedSystemApp;
+ if (pkgSetting != null) {
+ isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+ applyPolicy(parsedPackage, scanFlags, mPm.mPlatformPackage, isUpdatedSystemApp);
+ assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
+
+ SharedUserSetting sharedUserSetting = null;
+ if (parsedPackage.getSharedUserId() != null) {
+ // SIDE EFFECTS; may potentially allocate a new shared user
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+ }
+ String platformPackageName = mPm.mPlatformPackage == null
+ ? null : mPm.mPlatformPackage.getPackageName();
+ final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+ pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+ originalPkgSetting, realPkgName, parseFlags, scanFlags,
+ Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
+ cpuAbiOverride);
+ return scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest, currentTime);
+ }
+ }
+
+ /**
+ * Just scans the package without any side effects.
+ * Not entirely true at the moment. There is still one side effect -- this
+ * method potentially modifies a live {@link PackageSetting} object representing
+ * the package being scanned. This will be resolved in the future.
+ *
+ * @param injector injector for acquiring dependencies
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mPm.mInstallLock")
+ @VisibleForTesting
+ @NonNull
+ public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ PackageManagerService.Injector injector,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+ final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
+
+ List changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
+ }
+
+ // Initialize package source and resource directories
+ final File destCodeFile = new File(parsedPackage.getPath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
+ // API info from existing package setting. However, stub packages currently do not
+ // preserve ABI info, thus the special condition check here. Remove the special
+ // check after we fix the stub generation.
+ if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
+ needToDeriveAbi = true;
+ } else {
+ primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
+ secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+ }
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ boolean leavingSharedUser = false;
+
+ if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
+ if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
+ leavingSharedUser = true;
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.sharedUser.name + " to " + ".");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.sharedUser != null
+ ? pkgSetting.sharedUser.name : "")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ }
+
+ String[] usesStaticLibraries = null;
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
+ usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+ parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
+ }
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+ originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesStaticLibraries,
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
+ } else {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.pkg = parsedPackage;
+
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
+ UserManagerService.getInstance(),
+ usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
+ parsedPackage.getMimeGroups(), newDomainSetId);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
+ parsedPackage.setPackageName(originalPkgSetting.name);
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.realName
+ + " renamed to replace old package " + pkgSetting.name;
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+
+ final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
+ // for existing packages, change the install state; but, only if it's explicitly specified
+ if (!createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ PackageManagerService.setInstantAppForUser(
+ injector, pkgSetting, userId, instantApp, fullApp);
+ }
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
+ }
+
+ parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
+ injector.getCompatibility()));
+
+ if (parsedPackage.isSystem()) {
+ configurePackageComponents(parsedPackage);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+
+ final File appLib32InstallDir = PackageManagerService.getAppLib32InstallDir();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final Pair derivedAbi =
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride, appLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ parsedPackage);
+ abis.applyTo(parsedPackage);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+ .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
+
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : "
+ + parsedPackage.getPackageName() + " "
+ + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new code path.
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
+ .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
+ appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
+ ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null) {
+ Slog.w(TAG, "Ignoring persisted ABI override for package "
+ + parsedPackage.getPackageName());
+ }
+ }
+
+ pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
+ pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.primaryCpuAbiString
+ + " secondary=" + pkgSetting.primaryCpuAbiString
+ + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
+ packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.sharedUser.packages, parsedPackage));
+ }
+
+ parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+ .contains(android.Manifest.permission.FACTORY_TEST));
+
+ if (parsedPackage.isSystem()) {
+ pkgSetting.setIsOrphaned(true);
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(parsedPackage);
+ if (currentTime != 0) {
+ if (pkgSetting.firstInstallTime == 0) {
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.lastUpdateTime = currentTime;
+ }
+ } else if (pkgSetting.firstInstallTime == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.timeStamp) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.lastUpdateTime = scanFileTime;
+ }
+ }
+ pkgSetting.setTimeStamp(scanFileTime);
+ // TODO(b/135203078): Remove, move to constructor
+ pkgSetting.pkg = parsedPackage;
+ pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
+ pkgSetting.pkgPrivateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
+ if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
+ pkgSetting.versionCode = parsedPackage.getLongVersionCode();
+ }
+ // Update volume if needed
+ final String volumeUuid = parsedPackage.getVolumeUuid();
+ if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + parsedPackage.getPackageName()
+ + " volume from " + pkgSetting.volumeUuid
+ + " to " + volumeUuid);
+ pkgSetting.volumeUuid = volumeUuid;
+ }
+
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
+ }
+ List dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+ dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+ for (String name : parsedPackage.getLibraryNames()) {
+ dynamicSharedLibraryInfos.add(
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
+ }
+ }
+
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */,
+ leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
+ }
+
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ *
+ * - {@link PackageManagerService.SCAN_AS_SYSTEM}
+ * - {@link PackageManagerService.SCAN_AS_PRIVILEGED}
+ * - {@link PackageManagerService.SCAN_AS_OEM}
+ * - {@link PackageManagerService.SCAN_AS_VENDOR}
+ * - {@link PackageManagerService.SCAN_AS_PRODUCT}
+ * - {@link PackageManagerService.SCAN_AS_SYSTEM_EXT}
+ * - {@link PackageManagerService.SCAN_AS_INSTANT_APP}
+ * - {@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}
+ * - {@link PackageManagerService.SCAN_AS_ODM}
+ *
+ */
+ private @PackageManagerService.ScanFlags int adjustScanFlags(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
+ AndroidPackage pkg) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+ scanFlags |= SCAN_AS_PRODUCT;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
+ scanFlags |= SCAN_AS_SYSTEM_EXT;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtulalPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+
+ // Scan as privileged apps that share a user with a priv-app.
+ final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
+ && getVendorPartitionVersion() < 28;
+ if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
+ && !pkg.isPrivileged()
+ && (pkg.getSharedUserId() != null)
+ && !skipVendorPrivilegeScan) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+ 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ // TODO(b/72378145) Fix this exemption. Force signature apps
+ // to allowlist their privileged permissions just like other
+ // priv-apps.
+ synchronized (mPm.mLock) {
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if ((compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ pkg.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ }
+ }
+ }
+
+ return scanFlags;
+ }
+
+
+ /**
+ * Adds a new package to the internal data structures during platform initialization.
+ * After adding, the package is known to the system and available for querying.
+ *
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.
+ *
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.
+ *
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 = getRealPackageName(parsedPackage,
+ renamedPkgName);
+ if (realPkgName != null) {
+ ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = 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);
+ applyPolicy(parsedPackage, scanFlags,
+ platformPackage, true);
+ final ScanResult scanResult =
+ 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 = 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 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 = optimisticallyRegisterAppId(scanResult);
+ mPm.commitReconciledScanResultLocked(
+ reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+ } catch (PackageManagerException e) {
+ if (appIdCreated) {
+ 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 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());
+ }
+ }
+
+ /**
+ * Returns the original package setting.
+ * A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ *
An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable
+ private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return null;
+ }
+ for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.sharedUser != null) {
+ if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ + " to " + pkg.getPackageName() + ": old uid "
+ + originalPs.sharedUser.name
+ + " differs from " + pkg.getSharedUserId());
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) {
+ Log.v(TAG, "Renaming new package "
+ + pkg.getPackageName() + " to old name " + originalPs.name);
+ }
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mPm.mLock")
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
+ if ((oldPkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.getPackageName()
+ + ": old package not in system partition");
+ return false;
+ } else if (mPm.mPackages.get(oldPkg.name) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.getPackageName()
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Asserts the parsed package is valid according to the given policy. If the
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
+ *
+ * Implementation detail: This method must NOT have any side effects. It would
+ * ideally be static, but, it requires locks to read system state.
+ *
+ * @throws PackageManagerException If the package fails any of the validation checks
+ */
+ private void assertPackageIsValid(AndroidPackage pkg,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ final @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
+ assertCodePolicy(pkg);
+ }
+
+ if (pkg.getPath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Check that there is an APEX package with the same name only during install/first boot
+ // after OTA.
+ final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
+ final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if ((isUserInstall || isFirstBootOrUpgrade)
+ && mPm.mApexManager.isApexPackage(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ pkg.getPackageName()
+ + " is an APEX package and can't be installed as an APK.");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ ksms.assertScannedPackageValid(pkg);
+
+ synchronized (mPm.mLock) {
+ // The special "android" package can only be defined once
+ if (pkg.getPackageName().equals("android")) {
+ if (mPm.mAndroidApplication != null) {
+ Slog.w(TAG, "*************************************************");
+ Slog.w(TAG, "Core android package being redefined. Skipping.");
+ Slog.w(TAG, " codePath=" + pkg.getPath());
+ Slog.w(TAG, "*************************************************");
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Core android package being redefined. Skipping.");
+ }
+ }
+
+ // A package name must be unique; don't allow duplicates
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.getPackageName()
+ + " already installed. Skipping duplicate.");
+ }
+
+ if (pkg.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // but we still want the base name to be unique.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
+ throw new PackageManagerException(
+ "Duplicate static shared lib provider package");
+ }
+
+ // Static shared libraries should have at least O target SDK
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs must target O SDK or higher");
+ }
+
+ // Package declaring static a shared lib cannot be instant apps
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be instant apps");
+ }
+
+ // Package declaring static a shared lib cannot be renamed since the package
+ // name is synthetic and apps can't code around package manager internals.
+ if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be renamed");
+ }
+
+ // Package declaring static a shared lib cannot declare dynamic libs
+ if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare dynamic libs");
+ }
+
+ // Package declaring static a shared lib cannot declare shared users
+ if (pkg.getSharedUserId() != null) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare shared users");
+ }
+
+ // Static shared libs cannot declare activities
+ if (!pkg.getActivities().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare activities");
+ }
+
+ // Static shared libs cannot declare services
+ if (!pkg.getServices().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare services");
+ }
+
+ // Static shared libs cannot declare providers
+ if (!pkg.getProviders().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare content providers");
+ }
+
+ // Static shared libs cannot declare receivers
+ if (!pkg.getReceivers().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare broadcast receivers");
+ }
+
+ // Static shared libs cannot declare permission groups
+ if (!pkg.getPermissionGroups().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permission groups");
+ }
+
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare features");
+ }
+
+ // Static shared libs cannot declare permissions
+ if (!pkg.getPermissions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permissions");
+ }
+
+ // Static shared libs cannot declare protected broadcasts
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare protected broadcasts");
+ }
+
+ // Static shared libs cannot be overlay targets
+ if (pkg.getOverlayTarget() != null) {
+ throw new PackageManagerException(
+ "Static shared libs cannot be overlay targets");
+ }
+
+ // The version codes must be ordered as lib versions
+ long minVersionCode = Long.MIN_VALUE;
+ long maxVersionCode = Long.MAX_VALUE;
+
+ WatchedLongSparseArray versionedLib = mPm.mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(i);
+ final long libVersionCode = libInfo.getDeclaringPackage()
+ .getLongVersionCode();
+ if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getLongVersion()
+ > pkg.getStaticSharedLibVersion()) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.getLongVersionCode() < minVersionCode
+ || pkg.getLongVersionCode() > maxVersionCode) {
+ throw new PackageManagerException("Static shared"
+ + " lib version codes must be ordered as lib versions");
+ }
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mPm.mExpectingBetter.containsKey(pkg.getPackageName())) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
+ } else {
+ PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.getPath()
+ + " and requiring known path " + known.getPathString());
+ }
+ if (!pkg.getPath().equals(known.getPathString())) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.getPackageName()
+ + " found at " + pkg.getPath()
+ + " but expected at " + known.getPathString()
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.getPackageName()
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ mPm.mComponentResolver.assertProvidersNotDefined(pkg);
+ }
+
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ final Map procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+ 0, 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Apps that share a user with a "
+ + "privileged app must themselves be marked as privileged. "
+ + pkg.getPackageName() + " shares privileged user "
+ + pkg.getSharedUserId() + ".");
+ }
+ }
+ }
+
+ // Apply policies specific for runtime resource overlays (RROs).
+ if (pkg.getOverlayTarget() != null) {
+ // System overlays have some restrictions on their use of the 'static' state.
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ // We are scanning a system overlay. This can be the first scan of the
+ // system/vendor/oem partition, or an update to the system overlay.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ Objects.requireNonNull(mPm.mOverlayConfig,
+ "Parsing non-system dir before overlay configs are initialized");
+ if (!mPm.mOverlayConfig.isMutable(pkg.getPackageName())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " is static and cannot be upgraded.");
+ }
+ } else {
+ if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of vendor"
+ + " overlays (" + getVendorPartitionVersion() + ")."
+ + " This will become an install error in a future release");
+ }
+ } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of system"
+ + " overlays (" + Build.VERSION.SDK_INT + ")."
+ + " This will become an install error in a future release");
+ }
+ }
+ } else {
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without , will
+ // only be used if it is signed with the same certificate as its target OR if
+ // it is signed with the same certificate as a reference package declared
+ // in 'overlay-config-signature' tag of SystemConfig.
+ // If the target is already installed or 'overlay-config-signature' tag in
+ // SystemConfig is set, check this here to augment the last line of defense
+ // which is OMS.
+ if (pkg.getOverlayTargetOverlayableName() == null) {
+ final PackageSetting targetPkgSetting =
+ mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
+ if (targetPkgSetting != null) {
+ if (!comparePackageSignatures(targetPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ // check reference signature
+ if (mPm.mOverlayConfigSignaturePackage == null) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " and target "
+ + pkg.getOverlayTarget() + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " ");
+ }
+ final PackageSetting refPkgSetting =
+ mPm.mSettings.getPackageLPr(
+ mPm.mOverlayConfigSignaturePackage);
+ if (!comparePackageSignatures(refPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " signed with a different "
+ + "certificate than both the reference package and "
+ + "target " + pkg.getOverlayTarget() + ", and the "
+ + "overlay lacks ");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If the package is not on a system partition ensure it is signed with at least the
+ // minimum signature scheme version required for its target SDK.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
+ }
+ }
+ }
+ }
+
+ private static void assertPackageProcesses(AndroidPackage pkg,
+ List components, Map procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ParsedMainComponent component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.getClassName()
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the list");
+ }
+ }
+ }
+
+ /**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
+ * relevant packages and settings.
+ * @param sharedUserSetting The {@code SharedUserSetting} to adjust
+ * @param scannedPackage the package being scanned or null
+ * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
+ * @return the list of code paths that belong to packages that had their ABIs adjusted.
+ */
+ public static List applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
+ ParsedPackage scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.setPrimaryCpuAbi(adjustedAbi);
+ }
+ List changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
+ if (ps.primaryCpuAbiString != null) {
+ continue;
+ }
+
+ ps.primaryCpuAbiString = adjustedAbi;
+ if (ps.pkg != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.getPathString());
+ }
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+ /**
+ * Applies policy to the parsed package based upon the given policy flags.
+ * Ensures the package is in a good state.
+ *
+ * Implementation detail: This method must NOT have any side effect. It would
+ * ideally be static, but, it requires locks to read system state.
+ */
+ private static void applyPolicy(ParsedPackage parsedPackage,
+ final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ parsedPackage.setSystem(true);
+ // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+ // is set during parse.
+ if (parsedPackage.isDirectBootAware()) {
+ parsedPackage.setAllComponentsDirectBootAware(true);
+ }
+ if (compressedFileExists(parsedPackage.getPath())) {
+ parsedPackage.setStub(true);
+ }
+ } else {
+ parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
+ // non system apps can't be flagged as core
+ .setCoreApp(false)
+ // clear flags not applicable to regular apps
+ .setPersistent(false)
+ .setDefaultToDeviceProtectedStorage(false)
+ .setDirectBootAware(false)
+ // non system apps can't have permission priority
+ .capPermissionPriorities();
+ }
+ if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ parsedPackage
+ .markNotActivitiesAsNotExportedIfSingleUser();
+ }
+
+ parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+ .setOem((scanFlags & SCAN_AS_OEM) != 0)
+ .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+ .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+ .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+ .setOdm((scanFlags & SCAN_AS_ODM) != 0);
+
+ // Check if the package is signed with the same key as the platform package.
+ parsedPackage.setSignedWithPlatformKey(
+ (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+ || (platformPkg != null && compareSignatures(
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
+ ) == PackageManager.SIGNATURE_MATCH))
+ );
+
+ if (!parsedPackage.isSystem()) {
+ // Only system apps can use these features.
+ parsedPackage.clearOriginalPackages()
+ .clearAdoptPermissions();
+ }
+
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ private static void assertCodePolicy(AndroidPackage pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode = pkg.isHasCode();
+ if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getBaseApkPath() + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+ for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ *
This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return AndroidPackageUtils.getRealPackageOrNull(pkg);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.getOriginalPackages().contains(renamedPkgName);
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ *
When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ @NonNull String renamedPackageName) {
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ || parsedPackage.getPackageName().equals(renamedPackageName)) {
+ return;
+ }
+ parsedPackage.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ private static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
+ **/
+ private static void configurePackageComponents(AndroidPackage pkg) {
+ final ArrayMap componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.getPackageName());
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getActivities().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getReceivers().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
+ final ParsedProvider component = pkg.getProviders().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
+ final ParsedService component = pkg.getServices().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+ }
+
+ private static int getVendorPartitionVersion() {
+ final String version = SystemProperties.get("ro.vndk.version");
+ if (!version.isEmpty()) {
+ try {
+ return Integer.parseInt(version);
+ } catch (NumberFormatException ignore) {
+ if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ }
+ }
+ return Build.VERSION_CODES.P;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
new file mode 100644
index 0000000000000..e8dcfc7fc7f3b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -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 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 freezers = new ArrayList<>();
+ final ArrayList loaded = new ArrayList<>();
+ final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
+
+ final Settings.VersionInfo ver;
+ final List 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 unloaded = new ArrayList<>();
+ synchronized (mPm.mInstallLock) {
+ synchronized (mPm.mLock) {
+ final List 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 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 absoluteCodePaths = collectAbsoluteCodePaths();
+ List 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 collectAbsoluteCodePaths() {
+ synchronized (mPm.mLock) {
+ List 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;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 5de0c2bbd5ff8..197341004b9ee 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -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*/,