Make classloader namespace names unique, for debugging.

Also take the opportunity to switch to shorter names, when they are
changing anyway. These namespaces aren't visible, so renaming them
shouldn't be a compat issue.

Test: atest libnativeloader_test libnativeloader_e2e_tests
Test: adb shell "setprop debug.ld.all dlopen,dlerror && setprop log.tag.nativeloader :v && stop && start"
  Then check logcat that the classloader namespaces get the expected
  unique names (per process).
Bug: 258340826
Change-Id: Ic5d6ba850d8cd9cdfb3a66e175dfc87046c0ba31
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
index c5ace61..919feff 100644
--- a/libnativeloader/README.md
+++ b/libnativeloader/README.md
@@ -10,11 +10,11 @@
 
 The most typical use case of this library is calling `System.loadLibrary(name)`.
 When the method is called, the ART runtime delegates the call to this library
-along with the reference to the classloader where the call was made.  Then this
-library finds the linker namespace (named `classloader-namespace`) that is
-associated with the given classloader, and tries to load the requested library
-from the namespace. The actual searching, loading, and linking of the library
-is performed by the dynamic linker.
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (typically with the name `clns-` followed by
+a number to make it unique) that is associated with the given classloader, and
+tries to load the requested library from that namespace. The actual searching,
+loading, and linking of the library is performed by the dynamic linker.
 
 The linker namespace is created when an APK is loaded into the process, and is
 associated with the classloader that loaded the APK. The linker namespace is
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index aaf7241..2a6febd 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -30,6 +30,7 @@
 #include <android-base/macros.h>
 #include <android-base/result.h>
 #include <android-base/strings.h>
+#include <android-base/stringprintf.h>
 #include <nativehelper/scoped_utf_chars.h>
 
 #include "nativeloader/dlext_namespaces.h"
@@ -55,19 +56,20 @@
 // vndk_product namespace for unbundled product apps
 constexpr const char* kVndkProductNamespaceName = "vndk_product";
 
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// system namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for unbundled vendor APKs.
-constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-// Same thing for unbundled product APKs.
-constexpr const char* kProductClassloaderNamespaceName = "product-classloader-namespace";
+// clns-XX is a linker namespace that is created for normal apps installed in
+// the data partition. To be specific, it is created for the app classloader.
+// When System.load() is called from a Java class that is loaded from the
+// classloader, the clns namespace associated with that classloader is selected
+// for dlopen. The namespace is configured so that its search path is set to the
+// app-local JNI directory and it is linked to the system namespace with the
+// names of libs listed in the public.libraries.txt and other public libraries.
+// This way an app can only load its own JNI libraries along with the public
+// libs.
+constexpr const char* kClassloaderNamespaceName = "clns";
+// Same thing for unbundled APKs in the vendor partition.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-clns";
+// Same thing for unbundled APKs in the product partition.
+constexpr const char* kProductClassloaderNamespaceName = "product-clns";
 // If the namespace is shared then add this suffix to help identify it in debug
 // messages. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
 // inherited all the libraries of the parent classloader namespace, or the
@@ -294,6 +296,11 @@
     namespace_name = namespace_name + kSharedNamespaceSuffix;
   }
 
+  // Append a unique number to the namespace name, to tell them apart when
+  // debugging linker issues, e.g. with debug.ld.all set to "dlopen,dlerror".
+  static int clns_count = 0;
+  namespace_name = android::base::StringPrintf("%s-%d", namespace_name.c_str(), ++clns_count);
+
   ALOGD(
       "Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
       "permitted_path=%s",
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 3825614..6c0c8b1 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -32,6 +32,7 @@
 
 using ::testing::Eq;
 using ::testing::NotNull;
+using ::testing::StartsWith;
 using ::testing::StrEq;
 using internal::ConfigEntry;  // NOLINT - ConfigEntry is actually used
 using internal::ParseApexLibrariesConfig;
@@ -191,7 +192,7 @@
   std::string permitted_path = "/data/app/foo/" LIB_DIR;
 
   // expected output (.. for the default test inputs)
-  std::string expected_namespace_name = "classloader-namespace";
+  std::string expected_namespace_prefix = "clns";
   uint64_t expected_namespace_flags =
       ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
   std::string expected_library_path = library_path;
@@ -227,7 +228,7 @@
     EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
 
     EXPECT_CALL(*mock, mock_create_namespace(
-                           Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+                           Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
                            StrEq(expected_library_path), expected_namespace_flags,
                            StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
         .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
@@ -322,7 +323,7 @@
   dex_path = "/system/app/foo/foo.apk";
   is_shared = true;
 
-  expected_namespace_name = "classloader-namespace-shared";
+  expected_namespace_prefix = "clns-shared";
   expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
   SetExpectations();
   RunTest();
@@ -332,7 +333,7 @@
   dex_path = "/vendor/app/foo/foo.apk";
   is_shared = true;
 
-  expected_namespace_name = "classloader-namespace-shared";
+  expected_namespace_prefix = "clns-shared";
   expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
   SetExpectations();
   RunTest();
@@ -342,7 +343,7 @@
   dex_path = "/vendor/app/foo/foo.apk";
   is_shared = false;
 
-  expected_namespace_name = "vendor-classloader-namespace";
+  expected_namespace_prefix = "vendor-clns";
   expected_library_path = expected_library_path + ":/vendor/" LIB_DIR;
   expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
   expected_shared_libs_to_platform_ns =
@@ -356,7 +357,7 @@
   dex_path = "/product/app/foo/foo.apk";
   is_shared = true;
 
-  expected_namespace_name = "classloader-namespace-shared";
+  expected_namespace_prefix = "clns-shared";
   expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
   SetExpectations();
   RunTest();
@@ -366,7 +367,7 @@
   dex_path = "/system/framework/services.jar:/apex/com.android.conscrypt/javalib/service-foo.jar";
   is_shared = true;
 
-  expected_namespace_name = "classloader-namespace-shared";
+  expected_namespace_prefix = "clns-shared";
   expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
   expected_link_with_conscrypt_ns = true;
   SetExpectations();
@@ -378,7 +379,7 @@
   is_shared = false;
 
   if (is_product_vndk_version_defined()) {
-    expected_namespace_name = "product-classloader-namespace";
+    expected_namespace_prefix = "product-clns";
     expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
     expected_permitted_path =
         expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
@@ -416,7 +417,7 @@
   const std::string second_app_permitted_path = "/data/app/bar/" LIB_DIR;
   const std::string expected_second_app_permitted_path =
       std::string("/data:/mnt/expand:") + second_app_permitted_path;
-  const std::string expected_second_app_parent_namespace = "classloader-namespace";
+  const std::string expected_second_app_parent_namespace = "clns";
   // no ALSO_USED_AS_ANONYMOUS
   const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
 
@@ -429,7 +430,7 @@
   // namespace for the second app is created. Its parent is set to the namespace
   // of the first app.
   EXPECT_CALL(*mock, mock_create_namespace(
-                         Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+                         Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
                          StrEq(second_app_library_path), expected_second_namespace_flags,
                          StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
       .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));