Move apex-update-allowed check
1. Move the check into PackageSessionVerifier
2. Fix a bug that the check doesn't work for multi-package sessions
3. If reboot happens before running this check, it will be run on
next boot.
Bug: 213097119
Test: atest StagedInstallInternalTest
Change-Id: I92c44688be21498c11b6f7cf0dccfd3aabea0ba2
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a94985c..2887d99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -151,7 +151,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2357,28 +2356,6 @@
return;
}
-
- // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
- // the places that:
- // * Shared between staged and non-staged APEX update flows.
- // * Only is called after boot completes.
- // The later is important, since isApexUpdateAllowed check depends on the
- // ModuleInfoProvider, which is only populated after device has booted.
- if (isApexSession()) {
- boolean checkApexUpdateAllowed =
- (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
- == 0;
- synchronized (mLock) {
- if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName,
- mInstallSource.installerPackageName)) {
- onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Update of APEX package " + mPackageName + " is not allowed for "
- + mInstallSource.installerPackageName);
- return;
- }
- }
- }
-
if (params.isStaged) {
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
@@ -2825,25 +2802,6 @@
return sessionContains((s) -> !s.isApexSession());
}
- private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
- if (mPm.getModuleInfo(apexPackageName, 0) != null) {
- final String modulesInstaller =
- SystemConfig.getInstance().getModulesInstallerPackageName();
- if (modulesInstaller == null) {
- Slog.w(TAG, "No modules installer defined");
- return false;
- }
- return modulesInstaller.equals(installerPackageName);
- }
- final String vendorApexInstaller =
- SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
- if (vendorApexInstaller == null) {
- Slog.w(TAG, apexPackageName + " is not allowed to be updated");
- return false;
- }
- return vendorApexInstaller.equals(installerPackageName);
- }
-
/**
* Validate apex install.
* <p>
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 4f21d0e..fcb8b43 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -45,6 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.RollbackManagerInternal;
@@ -99,9 +100,11 @@
storeSession(session.mStagedSession);
if (session.isMultiPackage()) {
for (PackageInstallerSession child : session.getChildSessions()) {
+ checkApexUpdateAllowed(child);
checkRebootlessApex(child);
}
} else {
+ checkApexUpdateAllowed(session);
checkRebootlessApex(session);
}
verifyAPK(session, callback);
@@ -461,6 +464,51 @@
return mApexManager.abortStagedSession(sessionId);
}
+ private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
+ if (mPm.getModuleInfo(apexPackageName, 0) != null) {
+ final String modulesInstaller =
+ SystemConfig.getInstance().getModulesInstallerPackageName();
+ if (modulesInstaller == null) {
+ Slog.w(TAG, "No modules installer defined");
+ return false;
+ }
+ return modulesInstaller.equals(installerPackageName);
+ }
+ final String vendorApexInstaller =
+ SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
+ if (vendorApexInstaller == null) {
+ Slog.w(TAG, apexPackageName + " is not allowed to be updated");
+ return false;
+ }
+ return vendorApexInstaller.equals(installerPackageName);
+ }
+
+ /**
+ * Checks if APEX update is allowed.
+ *
+ * This phase is shared between staged and non-staged sessions and should be called after
+ * boot is completed since this check depends on the ModuleInfoProvider, which is only populated
+ * after device has booted.
+ */
+ private void checkApexUpdateAllowed(PackageInstallerSession session)
+ throws PackageManagerException {
+ if (!session.isApexSession()) {
+ return;
+ }
+ final int installFlags = session.params.installFlags;
+ if ((installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK) != 0) {
+ return;
+ }
+ final String packageName = session.getPackageName();
+ final String installerPackageName = session.getInstallSource().installerPackageName;
+ if (!isApexUpdateAllowed(packageName, installerPackageName)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Update of APEX package " + packageName + " is not allowed for "
+ + installerPackageName);
+ }
+ }
+
/**
* Fails this rebootless APEX session if the same package name found in any staged sessions.
*/
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index b21d7b5..869999e 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -303,11 +303,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -315,11 +318,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -327,11 +333,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -339,11 +348,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test