summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2021-07-20 15:26:46 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2021-07-20 15:26:46 +0000
commit18a1022ca7900b9211e445633e8eff4f06a00008 (patch)
tree2ecb84d429a314eb1d6afbd848ea0e1b8318e583
parent85e0db545efec0839b56455a47d37504749cf6d3 (diff)
parentb9f01f9805edfff0d6ad23369de372c7de005687 (diff)
downloadapex-18a1022ca7900b9211e445633e8eff4f06a00008.tar.gz
Merge "Handle edge cases of shared libs APEX versions." into sc-dev
-rw-r--r--apexd/apexd.cpp37
-rw-r--r--apexd/apexd_test.cpp29
-rw-r--r--tests/src/com/android/tests/apex/SharedLibsApexTest.java217
3 files changed, 274 insertions, 9 deletions
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index cc1f1519..5948c5ea 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -2511,19 +2511,46 @@ std::vector<ApexFileRef> SelectApexForActivation(
const ApexFileRef& a_ref,
const int version_b) mutable {
const ApexFile& a = a_ref.get();
- // APEX that provides shared library always gets activated
- const bool provides_shared_apex_libs =
- a.GetManifest().providesharedapexlibs();
// If A has higher version than B, then it should be activated
const bool higher_version = a.GetManifest().version() > version_b;
// If A has same version as B, then data version should get activated
const bool same_version_priority_to_data =
a.GetManifest().version() == version_b &&
!instance.IsPreInstalledApex(a);
- if (provides_shared_apex_libs || higher_version ||
- same_version_priority_to_data) {
+
+ // APEX that provides shared library are special:
+ // - if preinstalled version is lower than data version, both versions
+ // are activated.
+ // - if preinstalled version is equal to data version, data version only
+ // is activated.
+ // - if preinstalled version is higher than data version, preinstalled
+ // version only is activated.
+ const bool provides_shared_apex_libs =
+ a.GetManifest().providesharedapexlibs();
+ bool activate = false;
+ if (provides_shared_apex_libs) {
+ // preinstalled version gets activated in all cases except when same
+ // version as data.
+ if (instance.IsPreInstalledApex(a) &&
+ (a.GetManifest().version() != version_b)) {
+ LOG(DEBUG) << "Activating preinstalled shared libs APEX: "
+ << a.GetManifest().name() << " " << a.GetPath();
+ activate = true;
+ }
+ // data version gets activated in all cases except when its version
+ // is lower than preinstalled version.
+ if (!instance.IsPreInstalledApex(a) &&
+ (a.GetManifest().version() >= version_b)) {
+ LOG(DEBUG) << "Activating shared libs APEX: "
+ << a.GetManifest().name() << " " << a.GetPath();
+ activate = true;
+ }
+ } else if (higher_version || same_version_priority_to_data) {
LOG(DEBUG) << "Selecting between two APEX: " << a.GetManifest().name()
<< " " << a.GetPath();
+ activate = true;
+ }
+ if (activate) {
activation_list.emplace_back(a_ref);
}
};
diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp
index 585262c8..a3ebd03a 100644
--- a/apexd/apexd_test.cpp
+++ b/apexd/apexd_test.cpp
@@ -222,6 +222,8 @@ TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) {
auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex"));
auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex"));
+ // Normally both pre-installed and data apex would be activated for a shared
+ // libs apex, but if they are the same version only the data apex will be.
auto shared_lib_2 = ApexFile::Open(
AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
@@ -234,10 +236,9 @@ TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) {
ASSERT_EQ(result.size(), 0u);
// When passed proper instance they should get selected
result = SelectApexForActivation(all_apex, instance);
- ASSERT_EQ(result.size(), 4u);
+ ASSERT_EQ(result.size(), 3u);
ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)),
ApexFileEq(ByRef(*shim_v1)),
- ApexFileEq(ByRef(*shared_lib_1)),
ApexFileEq(ByRef(*shared_lib_2))));
}
@@ -285,7 +286,8 @@ TEST_F(ApexdUnitTest, DataApexGetsPriorityForSameVersions) {
ApexFileEq(ByRef(*shim_v1))));
}
-// Both versions of shared libs can be selected
+// Both versions of shared libs can be selected when preinstalled version is
+// lower than data version
TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) {
auto shared_lib_v1 = ApexFile::Open(AddPreInstalledApex(
"com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
@@ -306,6 +308,27 @@ TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) {
ApexFileEq(ByRef(*shared_lib_v2))));
}
+// Data version of shared libs should not be selected if lower than
+// preinstalled version
+TEST_F(ApexdUnitTest, SharedLibsDataVersionDeletedIfLower) {
+ auto shared_lib_v2 = ApexFile::Open(AddPreInstalledApex(
+ "com.android.apex.test.sharedlibs_generated.v2.libvY.apex"));
+ // Initialize pre-installed APEX information
+ auto& instance = ApexFileRepository::GetInstance();
+ ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()})));
+
+ auto shared_lib_v1 = ApexFile::Open(
+ AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex"));
+ // Initialize data APEX information
+ ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir())));
+
+ auto all_apex = instance.AllApexFilesByName();
+ auto result = SelectApexForActivation(all_apex, instance);
+ ASSERT_EQ(result.size(), 1u);
+
+ ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v2))));
+}
+
TEST_F(ApexdUnitTest, ProcessCompressedApex) {
auto compressed_apex = ApexFile::Open(
AddPreInstalledApex("com.android.apex.compressed.v1.capex"));
diff --git a/tests/src/com/android/tests/apex/SharedLibsApexTest.java b/tests/src/com/android/tests/apex/SharedLibsApexTest.java
index 9097e3a2..fdeac2c3 100644
--- a/tests/src/com/android/tests/apex/SharedLibsApexTest.java
+++ b/tests/src/com/android/tests/apex/SharedLibsApexTest.java
@@ -132,6 +132,46 @@ public class SharedLibsApexTest extends BaseHostJUnit4Test {
}
/**
+ * Utility function to generate the file name of an installed package as per
+ * apexd convention e.g.: "com.android.apex.test.bar@1.apex"
+ */
+ private String getInstalledApexFileName(ApexName apexName, ApexVersion apexVersion) {
+ StringBuilder ret = new StringBuilder();
+ ret.append("com.android.apex.test.");
+ switch(apexName) {
+ case FOO:
+ ret.append("foo");
+ break;
+ case BAR:
+ ret.append("bar");
+ break;
+ case BAZ:
+ ret.append("baz");
+ break;
+ case PONY:
+ ret.append("pony");
+ break;
+ case SHAREDLIBS:
+ ret.append("sharedlibs");
+ break;
+ case SHAREDLIBS_SECONDARY:
+ ret.append("sharedlibs_secondary");
+ break;
+ }
+ ret.append("@");
+ switch(apexVersion) {
+ case ONE:
+ ret.append("1");
+ break;
+ case TWO:
+ ret.append("2");
+ break;
+ }
+ ret.append(".apex");
+ return ret.toString();
+ }
+
+ /**
* Tests basic functionality of two apex packages being force-installed and the C++ binaries
* contained in them being executed correctly.
*/
@@ -198,6 +238,8 @@ public class SharedLibsApexTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
assumeTrue("Device requires root", getDevice().isAdbRoot());
+ // Base case:
+ //
// Pre-installed on /system:
// package bar version 1 using library version X
// package foo version 1 using library version X
@@ -236,10 +278,52 @@ public class SharedLibsApexTest extends BaseHostJUnit4Test {
"/apex/com.android.apex.test.pony/bin/pony_test");
assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z");
+ // Edge case: sharedlibs updated with a same version apex.
+ //
+ // Updated packages (installed on /data/apex/active):
+ // package sharedlibs version 1 exporting library version X <-- new
+ // package sharedlibs_secondary version 1 exporting library version Z <-- new
+ //
+ // Pre-installed:
+ // package bar version 1 using library version X
+ // package foo version 1 using library version X
+ // (inactive) package sharedlibs version 1 exporting library version X
+ //
+ // package pony version 1 using library version Z
+ // (inactive) package sharedlibs_secondary version 1 exporting library version Z
+
+ mPreparer.stageMultiplePackages(
+ new String[]{
+ getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE,
+ SharedLibsVersion.X),
+ getTestApex(ApexName.SHAREDLIBS_SECONDARY, ApexType.DEFAULT, ApexVersion.ONE,
+ SharedLibsVersion.Z),
+ },
+ new String[]{
+ "com.android.apex.test.sharedlibs",
+ "com.android.apex.test.sharedlibs_secondary",
+ }).reboot();
+
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.foo/bin/foo_test");
+ assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X");
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test32");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) {
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test64");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ }
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.pony/bin/pony_test");
+ assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z");
+
// Updated packages (installed on /data/apex/active):
// package bar version 2 using library version Y <-- new
// package foo version 2 using library version Y <-- new
// package sharedlibs version 2 exporting library version Y <-- new
+ // package sharedlibs_secondary version 1 exporting library version Z
//
// Pre-installed:
// (inactive) package bar version 1 using library version X
@@ -247,7 +331,7 @@ public class SharedLibsApexTest extends BaseHostJUnit4Test {
// package sharedlibs version 1 exporting library version X
//
// package pony version 1 using library version Z
- // package sharedlibs_secondary version 1 exporting library version Z
+ // (inactive) package sharedlibs_secondary version 1 exporting library version Z
mPreparer.stageMultiplePackages(
new String[]{
@@ -316,4 +400,135 @@ public class SharedLibsApexTest extends BaseHostJUnit4Test {
"/apex/com.android.apex.test.pony/bin/pony_test");
assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z");
}
+
+ /**
+ * Tests that when a shared library apex is updated via OTA the previously
+ * downloaded version is remoted.
+ */
+ @Test
+ public void testHigherVersionOnSystemDeletesDataVersion() throws Exception {
+ assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+ assumeTrue("Device requires root", getDevice().isAdbRoot());
+
+ // Base case:
+ //
+ // Pre-installed on /system:
+ // package bar version 1 using library version X
+ // package foo version 1 using library version X
+ // package sharedlibs version 1 exporting library version X
+ for (String apex : new String[]{
+ getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE,
+ SharedLibsVersion.X),
+ }) {
+ mPreparer.pushResourceFile(apex,
+ "/system/apex/" + apex);
+ }
+ mPreparer.reboot();
+ String runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.foo/bin/foo_test");
+ assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X");
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test32");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) {
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test64");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ }
+
+ // Same-grade case:
+ //
+ // Pre-installed on /system:
+ // package bar version 1 using library version X
+ // package foo version 1 using library version X
+ // package sharedlibs version 1 exporting library version X
+ // Updated packages (installed on /data/apex/active):
+ // package bar version 1 using library version X
+ // package foo version 1 using library version X
+ // package sharedlibs version 1 exporting library version X
+ mPreparer.stageMultiplePackages(
+ new String[]{
+ getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE,
+ SharedLibsVersion.X),
+ },
+ new String[]{
+ "com.android.apex.test.bar",
+ "com.android.apex.test.foo",
+ "com.android.apex.test.sharedlibs",
+ }).reboot();
+
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.foo/bin/foo_test");
+ assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X");
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test32");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) {
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test64");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X");
+ }
+
+ // Simulate OTA upgrading pre-installed modules:
+ //
+ // Pre-installed on /system:
+ // package bar version 2 using library version Y
+ // package foo version 2 using library version Y
+ // package sharedlibs version 2 exporting library version Y
+ //
+ // Updated packages (installed on /data/apex/active):
+ // package bar version 1 using library version X (deleted)
+ // package foo version 1 using library version X (deleted)
+ // package sharedlibs version 1 exporting library version X (deleted)
+ //
+ for (String apex : new String[]{
+ getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X),
+ getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE,
+ SharedLibsVersion.X),
+ }) {
+ mPreparer.deleteFile("/system/apex/" + apex);
+ }
+ for (String apex : new String[]{
+ getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y),
+ getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y),
+ getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.TWO,
+ SharedLibsVersion.Y),
+ }) {
+ mPreparer.pushResourceFile(apex,
+ "/system/apex/" + apex);
+ }
+
+ // Check that files in /data are deleted on first boot.
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.BAR, ApexVersion.ONE))).isTrue();
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.FOO, ApexVersion.ONE))).isTrue();
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.SHAREDLIBS, ApexVersion.ONE))).isTrue();
+ mPreparer.reboot();
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.BAR, ApexVersion.ONE))).isFalse();
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.FOO, ApexVersion.ONE))).isFalse();
+ assertThat(getDevice().doesFileExist("/data/apex/active/"
+ + getInstalledApexFileName(ApexName.SHAREDLIBS, ApexVersion.ONE))).isFalse();
+
+ getDevice().disableAdbRoot();
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.foo/bin/foo_test");
+ assertThat(runAsResult).isEqualTo("FOO_VERSION_2 SHARED_LIB_VERSION_Y");
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test32");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y");
+ if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) {
+ runAsResult = getDevice().executeShellCommand(
+ "/apex/com.android.apex.test.bar/bin/bar_test64");
+ assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y");
+ }
+ }
}