diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9d704e7379789..cdd347beba0a6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -545,7 +545,6 @@ public class PackageManagerService extends IPackageManager.Stub static final int SCAN_AS_SYSTEM_EXT = 1 << 21; static final int SCAN_AS_ODM = 1 << 22; static final int SCAN_AS_APK_IN_APEX = 1 << 23; - static final int SCAN_EXPECTED_BETTER = 1 << 24; @IntDef(flag = true, prefix = { "SCAN_" }, value = { SCAN_NO_DEX, @@ -805,12 +804,11 @@ public class PackageManagerService extends IPackageManager.Stub final SparseIntArray mIsolatedOwners = new SparseIntArray(); /** - * Tracks packages that we expect to find updated versions of on disk. - * Keys are package name, values are package location and package version code. - * - * @see #expectBetter(String, File, long) + * Tracks new system packages [received in an OTA] that we expect to + * find updated user-installed versions. Keys are package name, values + * are package location. */ - private final ArrayMap>> mExpectingBetter = new ArrayMap<>(); + final private ArrayMap mExpectingBetter = new ArrayMap<>(); /** * Tracks existing packages prior to receiving an OTA. Keys are package name. @@ -3351,7 +3349,7 @@ public class PackageManagerService extends IPackageManager.Stub + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); - expectBetter(ps.name, ps.getPath(), ps.versionCode); + mExpectingBetter.put(ps.name, ps.getPath()); } continue; @@ -3381,8 +3379,7 @@ public class PackageManagerService extends IPackageManager.Stub // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. - expectBetter(disabledPs.name, disabledPs.getPath(), - disabledPs.versionCode); + mExpectingBetter.put(disabledPs.name, disabledPs.getPath()); } } } @@ -3483,48 +3480,38 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < mExpectingBetter.size(); i++) { final String packageName = mExpectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { + final File scanFile = mExpectingBetter.valueAt(i); + logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); - final List> scanFiles = mExpectingBetter.valueAt(i); - // Sort ascending and iterate backwards to take highest version code - Collections.sort(scanFiles, - (first, second) -> Long.compare(first.second, second.second)); - for (int index = scanFiles.size() - 1; index >= 0; index--) { - File scanFile = scanFiles.get(index).first; - - @ParseFlags int reparseFlags = 0; - @ScanFlags int rescanFlags = 0; - for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { - final ScanPartition partition = mDirsToScanAsSystem.get(i1); - if (partition.containsPrivApp(scanFile)) { - reparseFlags = systemParseFlags; - rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED - | partition.scanFlag; - break; - } - if (partition.containsApp(scanFile)) { - reparseFlags = systemParseFlags; - rescanFlags = systemScanFlags | partition.scanFlag; - break; - } - } - if (rescanFlags == 0) { - Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); - continue; - } - mSettings.enableSystemPackageLPw(packageName); - - rescanFlags |= SCAN_EXPECTED_BETTER; - - try { - scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null); - // Take first success and break out of for loop + @ParseFlags int reparseFlags = 0; + @ScanFlags int rescanFlags = 0; + for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { + final ScanPartition partition = mDirsToScanAsSystem.get(i1); + if (partition.containsPrivApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED + | partition.scanFlag; break; - } catch (PackageManagerException e) { - Slog.e(TAG, "Failed to parse original system package: " - + e.getMessage()); } + if (partition.containsApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | partition.scanFlag; + break; + } + } + if (rescanFlags == 0) { + Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); + continue; + } + mSettings.enableSystemPackageLPw(packageName); + + try { + scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse original system package: " + + e.getMessage()); } } } @@ -3913,33 +3900,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - /** - * Mark a package as skipped during initial scan, expecting a more up to date version to be - * available on the scan of a higher priority partition. This can be either a system partition - * or the data partition. - * - * If for some reason that newer version cannot be scanned successfully, the data structure - * created here will be used to backtrack in the scanning process to try and take the highest - * version code of the package left on disk that scans successfully. - * - * This can occur if an OTA adds a new system package which the user has already installed an - * update on data for. Or if the device image includes multiple versions of the same package, - * for cases where the maintainer of a higher priority partition wants to update an app on - * a lower priority partition before shipping a device to users. - * - * @param pkgName the package name identifier to queue under - * @param codePath the path to re-scan if needed - * @param knownVersionCode the version of the package so that the set of files can be sorted - */ - private void expectBetter(String pkgName, File codePath, long knownVersionCode) { - List> pairs = mExpectingBetter.get(pkgName); - if (pairs == null) { - pairs = new ArrayList<>(0); - mExpectingBetter.put(pkgName, pairs); - } - pairs.add(Pair.create(codePath, knownVersionCode)); - } - /** * Extract, install and enable a stub package. *

If the compressed file can not be extracted / installed for any reason, the stub @@ -11311,23 +11271,7 @@ public class PackageManagerService extends IPackageManager.Stub isUpdatedSystemApp = disabledPkgSetting != null; } applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp); - try { - assertPackageIsValid(parsedPackage, pkgSetting, parseFlags, scanFlags); - } catch (PackageManagerException e) { - if (e.error == INSTALL_FAILED_VERSION_DOWNGRADE - && ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) - && ((scanFlags & SCAN_BOOTING) != 0)) { - if (pkgSetting != null && pkgSetting.getPkg() == null) { - // If a package for the pkgSetting hasn't already been found, this is - // skipping a downgrade on a lower priority partition, and so a later scan - // is expected to fill the package. - expectBetter(pkgSetting.name, new File(parsedPackage.getPath()), - parsedPackage.getLongVersionCode()); - } - } - - throw e; - } + assertPackageIsValid(parsedPackage, parseFlags, scanFlags); SharedUserSetting sharedUserSetting = null; if (parsedPackage.getSharedUserId() != null) { @@ -12179,9 +12123,9 @@ public class PackageManagerService extends IPackageManager.Stub * * @throws PackageManagerException If the package fails any of the validation checks */ - private void assertPackageIsValid(AndroidPackage pkg, - @Nullable PackageSetting existingPkgSetting, final @ParseFlags int parseFlags, - final @ScanFlags int scanFlags) throws PackageManagerException { + private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags, + final @ScanFlags int scanFlags) + throws PackageManagerException { if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) { assertCodePolicy(pkg); } @@ -12196,11 +12140,11 @@ public class PackageManagerService extends IPackageManager.Stub // after OTA. final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0; final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; - String pkgName = pkg.getPackageName(); if ((isUserInstall || isFirstBootOrUpgrade) - && mApexManager.isApexPackage(pkgName)) { + && mApexManager.isApexPackage(pkg.getPackageName())) { throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, - pkgName + " is an APEX package and can't be installed as an APK."); + pkg.getPackageName() + + " is an APEX package and can't be installed as an APK."); } // Make sure we're not adding any bogus keyset info @@ -12209,7 +12153,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { // The special "android" package can only be defined once - if (pkgName.equals("android")) { + if (pkg.getPackageName().equals("android")) { if (mAndroidApplication != null) { Slog.w(TAG, "*************************************************"); Slog.w(TAG, "Core android package being redefined. Skipping."); @@ -12220,46 +12164,12 @@ public class PackageManagerService extends IPackageManager.Stub } } - final long newLongVersionCode = pkg.getLongVersionCode(); - if ((scanFlags & SCAN_NEW_INSTALL) == 0) { - boolean runDuplicateCheck = false; - - // It's possible to re-scan a package if an updated system app was expected, but - // no update on /data could be found. To avoid infinitely looping, a flag is passed - // in when re-scanning and this first branch is skipped if the flag is set. - if ((scanFlags & SCAN_EXPECTED_BETTER) == 0 && existingPkgSetting != null) { - long existingLongVersionCode = existingPkgSetting.versionCode; - if (newLongVersionCode <= existingLongVersionCode) { - // Must check that real name is equivalent, as it's possible to downgrade - // version code if the package is actually a different package taking over - // a package name through . It is assumed that this - // migration is one time, one way, and that there is no failsafe if this - // doesn't hold true. - if (Objects.equals(existingPkgSetting.realName, pkg.getRealPackage())) { - if (newLongVersionCode != existingLongVersionCode) { - throw new PackageManagerException( - INSTALL_FAILED_VERSION_DOWNGRADE, - "Ignoring lower version " + newLongVersionCode - + " for package " + pkgName - + " with expected version " - + existingLongVersionCode); - } - } - } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 - && (scanFlags & SCAN_BOOTING) != 0) { - // During system boot scan, if there's already a package known, but this - // package is higher version, use it instead, ignoring the duplicate check. - // This will store the higher version in the setting object, and the above - // branch/exception will cause future scans to skip the lower versions. - runDuplicateCheck = false; - } - } - - if (runDuplicateCheck && mPackages.containsKey(pkgName)) { - throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, - "Application package " + pkgName - + " already installed. Skipping duplicate."); - } + // 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()) { @@ -12379,8 +12289,8 @@ public class PackageManagerService extends IPackageManager.Stub } } } - if (newLongVersionCode < minVersionCode - || newLongVersionCode > maxVersionCode) { + if (pkg.getLongVersionCode() < minVersionCode + || pkg.getLongVersionCode() > maxVersionCode) { throw new PackageManagerException("Static shared" + " lib version codes must be ordered as lib versions"); } @@ -12395,10 +12305,11 @@ public class PackageManagerService extends IPackageManager.Stub // 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(pkgName)) { - Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkgName); + if (mExpectingBetter.containsKey(pkg.getPackageName())) { + Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + + pkg.getPackageName()); } else { - PackageSetting known = mSettings.getPackageLPr(pkgName); + PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName()); if (known != null) { if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Examining " + pkg.getPath() @@ -12406,14 +12317,14 @@ public class PackageManagerService extends IPackageManager.Stub } if (!pkg.getPath().equals(known.getPathString())) { throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, - "Application package " + pkgName + "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 " + pkgName + "Application package " + pkg.getPackageName() + " not found; ignoring."); } } @@ -12436,7 +12347,7 @@ public class PackageManagerService extends IPackageManager.Stub INSTALL_FAILED_PROCESS_NOT_DEFINED, "Can't install because application tag's process attribute " + pkg.getProcessName() - + " (in package " + pkgName + + " (in package " + pkg.getPackageName() + ") is not included in the list"); } assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity"); @@ -12460,7 +12371,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg.getSigningDetails().signatures)) { throw new PackageManagerException("Apps that share a user with a " + "privileged app must themselves be marked as privileged. " + - pkgName + " shares privileged user " + + pkg.getPackageName() + " shares privileged user " + pkg.getSharedUserId() + "."); } } @@ -12477,21 +12388,21 @@ public class PackageManagerService extends IPackageManager.Stub // upgraded. Objects.requireNonNull(mOverlayConfig, "Parsing non-system dir before overlay configs are initialized"); - if (!mOverlayConfig.isMutable(pkgName)) { + if (!mOverlayConfig.isMutable(pkg.getPackageName())) { throw new PackageManagerException("Overlay " - + pkgName + + pkg.getPackageName() + " is static and cannot be upgraded."); } } else { if ((scanFlags & SCAN_AS_VENDOR) != 0) { if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) { - Slog.w(TAG, "System overlay " + pkgName + 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 " + pkgName + 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"); @@ -12507,7 +12418,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!comparePackageSignatures(platformPkgSetting, pkg.getSigningDetails().signatures)) { throw new PackageManagerException("Overlay " - + pkgName + + pkg.getPackageName() + " must target Q or later, " + "or be signed with the platform certificate"); } @@ -12529,7 +12440,7 @@ public class PackageManagerService extends IPackageManager.Stub // check reference signature if (mOverlayConfigSignaturePackage == null) { throw new PackageManagerException("Overlay " - + pkgName + " and target " + + pkg.getPackageName() + " and target " + pkg.getOverlayTarget() + " signed with" + " different certificates, and the overlay lacks" + " "); @@ -12539,7 +12450,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!comparePackageSignatures(refPkgSetting, pkg.getSigningDetails().signatures)) { throw new PackageManagerException("Overlay " - + pkgName + " signed with a different " + + pkg.getPackageName() + " signed with a different " + "certificate than both the reference package and " + "target " + pkg.getOverlayTarget() + ", and the " + "overlay lacks "); @@ -12559,7 +12470,7 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) { throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion - + " or newer for package " + pkgName); + + " or newer for package " + pkg.getPackageName()); } } } diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java index 3463daf748a69..74ec16140c945 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java +++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java @@ -57,16 +57,7 @@ public class PackageCacher { * Returns the cache key for a specified {@code packageFile} and {@code flags}. */ private String getCacheKey(File packageFile, int flags) { - StringBuilder sb = new StringBuilder(); - - // To support packages with the same file name across partitions, use the partition name - // as a prefix. The cache should only be used for cases where the file paths have been - // established using the unique partition names, without canonicalization, so any links - // which would point to the same partition name should be handled separately. - String cachePrefix = packageFile.toPath().getName(0).toString(); - sb.append(cachePrefix); - sb.append('-'); - sb.append(packageFile.getName()); + StringBuilder sb = new StringBuilder(packageFile.getName()); sb.append('-'); sb.append(flags); diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java index 46d31d9e907e9..851ddd1eae48d 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java +++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java @@ -135,7 +135,7 @@ public class PackageParser2 implements AutoCloseable { } /** - * TODO(b/155493909): Document new package parsing + * TODO(b/135203078): Document new package parsing */ @AnyThread public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt index 7ae2fe0935de1..e17358d38d8cd 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt @@ -17,8 +17,11 @@ import org.junit.runner.RunWith class FactoryPackageTest : BaseHostJUnit4Test() { companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" private const val DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk" - private const val DEVICE_SIDE_PKG_NAME = "com.android.server.pm.test.deviceside" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -37,8 +40,7 @@ class FactoryPackageTest : BaseHostJUnit4Test() { @Before @After fun removeApk() { - HostUtils.deleteAllTestPackages(device, preparer) - device.uninstallPackage(DEVICE_SIDE_PKG_NAME) + device.uninstallPackage(TEST_PKG_NAME) device.deleteFile(filePath.parent.toString()) device.reboot() } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt index f0b60f48e0903..9399030e057c0 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt @@ -23,15 +23,6 @@ import org.junit.rules.TemporaryFolder import java.io.File import java.io.FileOutputStream -internal const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" -internal const val VERSION_STUB = "PackageManagerTestAppStub.apk" -internal const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" -internal const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" -internal const val VERSION_THREE = "PackageManagerTestAppVersion3.apk" -internal const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk" -internal const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk" -internal const val VERSION_OVERRIDE = "PackageManagerTestAppOriginalOverride.apk" - internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) = pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition) .toString()) @@ -98,25 +89,12 @@ internal fun retryUntilSuccess(block: () -> Boolean) { internal object HostUtils { - /** - * Since most of the tests use the same test APKs, consolidate the logic for deleting them - * before and after a test runs. This also ensures that a failing test doesn't leave an APK on - * device that could spill over to another test when developing locally. - * - * Iterates all partitions since different tests use different partitions. - */ - fun deleteAllTestPackages(device: ITestDevice, preparer: SystemPreparer) { - Partition.values().forEach { partition -> - device.uninstallPackage(TEST_PKG_NAME) - preparer.deleteApkFolders(partition, VERSION_ONE, VERSION_TWO, VERSION_THREE, - VERSION_THREE_INVALID, VERSION_FOUR, VERSION_OVERRIDE) - } - - // TODO: There is an optimization that can be made here by hooking into the SystemPreparer's - // reboot rule, avoiding a reboot cycle by doing the delete in line the built in @After - // reboot. - preparer.reboot() - } + fun getDataDir(device: ITestDevice, pkgName: String) = + device.executeShellCommand("dumpsys package $pkgName") + .lineSequence() + .map(String::trim) + .single { it.startsWith("dataDir=") } + .removePrefix("dataDir=") fun makePathForApk(fileName: String, partition: Partition) = makePathForApk(File(fileName), partition) @@ -158,35 +136,14 @@ internal object HostUtils { } .map(String::trim) - fun getDataDir(device: ITestDevice, pkgName: String) = - packageSection(device, pkgName) - .singleOrNull { it.startsWith("dataDir=") } - ?.removePrefix("dataDir=") - - /** Return all code paths for a package. This will include hidden system package code paths. */ fun getCodePaths(device: ITestDevice, pkgName: String) = - (packageSection(device, pkgName) + - packageSection(device, pkgName, "Hidden system packages")) + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .map(String::trim) .filter { it.startsWith("codePath=") } .map { it.removePrefix("codePath=") } .toList() - fun getVersionCode(device: ITestDevice, pkgName: String) = - packageSection(device, pkgName) - .filter { it.startsWith("versionCode=") } - .map { it.removePrefix("versionCode=") } - .map { it.takeWhile { !it.isWhitespace() } } - .map { it.toInt() } - .firstOrNull() - - fun getPrivateFlags(device: ITestDevice, pkgName: String) = - packageSection(device, pkgName) - .filter { it.startsWith("privateFlags=") } - .map { it.removePrefix("privateFlags=[ ") } - .map { it.removeSuffix(" ]") } - .map { it.split(" ") } - .firstOrNull() - private fun userIdLineSequence(device: ITestDevice, pkgName: String) = packageSection(device, pkgName) .filter { it.startsWith("User ") } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt index 85947063dc3a0..37c999cbee685 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -33,6 +33,12 @@ import org.junit.runner.RunWith class InvalidNewSystemAppTest : BaseHostJUnit4Test() { companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk" + private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk" + @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) } @@ -49,7 +55,7 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() { @Before @After fun removeApk() { - HostUtils.deleteAllTestPackages(device, preparer) + device.uninstallPackage(TEST_PKG_NAME) preparer.deleteFile(filePath.parent.toString()) .reboot() } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt index 0c5816bd4317a..4becae66633f5 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt @@ -34,6 +34,10 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { companion object { private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk" + private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -50,7 +54,9 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { @Before @After fun deleteApkFolders() { - HostUtils.deleteAllTestPackages(device, preparer) + preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE, + NEW_PKG) + .reboot() } @Test @@ -83,10 +89,10 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { device.pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt") preparer.deleteApkFolders(Partition.SYSTEM, apk) - .pushApk(VERSION_OVERRIDE, Partition.SYSTEM) + .pushApk(NEW_PKG, Partition.SYSTEM) .reboot() - assertCodePath(VERSION_OVERRIDE) + assertCodePath(NEW_PKG) // And then reading the data contents back assertThat(device.pullFileContents( diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt deleted file mode 100644 index 8d789e03f612e..0000000000000 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2020 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.test - -import com.android.internal.util.test.SystemPreparer -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.ClassRule -import org.junit.Rule -import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.rules.TemporaryFolder -import org.junit.runner.RunWith -import java.io.File - -/** - * Pushes APKs onto various system partitions to verify that multiple versions result in the - * highest version being scanned. Also tries to upgrade/replace these APKs which should result - * in a version upgrade on reboot. - * - * This will also verify that APKs under the same folder/file name across different partitions - * are parsed as separate entities and don't get combined under the same cache entry. - * - * Known limitations: - * - Does not verify that v1 isn't scanned. It's possible to introduce a bug that upgrades the - * system on every reboot from v1 -> v2, as this isn't easily visible after scan has finished. - * This would also have to successfully preserve the app data though, which seems unlikely. - * - This takes a very long time to run. 105 seconds for the first test case, up to 60 seconds for - * each following case. It's theoretically possible to parallelize these tests so that each - * method is run by installing all the apps under different names, requiring only 3 reboots to - * fully verify, rather than 3 * numTestCases. - */ -@RunWith(DeviceJUnit4ClassRunner::class) -class SystemAppScanPriorityTest : BaseHostJUnit4Test() { - - companion object { - @get:ClassRule - var deviceRebootRule = SystemPreparer.TestRuleDelegate(true) - } - - private val tempFolder = TemporaryFolder() - private val preparer: SystemPreparer = SystemPreparer(tempFolder, - SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } - - private var firstReboot = true - - @Rule - @JvmField - val rules = RuleChain.outerRule(tempFolder).around(preparer)!! - - @Before - @After - fun deleteFiles() { - HostUtils.deleteAllTestPackages(device, preparer) - } - - @Before - fun resetFirstReboot() { - firstReboot = true - } - - @Test - fun takeHigherPriority() { - preparer.pushFile(VERSION_ONE, Partition.VENDOR) - .pushFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.PRODUCT) - } - - @Test - fun takeLowerPriority() { - preparer.pushFile(VERSION_TWO, Partition.VENDOR) - .pushFile(VERSION_ONE, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.VENDOR) - } - - @Test - fun upgradeToHigherOnLowerPriority() { - preparer.pushFile(VERSION_ONE, Partition.VENDOR) - .pushFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.PRODUCT) - - preparer.pushFile(VERSION_THREE, Partition.VENDOR) - .rebootForTest() - - assertVersionAndPartition(3, Partition.VENDOR) - } - - @Test - fun upgradeToNewerOnHigherPriority() { - preparer.pushFile(VERSION_ONE, Partition.VENDOR) - .pushFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.PRODUCT) - - preparer.pushFile(VERSION_THREE, Partition.SYSTEM_EXT) - .rebootForTest() - - assertVersionAndPartition(3, Partition.SYSTEM_EXT) - } - - @Test - fun replaceNewerOnLowerPriority() { - preparer.pushFile(VERSION_TWO, Partition.VENDOR) - .pushFile(VERSION_ONE, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.VENDOR) - - preparer.pushFile(VERSION_THREE, Partition.VENDOR) - .rebootForTest() - - assertVersionAndPartition(3, Partition.VENDOR) - } - - @Test - fun replaceNewerOnHigherPriority() { - preparer.pushFile(VERSION_ONE, Partition.VENDOR) - .pushFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.PRODUCT) - - preparer.pushFile(VERSION_THREE, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(3, Partition.PRODUCT) - } - - @Test - fun fallbackToLowerPriority() { - preparer.pushFile(VERSION_TWO, Partition.VENDOR) - .pushFile(VERSION_ONE, Partition.PRODUCT) - .pushFile(VERSION_THREE, Partition.SYSTEM_EXT) - .rebootForTest() - - assertVersionAndPartition(3, Partition.SYSTEM_EXT) - - preparer.deleteFile(VERSION_THREE, Partition.SYSTEM_EXT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.VENDOR) - } - - @Test - fun fallbackToHigherPriority() { - preparer.pushFile(VERSION_THREE, Partition.VENDOR) - .pushFile(VERSION_ONE, Partition.PRODUCT) - .pushFile(VERSION_TWO, Partition.SYSTEM_EXT) - .rebootForTest() - - assertVersionAndPartition(3, Partition.VENDOR) - - preparer.deleteFile(VERSION_THREE, Partition.VENDOR) - .rebootForTest() - - assertVersionAndPartition(2, Partition.SYSTEM_EXT) - } - - @Test - fun removeBoth() { - preparer.pushFile(VERSION_ONE, Partition.VENDOR) - .pushFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertVersionAndPartition(2, Partition.PRODUCT) - - preparer.deleteFile(VERSION_ONE, Partition.VENDOR) - .deleteFile(VERSION_TWO, Partition.PRODUCT) - .rebootForTest() - - assertThat(device.getAppPackageInfo(TEST_PKG_NAME)).isNull() - } - - private fun assertVersionAndPartition(versionCode: Int, partition: Partition) { - assertThat(HostUtils.getVersionCode(device, TEST_PKG_NAME)).isEqualTo(versionCode) - - val privateFlags = HostUtils.getPrivateFlags(device, TEST_PKG_NAME) - - when (partition) { - Partition.SYSTEM, - Partition.SYSTEM_PRIVILEGED -> { - assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString()) - assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString()) - assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString()) - } - Partition.VENDOR -> { - assertThat(privateFlags).contains(Partition.VENDOR.toString()) - assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString()) - assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString()) - } - Partition.PRODUCT -> { - assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString()) - assertThat(privateFlags).contains(Partition.PRODUCT.toString()) - assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString()) - } - Partition.SYSTEM_EXT -> { - assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString()) - assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString()) - assertThat(privateFlags).contains(Partition.SYSTEM_EXT.toString()) - } - }.run { /* exhaust */ } - } - - // Following methods don't use HostUtils in order to test cache behavior when using the same - // name across partitions. Writes all files under the version 1 name. - private fun makeDevicePath(partition: Partition) = - partition.baseAppFolder - .resolve(File(VERSION_ONE).nameWithoutExtension) - .resolve(VERSION_ONE) - .toString() - - private fun SystemPreparer.pushFile(file: String, partition: Partition) = - pushResourceFile(file, makeDevicePath(partition)) - - private fun SystemPreparer.deleteFile(file: String, partition: Partition) = - deleteFile(makeDevicePath(partition)) - - /** - * Custom reboot used to write app data after the first reboot. This can then be verified - * after each subsequent reboot to ensure no data is lost. - */ - private fun SystemPreparer.rebootForTest() { - if (firstReboot) { - firstReboot = false - preparer.reboot() - - val file = tempFolder.newFile() - file.writeText("Test") - pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt") - } else { - val versionBefore = HostUtils.getVersionCode(device, TEST_PKG_NAME) - preparer.reboot() - val versionAfter = HostUtils.getVersionCode(device, TEST_PKG_NAME) - - if (versionBefore != null && versionAfter != null) { - val fileContents = device.pullFileContents( - "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt") - if (versionAfter < versionBefore) { - // A downgrade will wipe app data - assertThat(fileContents).isNull() - } else { - // An upgrade or update will preserve app data - assertThat(fileContents).isEqualTo("Test") - } - } - } - } -} diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt index 99dff086170df..46120af065508 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt @@ -37,6 +37,9 @@ import java.util.zip.GZIPOutputStream class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_STUB = "PackageManagerTestAppStub.apk" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" /** * How many total users on device to test, including primary. This will clean up any @@ -87,12 +90,6 @@ class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { savedDevice?.removeUser(it) } - savedDevice?.let { device -> - savedPreparer?.let { preparer -> - HostUtils.deleteAllTestPackages(device, preparer) - } - } - savedDevice?.uninstallPackage(TEST_PKG_NAME) savedDevice?.deleteFile(stubFile.parent.toString()) savedDevice?.deleteFile(deviceCompressedFile.parent.toString())