diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2021-07-20 15:26:46 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-07-20 15:26:46 +0000 |
commit | 18a1022ca7900b9211e445633e8eff4f06a00008 (patch) | |
tree | 2ecb84d429a314eb1d6afbd848ea0e1b8318e583 | |
parent | 85e0db545efec0839b56455a47d37504749cf6d3 (diff) | |
parent | b9f01f9805edfff0d6ad23369de372c7de005687 (diff) | |
download | apex-18a1022ca7900b9211e445633e8eff4f06a00008.tar.gz |
Merge "Handle edge cases of shared libs APEX versions." into sc-dev
-rw-r--r-- | apexd/apexd.cpp | 37 | ||||
-rw-r--r-- | apexd/apexd_test.cpp | 29 | ||||
-rw-r--r-- | tests/src/com/android/tests/apex/SharedLibsApexTest.java | 217 |
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"); + } + } } |