summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java10
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java37
-rw-r--r--tests/StagedInstallTest/Android.bp3
-rw-r--r--tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java23
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java55
5 files changed, 127 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ff9edd511e84..2807a1e4c081 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1637,7 +1637,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* If session should be sealed, then it's sealed to prevent further modification.
* If the session can't be sealed then it's destroyed.
*
- * Additionally for staged APEX sessions read+validate the package and populate req'd fields.
+ * Additionally for staged APEX/APK sessions read+validate the package and populate req'd
+ * fields.
*
* <p> This is meant to be called after all of the sessions are loaded and added to
* PackageInstallerService
@@ -1670,6 +1671,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// APEX installations rely on certain fields to be populated after reboot.
// E.g. mPackageName.
validateApexInstallLocked();
+ } else {
+ // Populate mPackageName for this APK session which is required by the staging
+ // manager to check duplicate apk-in-apex.
+ PackageInstallerSession parent = allSessions.get(mParentSessionId);
+ if (parent != null && parent.isStagedSessionReady()) {
+ validateApkInstallLocked();
+ }
}
} catch (PackageManagerException e) {
Slog.e(TAG, "Package not valid", e);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index f9bf54a11df0..ac05aabf998f 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -56,6 +56,7 @@ import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -84,6 +85,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -650,6 +652,7 @@ public class StagingManager {
try {
if (hasApex) {
checkInstallationOfApkInApexSuccessful(session);
+ checkDuplicateApkInApex(session);
snapshotAndRestoreForApexSession(session);
Slog.i(TAG, "APEX packages in session " + session.sessionId
+ " were successfully activated. Proceeding with APK packages, if any");
@@ -829,6 +832,40 @@ public class StagingManager {
return null;
}
+ /**
+ * Throws a PackageManagerException if there are duplicate packages in apk and apk-in-apex.
+ */
+ private void checkDuplicateApkInApex(@NonNull PackageInstallerSession session)
+ throws PackageManagerException {
+ if (!session.isMultiPackage()) {
+ return;
+ }
+ final int[] childSessionIds = session.getChildSessionIds();
+ final Set<String> apkNames = new ArraySet<>();
+ synchronized (mStagedSessions) {
+ for (int id : childSessionIds) {
+ final PackageInstallerSession s = mStagedSessions.get(id);
+ if (!isApexSession(s)) {
+ apkNames.add(s.getPackageName());
+ }
+ }
+ }
+ final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
+ for (PackageInstallerSession apexSession : apexSessions) {
+ String packageName = apexSession.getPackageName();
+ for (String apkInApex : mApexManager.getApksInApex(packageName)) {
+ if (!apkNames.add(apkInApex)) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Package: " + packageName + " in session: "
+ + apexSession.sessionId + " has duplicate apk-in-apex: "
+ + apkInApex, null);
+
+ }
+ }
+ }
+ }
+
private void installApksInSession(@NonNull PackageInstallerSession session)
throws PackageManagerException {
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 530d0e492f2e..76f8df02465b 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -18,6 +18,9 @@ android_test_helper_app {
srcs: ["app/src/**/*.java"],
static_libs: ["androidx.test.rules", "cts-install-lib"],
test_suites: ["general-tests"],
+ java_resources: [
+ ":com.android.apex.apkrollback.test_v2",
+ ],
}
java_test_host {
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 02597d548361..12f1750fb78c 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -49,6 +49,11 @@ import java.util.function.Consumer;
public class StagedInstallInternalTest {
private static final String TAG = StagedInstallInternalTest.class.getSimpleName();
+ private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+ private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1",
+ APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
+ private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
+ APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
private File mTestStateFile = new File(
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
@@ -82,6 +87,24 @@ public class StagedInstallInternalTest {
}
@Test
+ public void testDuplicateApkInApexShouldFail_Commit() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ // Duplicate packages(TestApp.A) in TEST_APEX_WITH_APK_V2(apk-in-apex) and TestApp.A2(apk)
+ // should fail to install.
+ int sessionId = Install.multi(TEST_APEX_WITH_APK_V2, TestApp.A2).setStaged().commit();
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testDuplicateApkInApexShouldFail_Verify() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ int sessionId = retrieveLastSessionId();
+ PackageInstaller.SessionInfo info =
+ InstallUtils.getPackageInstaller().getSessionInfo(sessionId);
+ assertThat(info.isStagedSessionFailed()).isTrue();
+ }
+
+ @Test
public void testSystemServerRestartDoesNotAffectStagedSessions_Commit() throws Exception {
int sessionId = Install.single(TestApp.A1).setStaged().commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 55def498a0cd..792e29d93be2 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -24,11 +24,14 @@ import static org.junit.Assume.assumeTrue;
import android.cts.install.lib.host.InstallUtilsHost;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tests.rollback.host.AbandonSessionsRule;
import com.android.tests.util.ModuleTestUtils;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.ProcessInfo;
import org.junit.After;
@@ -49,6 +52,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex";
private static final String APK_A = "TestAppAv1.apk";
+ private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
@@ -74,6 +78,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
} catch (AssertionError e) {
Log.e(TAG, e);
}
+ deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
+ "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
}
@Before
@@ -86,6 +92,55 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
cleanUp();
}
+ /**
+ * Deletes files and reboots the device if necessary.
+ * @param files the paths of files which might contain wildcards
+ */
+ private void deleteFiles(String... files) throws Exception {
+ boolean found = false;
+ for (String file : files) {
+ CommandResult result = getDevice().executeShellV2Command("ls " + file);
+ if (result.getStatus() == CommandStatus.SUCCESS) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ for (String file : files) {
+ getDevice().executeShellCommand("rm -rf " + file);
+ }
+ getDevice().reboot();
+ }
+ }
+
+ private void pushTestApex() throws Exception {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
+ final File apex = buildHelper.getTestFile(fileName);
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+ getDevice().reboot();
+ }
+
+ /**
+ * Tests that duplicate packages in apk-in-apex and apk should fail to install.
+ */
+ @Test
+ public void testDuplicateApkInApexShouldFail() throws Exception {
+ pushTestApex();
+ runPhase("testDuplicateApkInApexShouldFail_Commit");
+ getDevice().reboot();
+ runPhase("testDuplicateApkInApexShouldFail_Verify");
+ }
+
@Test
public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception {
runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit");