summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/DumpHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/DumpState.java1
-rw-r--r--services/core/java/com/android/server/pm/InitAppsHelper.java26
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java70
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java16
-rw-r--r--services/core/java/com/android/server/pm/InstallingSession.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java11
-rw-r--r--services/core/java/com/android/server/pm/ScanPartition.java26
-rw-r--r--services/core/java/com/android/server/pm/Settings.java7
-rw-r--r--services/core/java/com/android/server/pm/StorageEventHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageState.java7
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateImpl.java11
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java33
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp5
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/ApexUpdateTest.kt117
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp40
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApex.xml20
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Apex/AndroidManifestApp.xml20
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Apex/apex_manifest.json4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java1
22 files changed, 369 insertions, 55 deletions
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<Runnable> 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 28a074b4e5d4..4e5f77f82d8a 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<ScanResult, Boolean> 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 878c1c159797..e6d99eb66291 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;
@@ -348,6 +355,11 @@ final class InstallRequest {
}
@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 439b54259554..8fa74ef431f9 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<ScanPartition> 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<PackageUserState> 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
@@ -714,6 +718,11 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated.Member
+ public @Nullable String getApexModuleName() {
+ return mApexModuleName;
+ }
+
+ @DataClass.Generated.Member
public @NonNull PackageStateImpl setBooleans( int value) {
mBooleans = value;
return this;
@@ -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<java.lang.String,java.util.Set<java.lang.String>> 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<com.android.server.pm.pkg.SharedLibrary> mUsesLibraries\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> 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<com.android.server.pm.pkg.PackageUserState> 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<java.lang.String,java.util.Set<java.lang.String>> 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<com.android.server.pm.pkg.SharedLibrary> mUsesLibraries\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> 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<com.android.server.pm.pkg.PackageUserState> 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<String> 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.
@@ -254,11 +257,6 @@ public class PackageStateUnserialized {
}
@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<com.android.server.pm.pkg.SharedLibraryWrapper> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> 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<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\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<com.android.server.pm.pkg.SharedLibraryWrapper> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> 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<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest package="com.android.server.pm.test.apex">
+ <application/>
+</manifest>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest package="com.android.server.pm.test.apex.app">
+ <application/>
+</manifest>
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;
}