Allow apps in /product to access extended public libraries.

Test: atest -a libnativeloader_e2e_tests
Bug: 237572732
Change-Id: I93e38e81ef6f37a4dbf78833e83ec5a02d6b8211
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index f31c430..96d4dde 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -231,51 +231,38 @@
   std::string system_exposed_libraries = default_public_libraries();
   std::string namespace_name = kClassloaderNamespaceName;
   ApkOrigin unbundled_app_origin = APK_ORIGIN_DEFAULT;
-  if ((apk_origin == APK_ORIGIN_VENDOR ||
-       (apk_origin == APK_ORIGIN_PRODUCT &&
-        is_product_vndk_version_defined())) &&
-      !is_shared) {
-    unbundled_app_origin = apk_origin;
-    // For vendor / product apks, give access to the vendor / product lib even though
-    // they are treated as unbundled; the libs and apks are still bundled
-    // together in the vendor / product partition.
-    const char* origin_partition;
-    const char* origin_lib_path;
-    const char* llndk_libraries;
+  const char* apk_origin_msg = "other apk";  // Only for debug logging.
 
-    switch (apk_origin) {
-      case APK_ORIGIN_VENDOR:
-        origin_partition = "vendor";
-        origin_lib_path = kVendorLibPath;
-        llndk_libraries = llndk_libraries_vendor().c_str();
-        break;
-      case APK_ORIGIN_PRODUCT:
-        origin_partition = "product";
-        origin_lib_path = kProductLibPath;
-        llndk_libraries = llndk_libraries_product().c_str();
-        break;
-      default:
-        origin_partition = "unknown";
-        origin_lib_path = "";
-        llndk_libraries = "";
-    }
-    library_path = library_path + ":" + origin_lib_path;
-    permitted_path = permitted_path + ":" + origin_lib_path;
+  if (!is_shared) {
+    if (apk_origin == APK_ORIGIN_VENDOR) {
+      unbundled_app_origin = APK_ORIGIN_VENDOR;
+      apk_origin_msg = "unbundled vendor apk";
 
-    // Also give access to LLNDK libraries since they are available to vendor or product
-    system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries;
+      // For vendor apks, give access to the vendor libs even though they are
+      // treated as unbundled; the libs and apks are still bundled together in the
+      // vendor partition.
+      library_path = library_path + ':' + kVendorLibPath;
+      permitted_path = permitted_path + ':' + kVendorLibPath;
 
-    // Different name is useful for debugging
-    namespace_name = kVendorClassloaderNamespaceName;
-    ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
-          origin_partition, library_path.c_str());
-  } else {
-    auto libs = filter_public_libraries(target_sdk_version, uses_libraries,
-                                        extended_public_libraries());
-    // extended public libraries are NOT available to vendor apks, otherwise it
-    // would be system->vendor violation.
-    if (!libs.empty()) {
-      system_exposed_libraries = system_exposed_libraries + ':' + libs;
+      // Also give access to LLNDK libraries since they are available to vendor.
+      system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_vendor();
+
+      // Different name is useful for debugging
+      namespace_name = kVendorClassloaderNamespaceName;
+    } else if (apk_origin == APK_ORIGIN_PRODUCT && is_product_vndk_version_defined()) {
+      unbundled_app_origin = APK_ORIGIN_PRODUCT;
+      apk_origin_msg = "unbundled product apk";
+
+      // Like for vendor apks, give access to the product libs since they are
+      // bundled together in the same partition.
+      library_path = library_path + ':' + kProductLibPath;
+      permitted_path = permitted_path + ':' + kProductLibPath;
+
+      // Also give access to LLNDK libraries since they are available to product.
+      system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_product();
+
+      // Different name is useful for debugging
+      namespace_name = kVendorClassloaderNamespaceName;
     }
   }
 
@@ -285,6 +272,32 @@
     namespace_name = namespace_name + kSharedNamespaceSuffix;
   }
 
+  ALOGD(
+      "Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
+      "permitted_path=%s",
+      namespace_name.c_str(),
+      apk_origin_msg,
+      dex_path.c_str(),
+      static_cast<unsigned>(target_sdk_version),
+      android::base::Join(uses_libraries, ':').c_str(),
+      library_path.c_str(),
+      permitted_path.c_str());
+
+  if (unbundled_app_origin != APK_ORIGIN_VENDOR) {
+    // Extended public libraries are NOT available to unbundled vendor apks, but
+    // they are to other apps, including those in system, system_ext, and
+    // product partitions. The reason is that when GSI is used, the system
+    // partition may get replaced, and then vendor apps may fail. It's fine for
+    // product (and system_ext) apps, because those partitions aren't mounted in
+    // GSI tests.
+    auto libs =
+        filter_public_libraries(target_sdk_version, uses_libraries, extended_public_libraries());
+    if (!libs.empty()) {
+      ALOGD("Extending system_exposed_libraries: %s", libs.c_str());
+      system_exposed_libraries = system_exposed_libraries + ':' + libs;
+    }
+  }
+
   // Create the app namespace
   NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
   // Heuristic: the first classloader with non-empty library_path is assumed to
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
index 123e06b..243e2c7 100644
--- a/libnativeloader/test/src/android/test/app/ProductAppTest.java
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -29,18 +29,11 @@
 public class ProductAppTest {
     @Test
     public void testLoadLibraries() {
-        assertLinkerNamespaceError("foo.oem1");
-        assertLinkerNamespaceError("bar.oem1");
-        assertLinkerNamespaceError("foo.oem2");
-        assertLinkerNamespaceError("bar.oem2");
+        System.loadLibrary("foo.oem1");
+        System.loadLibrary("bar.oem1");
+        System.loadLibrary("foo.oem2");
+        System.loadLibrary("bar.oem2");
         System.loadLibrary("foo.product1");
         System.loadLibrary("bar.product1");
     }
-
-    private void assertLinkerNamespaceError(String libraryName) {
-        Throwable t =
-                assertThrows(UnsatisfiedLinkError.class, () -> System.loadLibrary(libraryName));
-        assertThat(t.getMessage())
-                .containsMatch("dlopen failed: .* is not accessible for the namespace");
-    }
 }