From 6add2584c5188af510818d8ef0a28806db51f438 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 28 Dec 2022 16:50:27 -0800 Subject: Add PackageState#getApexModuleName Tracks the name of the APEX module for both APEX and APK-in-APEX PackageStates. This involves inferring the module name from the ActiveApexInfo associated with each APEX scan partition. This is set on each APK found in that parition and also set on disabled settings for a package where the APK-in-APEX has been updated to a /data variant. The test also validates that clearing the APEX from the system image will also un-associate the apexModuleName, since it downgrades the package to a regular non-system, non-APK-in-APEX /data package. Also adds a package dump --include-apex option to print APEX PackageStates like any other regular APK. Bug: 261913353 Test: atest ApexUpdateTest Change-Id: Ic2806a4df8eac4843d9d3358a349b3d335a7455b --- .../java/com/android/server/pm/ApexManager.java | 2 +- .../java/com/android/server/pm/DumpHelper.java | 2 + .../core/java/com/android/server/pm/DumpState.java | 1 + .../java/com/android/server/pm/InitAppsHelper.java | 26 ++--- .../android/server/pm/InstallPackageHelper.java | 70 +++++++++--- .../java/com/android/server/pm/InstallRequest.java | 16 +++ .../com/android/server/pm/InstallingSession.java | 1 + .../android/server/pm/PackageManagerService.java | 2 +- .../java/com/android/server/pm/PackageSetting.java | 11 ++ .../java/com/android/server/pm/ScanPartition.java | 26 ++++- .../core/java/com/android/server/pm/Settings.java | 7 +- .../com/android/server/pm/StorageEventHelper.java | 2 +- .../com/android/server/pm/pkg/PackageState.java | 7 ++ .../android/server/pm/pkg/PackageStateImpl.java | 11 +- .../server/pm/pkg/PackageStateUnserialized.java | 33 +++--- .../PackageManagerServiceTests/host/Android.bp | 5 + .../com/android/server/pm/test/ApexUpdateTest.kt | 117 +++++++++++++++++++++ .../host/test-apps/Apex/Android.bp | 40 +++++++ .../host/test-apps/Apex/AndroidManifestApex.xml | 20 ++++ .../host/test-apps/Apex/AndroidManifestApp.xml | 20 ++++ .../host/test-apps/Apex/apex_manifest.json | 4 + .../src/com/android/server/pm/ApexManagerTest.java | 1 + 22 files changed, 369 insertions(+), 55 deletions(-) create mode 100644 services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/ApexUpdateTest.kt create mode 100644 services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp create mode 100644 services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApex.xml create mode 100644 services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApp.xml create mode 100644 services/tests/PackageManagerServiceTests/host/test-apps/Apex/apex_manifest.json diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 71593e10d45b..5e62b56c7bcd 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -138,7 +138,7 @@ public abstract class ApexManager { this.activeApexChanged = activeApexChanged; } - private ActiveApexInfo(ApexInfo apexInfo) { + public ActiveApexInfo(ApexInfo apexInfo) { this( apexInfo.moduleName, new File(Environment.getApexDirectory() + File.separator diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java index 3385a09e49db..fcaaa90dbc8a 100644 --- a/services/core/java/com/android/server/pm/DumpHelper.java +++ b/services/core/java/com/android/server/pm/DumpHelper.java @@ -111,6 +111,8 @@ final class DumpHelper { dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS); } else if ("-f".equals(opt)) { dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); + } else if ("--include-apex".equals(opt)) { + dumpState.setOptionEnabled(DumpState.OPTION_INCLUDE_APEX); } else if ("--proto".equals(opt)) { dumpProto(snapshot, fd); return; diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 6225753cc38f..0bdce21f885f 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -51,6 +51,7 @@ public final class DumpState { public static final int OPTION_SHOW_FILTERS = 1 << 0; public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1; public static final int OPTION_SKIP_PERMISSIONS = 1 << 2; + public static final int OPTION_INCLUDE_APEX = 1 << 3; private int mTypes; diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 6825dd7832ce..5c4447eb99a4 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -21,11 +21,9 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; -import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED; import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; -import static com.android.server.pm.PackageManagerService.SCAN_DROP_CACHE; import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE; import static com.android.server.pm.PackageManagerService.SCAN_INITIAL; import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; @@ -147,14 +145,7 @@ final class InitAppsHelper { sp.getFolder().getAbsolutePath()) || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( sp.getFolder().getAbsolutePath() + File.separator)) { - int additionalScanFlag = SCAN_AS_APK_IN_APEX; - if (apexInfo.isFactory) { - additionalScanFlag |= SCAN_AS_FACTORY; - } - if (apexInfo.activeApexChanged) { - additionalScanFlag |= SCAN_DROP_CACHE; - } - return new ScanPartition(apexInfo.apexDirectory, sp, additionalScanFlag); + return new ScanPartition(apexInfo.apexDirectory, sp, apexInfo); } } return null; @@ -266,7 +257,7 @@ final class InitAppsHelper { } scanDirTracedLI(mPm.getAppInstallDir(), 0, - mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService); + mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService, null); List unfinishedTasks = mExecutorService.shutdownNow(); if (!unfinishedTasks.isEmpty()) { @@ -335,12 +326,12 @@ final class InitAppsHelper { } scanDirTracedLI(partition.getOverlayFolder(), mSystemParseFlags, mSystemScanFlags | partition.scanFlag, - packageParser, executorService); + packageParser, executorService, partition.apexInfo); } scanDirTracedLI(frameworkDir, mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, - packageParser, executorService); + packageParser, executorService, null); if (!mPm.mPackages.containsKey("android")) { throw new IllegalStateException( "Failed to load frameworks package; check log for warnings"); @@ -352,11 +343,11 @@ final class InitAppsHelper { scanDirTracedLI(partition.getPrivAppFolder(), mSystemParseFlags, mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, - packageParser, executorService); + packageParser, executorService, partition.apexInfo); } scanDirTracedLI(partition.getAppFolder(), mSystemParseFlags, mSystemScanFlags | partition.scanFlag, - packageParser, executorService); + packageParser, executorService, partition.apexInfo); } } @@ -373,7 +364,8 @@ final class InitAppsHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags, - PackageParser2 packageParser, ExecutorService executorService) { + PackageParser2 packageParser, ExecutorService executorService, + @Nullable ApexManager.ActiveApexInfo apexInfo) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { @@ -381,7 +373,7 @@ final class InitAppsHelper { parseFlags |= PARSE_APK_IN_APEX; } mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags, - scanFlags, packageParser, executorService); + scanFlags, packageParser, executorService, apexInfo); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index ac4da2ed7d73..756c2e26d1c6 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -367,10 +367,11 @@ final class InstallPackageHelper { if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { boolean isFactory = (scanFlags & SCAN_AS_FACTORY) != 0; - pkgSetting.getPkgState().setApkInApex(true); pkgSetting.getPkgState().setApkInUpdatedApex(!isFactory); } + pkgSetting.getPkgState().setApexModuleName(request.getApexModuleName()); + // TODO(toddke): Consider a method specifically for modifying the Package object // post scan; or, moving this stuff out of the Package object since it has nothing // to do with the package on disk. @@ -1716,6 +1717,7 @@ final class InstallPackageHelper { + ", old=" + oldPackage); } request.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + request.setApexModuleName(oldPackageState.getApexModuleName()); targetParseFlags = systemParseFlags; targetScanFlags = systemScanFlags; } else { // non system replace @@ -3172,7 +3174,7 @@ final class InstallPackageHelper { final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); removePackageHelper.removePackage(stubPkg, true /*chatty*/); try { - return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags); + return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), e); @@ -3304,7 +3306,7 @@ final class InstallPackageHelper { | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); final AndroidPackage pkg = scanSystemPackageTracedLI( - codePath, parseFlags, scanFlags); + codePath, parseFlags, scanFlags, null); synchronized (mPm.mLock) { PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); @@ -3484,7 +3486,7 @@ final class InstallPackageHelper { try { final File codePath = new File(pkg.getPath()); synchronized (mPm.mInstallLock) { - scanSystemPackageTracedLI(codePath, 0, scanFlags); + scanSystemPackageTracedLI(codePath, 0, scanFlags, null); } } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " @@ -3563,7 +3565,8 @@ final class InstallPackageHelper { if (throwable == null) { try { - addForInitLI(parseResult.parsedPackage, newParseFlags, newScanFlags, null); + addForInitLI(parseResult.parsedPackage, newParseFlags, newScanFlags, null, + new ApexManager.ActiveApexInfo(ai)); AndroidPackage pkg = parseResult.parsedPackage.hideAsFinal(); if (ai.isFactory && !ai.isActive) { disableSystemPackageLPw(pkg); @@ -3585,8 +3588,8 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public void installPackagesFromDir(File scanDir, int parseFlags, - int scanFlags, PackageParser2 packageParser, - ExecutorService executorService) { + int scanFlags, PackageParser2 packageParser, ExecutorService executorService, + @Nullable ApexManager.ActiveApexInfo apexInfo) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); @@ -3634,7 +3637,7 @@ final class InstallPackageHelper { } try { addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, - new UserHandle(UserHandle.USER_SYSTEM)); + new UserHandle(UserHandle.USER_SYSTEM), apexInfo); } catch (PackageManagerException e) { errorCode = e.error; errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage(); @@ -3697,7 +3700,7 @@ final class InstallPackageHelper { try { synchronized (mPm.mInstallLock) { final AndroidPackage newPkg = scanSystemPackageTracedLI( - scanFile, reparseFlags, rescanFlags); + scanFile, reparseFlags, rescanFlags, null); // We rescanned a stub, add it to the list of stubbed system packages if (newPkg.isStub()) { stubSystemApps.add(packageName); @@ -3716,10 +3719,11 @@ final class InstallPackageHelper { */ @GuardedBy("mPm.mInstallLock") public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags, - int scanFlags) throws PackageManagerException { + int scanFlags, @Nullable ApexManager.ActiveApexInfo apexInfo) + throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); try { - return scanSystemPackageLI(scanFile, parseFlags, scanFlags); + return scanSystemPackageLI(scanFile, parseFlags, scanFlags, apexInfo); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3730,7 +3734,8 @@ final class InstallPackageHelper { * Returns {@code null} in case of errors and the error code is stored in mLastScanError */ @GuardedBy("mPm.mInstallLock") - private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags) + private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags, + @Nullable ApexManager.ActiveApexInfo apexInfo) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); @@ -3748,7 +3753,7 @@ final class InstallPackageHelper { } return addForInitLI(parsedPackage, parseFlags, scanFlags, - new UserHandle(UserHandle.USER_SYSTEM)); + new UserHandle(UserHandle.USER_SYSTEM), apexInfo); } /** @@ -3768,7 +3773,26 @@ final class InstallPackageHelper { private AndroidPackage addForInitLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, @PackageManagerService.ScanFlags int scanFlags, - @Nullable UserHandle user) throws PackageManagerException { + @Nullable UserHandle user, @Nullable ApexManager.ActiveApexInfo activeApexInfo) + throws PackageManagerException { + PackageSetting disabledPkgSetting; + synchronized (mPm.mLock) { + disabledPkgSetting = + mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName()); + if (activeApexInfo != null && disabledPkgSetting != null) { + // When a disabled system package is scanned, its final PackageSetting is actually + // skipped and not added to any data structures, instead relying on the disabled + // setting read from the persisted Settings XML file. This persistence does not + // include the APEX module name, so here, re-set it from the active APEX info. + // + // This also has the (beneficial) side effect where if a package disappears from an + // APEX, leaving only a /data copy, it will lose its apexModuleName. + // + // This must be done before scanSystemPackageLI as that will throw in the case of a + // system -> data package. + disabledPkgSetting.setApexModuleName(activeApexInfo.apexModuleName); + } + } final Pair scanResultPair = scanSystemPackageLI( parsedPackage, parseFlags, scanFlags, user); @@ -3777,6 +3801,24 @@ final class InstallPackageHelper { final InstallRequest installRequest = new InstallRequest( parsedPackage, parseFlags, scanFlags, user, scanResult); + String existingApexModuleName = null; + synchronized (mPm.mLock) { + var existingPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName()); + if (existingPkgSetting != null) { + existingApexModuleName = existingPkgSetting.getApexModuleName(); + } + } + + if (activeApexInfo != null) { + installRequest.setApexModuleName(activeApexInfo.apexModuleName); + } else { + if (disabledPkgSetting != null) { + installRequest.setApexModuleName(disabledPkgSetting.getApexModuleName()); + } else if (existingApexModuleName != null) { + installRequest.setApexModuleName(existingApexModuleName); + } + } + synchronized (mPm.mLock) { boolean appIdCreated = false; try { diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index c6cdc4cd350d..a66d0ba4e438 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -44,6 +44,7 @@ import android.util.Slog; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.parsing.ParsingPackageUtils; @@ -107,6 +108,12 @@ final class InstallRequest { @Nullable private ApexInfo mApexInfo; + /** + * For tracking {@link PackageState#getApexModuleName()}. + */ + @Nullable + private String mApexModuleName; + @Nullable private ScanResult mScanResult; @@ -347,6 +354,11 @@ final class InstallRequest { return mApexInfo; } + @Nullable + public String getApexModuleName() { + return mApexModuleName; + } + @Nullable public String getSourceInstallerPackageName() { return mInstallArgs.mInstallSource.mInstallerPackageName; @@ -644,6 +656,10 @@ final class InstallRequest { mApexInfo = apexInfo; } + public void setApexModuleName(@Nullable String apexModuleName) { + mApexModuleName = apexModuleName; + } + public void setPkg(AndroidPackage pkg) { mPkg = pkg; } diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java index eb3b29ce4b71..9f9ff92f1140 100644 --- a/services/core/java/com/android/server/pm/InstallingSession.java +++ b/services/core/java/com/android/server/pm/InstallingSession.java @@ -609,6 +609,7 @@ class InstallingSession { // processApkInstallRequests() fails. Need a way to keep info stored in apexd // and PMS in sync in the face of install failures. request.setApexInfo(apexInfo); + request.setApexModuleName(apexInfo.moduleName); mPm.mHandler.post(() -> processApkInstallRequests(true, requests)); return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 92bbb7e86327..9cc03340b08d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -716,7 +716,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService * The list of all system partitions that may contain packages in ascending order of * specificity (the more generic, the earlier in the list a partition appears). */ - @VisibleForTesting(visibility = Visibility.PRIVATE) + @VisibleForTesting(visibility = Visibility.PACKAGE) public static final List SYSTEM_PARTITIONS = Collections.unmodifiableList( PackagePartitions.getOrderedPartitions(ScanPartition::new)); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 6562de96388f..53fdfaad38ac 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -1262,6 +1262,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return pkgState.isApkInUpdatedApex(); } + @Nullable + @Override + public String getApexModuleName() { + return pkgState.getApexModuleName(); + } + public PackageSetting setDomainSetId(@NonNull UUID domainSetId) { mDomainSetId = domainSetId; onChanged(); @@ -1317,6 +1323,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return this; } + public PackageSetting setApexModuleName(@Nullable String apexModuleName) { + pkgState.setApexModuleName(apexModuleName); + return this; + } + @NonNull @Override public PackageStateUnserialized getTransientState() { diff --git a/services/core/java/com/android/server/pm/ScanPartition.java b/services/core/java/com/android/server/pm/ScanPartition.java index e1d2b3bcfefe..9ee6035f6f1a 100644 --- a/services/core/java/com/android/server/pm/ScanPartition.java +++ b/services/core/java/com/android/server/pm/ScanPartition.java @@ -16,13 +16,17 @@ package com.android.server.pm; +import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; +import static com.android.server.pm.PackageManagerService.SCAN_AS_FACTORY; 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_PRODUCT; 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_DROP_CACHE; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackagePartitions; import com.android.internal.annotations.VisibleForTesting; @@ -32,14 +36,18 @@ import java.io.File; /** * List of partitions to be scanned during system boot */ -@VisibleForTesting +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public class ScanPartition extends PackagePartitions.SystemPartition { @PackageManagerService.ScanFlags public final int scanFlag; + @Nullable + public final ApexManager.ActiveApexInfo apexInfo; + public ScanPartition(@NonNull PackagePartitions.SystemPartition partition) { super(partition); scanFlag = scanFlagForPartition(partition); + apexInfo = null; } /** @@ -48,9 +56,21 @@ public class ScanPartition extends PackagePartitions.SystemPartition { * partition along with any specified additional scan flags. */ public ScanPartition(@NonNull File folder, @NonNull ScanPartition original, - @PackageManagerService.ScanFlags int additionalScanFlag) { + @Nullable ApexManager.ActiveApexInfo apexInfo) { super(folder, original); - this.scanFlag = original.scanFlag | additionalScanFlag; + var scanFlags = original.scanFlag; + this.apexInfo = apexInfo; + if (apexInfo != null) { + scanFlags |= SCAN_AS_APK_IN_APEX; + if (apexInfo.isFactory) { + scanFlags |= SCAN_AS_FACTORY; + } + if (apexInfo.activeApexChanged) { + scanFlags |= SCAN_DROP_CACHE; + } + } + //noinspection WrongConstant + this.scanFlag = scanFlags; } private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 97fb0c2e3fe9..aedf78255422 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -5052,6 +5052,7 @@ public final class Settings implements Watchable, Snappable { pw.print(prefix); pw.print(" privatePkgFlags="); printFlags(pw, ps.getPrivateFlags(), PRIVATE_FLAG_DUMP_SPEC); pw.println(); + pw.print(prefix); pw.print(" apexModuleName="); pw.println(ps.getApexModuleName()); if (pkg != null && pkg.getOverlayTarget() != null) { pw.print(prefix); pw.print(" overlayTarget="); pw.println(pkg.getOverlayTarget()); @@ -5263,7 +5264,8 @@ public final class Settings implements Watchable, Snappable { && !packageName.equals(ps.getPackageName())) { continue; } - if (ps.getPkg() != null && ps.getPkg().isApex()) { + if (ps.getPkg() != null && ps.getPkg().isApex() + && !dumpState.isOptionEnabled(DumpState.OPTION_INCLUDE_APEX)) { // Filter APEX packages which will be dumped in the APEX section continue; } @@ -5319,7 +5321,8 @@ public final class Settings implements Watchable, Snappable { && !packageName.equals(ps.getPackageName())) { continue; } - if (ps.getPkg() != null && ps.getPkg().isApex()) { + if (ps.getPkg() != null && ps.getPkg().isApex() + && !dumpState.isOptionEnabled(DumpState.OPTION_INCLUDE_APEX)) { // Filter APEX packages which will be dumped in the APEX section continue; } diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 4f7c2bdf3057..23156d177abf 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -158,7 +158,7 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg; try { pkg = installPackageHelper.scanSystemPackageTracedLI( - ps.getPath(), parseFlags, SCAN_INITIAL); + ps.getPath(), parseFlags, SCAN_INITIAL, null); loaded.add(pkg); } catch (PackageManagerException e) { diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java index 5fdead0a8883..a12c9d0498a1 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageState.java +++ b/services/core/java/com/android/server/pm/pkg/PackageState.java @@ -417,4 +417,11 @@ public interface PackageState { * @hide */ boolean isVendor(); + + /** + * The name of the APEX module containing this package, if it is an APEX or APK-in-APEX. + * @hide + */ + @Nullable + String getApexModuleName(); } diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java index 8dee8ee2fa57..bc6dab41fc7e 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java @@ -154,6 +154,8 @@ public class PackageStateImpl implements PackageState { private final SigningInfo mSigningInfo; @NonNull private final SparseArray mUserStates; + @Nullable + private final String mApexModuleName; private PackageStateImpl(@NonNull PackageState pkgState, @Nullable AndroidPackage pkg) { mAndroidPackage = pkg; @@ -206,6 +208,8 @@ public class PackageStateImpl implements PackageState { mUserStates.put(userStates.keyAt(index), UserStateImpl.copy(userStates.valueAt(index))); } + + mApexModuleName = pkgState.getApexModuleName(); } @NonNull @@ -713,6 +717,11 @@ public class PackageStateImpl implements PackageState { return mUserStates; } + @DataClass.Generated.Member + public @Nullable String getApexModuleName() { + return mApexModuleName; + } + @DataClass.Generated.Member public @NonNull PackageStateImpl setBooleans( int value) { mBooleans = value; @@ -723,7 +732,7 @@ public class PackageStateImpl implements PackageState { time = 1671671043929L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java", - inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackage mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy int mHiddenApiEnforcementPolicy\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSeInfo\nprivate final boolean mHasSharedUser\nprivate final int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List mUsesLibraries\nprivate final @android.annotation.NonNull java.util.List mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override boolean isApex()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nprivate static final int APK_IN_UPDATED_APEX\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)") + inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackage mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy int mHiddenApiEnforcementPolicy\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSeInfo\nprivate final boolean mHasSharedUser\nprivate final int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List mUsesLibraries\nprivate final @android.annotation.NonNull java.util.List mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray mUserStates\nprivate final @android.annotation.Nullable java.lang.String mApexModuleName\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override boolean isApex()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nprivate static final int APK_IN_UPDATED_APEX\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java index 57fbfe91193b..19c08866e348 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java +++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java @@ -54,7 +54,6 @@ public class PackageStateUnserialized { private List usesLibraryFiles = emptyList(); private boolean updatedSystemApp; - private boolean apkInApex; private boolean apkInUpdatedApex; @NonNull @@ -70,6 +69,9 @@ public class PackageStateUnserialized { @NonNull private final PackageSetting mPackageSetting; + @Nullable + private String mApexModuleName; + public PackageStateUnserialized(@NonNull PackageSetting packageSetting) { mPackageSetting = packageSetting; } @@ -138,11 +140,11 @@ public class PackageStateUnserialized { } this.updatedSystemApp = other.updatedSystemApp; - this.apkInApex = other.apkInApex; this.apkInUpdatedApex = other.apkInUpdatedApex; this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills; this.overrideSeInfo = other.overrideSeInfo; this.seInfo = other.seInfo; + this.mApexModuleName = other.mApexModuleName; mPackageSetting.onChanged(); } @@ -187,12 +189,6 @@ public class PackageStateUnserialized { return this; } - public PackageStateUnserialized setApkInApex(boolean value) { - apkInApex = value; - mPackageSetting.onChanged(); - return this; - } - public PackageStateUnserialized setApkInUpdatedApex(boolean value) { apkInUpdatedApex = value; mPackageSetting.onChanged(); @@ -218,6 +214,13 @@ public class PackageStateUnserialized { return this; } + @NonNull + public PackageStateUnserialized setApexModuleName(@NonNull String value) { + mApexModuleName = value; + mPackageSetting.onChanged(); + return this; + } + // Code below generated by codegen v1.0.23. @@ -253,11 +256,6 @@ public class PackageStateUnserialized { return updatedSystemApp; } - @DataClass.Generated.Member - public boolean isApkInApex() { - return apkInApex; - } - @DataClass.Generated.Member public boolean isApkInUpdatedApex() { return apkInUpdatedApex; @@ -292,11 +290,16 @@ public class PackageStateUnserialized { return mPackageSetting; } + @DataClass.Generated.Member + public @Nullable String getApexModuleName() { + return mApexModuleName; + } + @DataClass.Generated( - time = 1666291743725L, + time = 1671483772254L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java", - inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate boolean apkInApex\nprivate boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull java.lang.String seInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryInfo(com.android.server.pm.pkg.SharedLibraryWrapper)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryFile(java.lang.String)\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized setSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)") + inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate @android.annotation.NonNull java.lang.String seInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate @android.annotation.Nullable java.lang.String mApexModuleName\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryInfo(com.android.server.pm.pkg.SharedLibraryWrapper)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryFile(java.lang.String)\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized setSeInfo(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized setApexModuleName(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index 83677c2a2242..47e7a37e9352 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -30,11 +30,16 @@ java_test_host { "truth-prebuilt", ], static_libs: [ + "ApexInstallHelper", "cts-host-utils", "frameworks-base-hostutils", "PackageManagerServiceHostTestsIntentVerifyUtils", ], test_suites: ["general-tests"], + data: [ + ":PackageManagerTestApex", + ":PackageManagerTestApexApp", + ], java_resources: [ ":PackageManagerTestOverlayActor", ":PackageManagerTestOverlay", diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/ApexUpdateTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/ApexUpdateTest.kt new file mode 100644 index 000000000000..44b4e303565c --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/ApexUpdateTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 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.modules.testing.utils.ApexInstallHelper +import com.android.tradefed.invoker.TestInformation +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.android.tradefed.testtype.junit4.BeforeClassWithInfo +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.AfterClass +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(DeviceJUnit4ClassRunner::class) +class ApexUpdateTest : BaseHostJUnit4Test() { + + companion object { + private const val APEX_NAME = "com.android.server.pm.test.apex" + private const val APK_IN_APEX_NAME = "$APEX_NAME.app" + private const val APK_FILE_NAME = "PackageManagerTestApexApp.apk" + + private lateinit var apexInstallHelper: ApexInstallHelper + + @JvmStatic + @BeforeClassWithInfo + fun initApexHelper(testInformation: TestInformation) { + apexInstallHelper = ApexInstallHelper(testInformation) + } + + @JvmStatic + @AfterClass + fun revertChanges() { + apexInstallHelper.revertChanges() + } + } + + @Before + @After + fun uninstallApp() { + device.uninstallPackage(APK_IN_APEX_NAME) + } + + @Test + fun apexModuleName() { + // Install the test APEX and assert it's returned as the APEX module itself + // (null when not --include-apex) + apexInstallHelper.pushApexAndReboot("PackageManagerTestApex.apex") + assertModuleName(APEX_NAME).isNull() + assertModuleName(APEX_NAME, includeApex = true).isEqualTo(APEX_NAME) + + // Check the APK-in-APEX, ensuring there is only 1 active package + assertModuleName(APK_IN_APEX_NAME).isEqualTo(APEX_NAME) + assertModuleName(APK_IN_APEX_NAME, hidden = true).isNull() + + // Then install a /data update to the APK-in-APEX + device.installPackage(testInformation.getDependencyFile(APK_FILE_NAME, false), false) + + // Verify same as above + assertModuleName(APEX_NAME, includeApex = true).isEqualTo(APEX_NAME) + assertModuleName(APK_IN_APEX_NAME).isEqualTo(APEX_NAME) + + // But also check that the /data variant now has a hidden package + assertModuleName(APK_IN_APEX_NAME, hidden = true).isEqualTo(APEX_NAME) + + // Reboot the device and check that values are preserved + device.reboot() + assertModuleName(APEX_NAME, includeApex = true).isEqualTo(APEX_NAME) + assertModuleName(APK_IN_APEX_NAME).isEqualTo(APEX_NAME) + assertModuleName(APK_IN_APEX_NAME, hidden = true).isEqualTo(APEX_NAME) + + // Revert the install changes (delete system image APEX) and check that it's gone + apexInstallHelper.revertChanges() + assertModuleName(APEX_NAME, includeApex = true).isNull() + + // Verify the module name is no longer associated with the APK-in-APEX, + // which is now just a regular /data APK with no hidden system variant. + // The assertion for the valid /data APK uses "null" because the value + // printed for normal packages is "apexModuleName=null". As opposed to + // a literal null indicating the package variant doesn't exist + assertModuleName(APK_IN_APEX_NAME).isEqualTo("null") + assertModuleName(APK_IN_APEX_NAME, hidden = true).isEqualTo(null) + } + + private fun assertModuleName( + packageName: String, + hidden: Boolean = false, + includeApex: Boolean = false + ) = assertThat( + device.executeShellCommand( + "dumpsys package ${"--include-apex".takeIf { includeApex }} $packageName" + ) + .lineSequence() + .map(String::trim) + .dropWhile { !it.startsWith(if (hidden) "Hidden system packages:" else "Packages:")} + .dropWhile { !it.startsWith("Package [$packageName]") } + .takeWhile { !it.startsWith("User 0:") } + .firstOrNull { it.startsWith("apexModuleName=") } + ?.removePrefix("apexModuleName=") + ) +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp new file mode 100644 index 000000000000..aef365e9d5fc --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2022 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +apex { + name: "PackageManagerTestApex", + apps: ["PackageManagerTestApexApp"], + androidManifest: "AndroidManifestApex.xml", + file_contexts: ":apex.test-file_contexts", + key: "apex.test.key", + certificate: ":apex.test.certificate", + min_sdk_version: "33", + installable: true, + updatable: true, +} + +android_test_helper_app { + name: "PackageManagerTestApexApp", + manifest: "AndroidManifestApp.xml", + sdk_version: "33", + min_sdk_version: "33", + apex_available: ["PackageManagerTestApex"], + certificate: ":apex.test.certificate", +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApex.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApex.xml new file mode 100644 index 000000000000..575b2bc74940 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApex.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApp.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApp.xml new file mode 100644 index 000000000000..87fb5cc2c14a --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApp.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/apex_manifest.json b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/apex_manifest.json new file mode 100644 index 000000000000..b89581d1e0ca --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.server.pm.test.apex", + "version": 1 +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index 2f909aa7b191..5b0e2f3800c3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -518,6 +518,7 @@ public class ApexManagerTest { apexInfo.isActive = isActive; apexInfo.isFactory = isFactory; apexInfo.modulePath = apexFile.getPath(); + apexInfo.preinstalledModulePath = apexFile.getPath(); return apexInfo; } -- cgit v1.2.3-59-g8ed1b