Merge "Allow vendors to extend the list of public libs"
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index d38bdd2..3ffabc3 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -20,9 +20,11 @@
 
 #include <dirent.h>
 #include <dlfcn.h>
+#include <fcntl.h>
 #include <jni.h>
 #include <JNIHelp.h>
 #include <libgen.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -32,17 +34,18 @@
 #include <unordered_set>
 #include <vector>
 
-static std::vector<std::string> kDefaultLibraryPaths = {
-#if defined(__LP64__)
-    "/system/lib64",
-    "/vendor/lib64",
-#else
-    "/system/lib",
-    "/vendor/lib",
-#endif
-  };
+#include "ScopedLocalRef.h"
+#include "ScopedUtfChars.h"
 
-static std::unordered_set<std::string> kPublicLibraries = {
+#if defined(__LP64__)
+static const std::string kSystemLibraryPath = "/system/lib64";
+static const std::string kVendorLibraryPath = "/vendor/lib64";
+#else
+static const std::string kSystemLibraryPath = "/system/lib";
+static const std::string kVendorLibraryPath = "/vendor/lib";
+#endif
+
+static std::unordered_set<std::string> kSystemPublicLibraries = {
     "libandroid.so",
     "libc.so",
     "libdl.so",
@@ -89,10 +92,12 @@
   return true;
 }
 
-static bool should_be_accessible(const std::string& path) {
+static bool should_be_accessible(const std::string& public_library_path,
+                                 const std::unordered_set<std::string>& public_libraries,
+                                 const std::string& path) {
   std::string name = basename(path.c_str());
-  return (kPublicLibraries.find(name) != kPublicLibraries.end()) &&
-         (kDefaultLibraryPaths.front() + "/" + name == path);
+  return (public_libraries.find(name) != public_libraries.end()) &&
+         (public_library_path + "/" + name == path);
 }
 
 static bool is_directory(const std::string path) {
@@ -105,17 +110,19 @@
 }
 
 static bool is_libdl(const std::string path) {
-  return kDefaultLibraryPaths.front() + "/libdl.so" == path;
+  return kSystemLibraryPath + "/libdl.so" == path;
 }
 
-extern "C" JNIEXPORT jstring JNICALL Java_android_jni_cts_LinkerNamespacesHelper_runAccessibilityTest(
-        JNIEnv* env, jclass clazz __attribute__((unused))) {
-  std::list<std::string> dirs(kDefaultLibraryPaths.begin(), kDefaultLibraryPaths.end());
+static bool check_libs(const std::string& public_library_path,
+                       const std::unordered_set<std::string>& public_libraries,
+                       std::string* error) {
+  std::list<std::string> dirs;
+  dirs.push_back(public_library_path);
+
   while (!dirs.empty()) {
     const auto dir = dirs.front();
     dirs.pop_front();
-    std::string error;
-    bool success = for_each_file(dir, [&dirs,&dir](const char* name, std::string* error_msg) {
+    bool success = for_each_file(dir, [&](const char* name, std::string* error_msg) {
       std::string path = dir + "/" + name;
       if (is_directory(path)) {
         dirs.push_back(path);
@@ -132,7 +139,7 @@
 
       auto dlcloser = [](void* handle) { dlclose(handle); };
       std::unique_ptr<void, decltype(dlcloser)> handle(dlopen(path.c_str(), RTLD_NOW), dlcloser);
-      if (should_be_accessible(path)) {
+      if (should_be_accessible(public_library_path, public_libraries, path)) {
         if (handle.get() == nullptr) {
           *error_msg = "The library \"" + path + "\" should be accessible but isn't: " + dlerror();
           return false;
@@ -151,13 +158,44 @@
         }
       }
       return true;
-    }, &error);
+    }, error);
 
     if (!success) {
-      return env->NewStringUTF(error.c_str());
+      return false;
     }
   }
 
+  return true;
+}
+
+static void load_vendor_libraries(JNIEnv* env,
+                                  jobjectArray java_vendor_public_libraries,
+                                  std::unordered_set<std::string>* libraries) {
+  size_t size = env->GetArrayLength(java_vendor_public_libraries);
+  for (size_t i = 0; i<size; ++i) {
+    ScopedLocalRef<jstring> java_soname(
+        env, (jstring) env->GetObjectArrayElement(java_vendor_public_libraries, i));
+
+    ScopedUtfChars soname(env, java_soname.get());
+    libraries->insert(soname.c_str());
+  }
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+    Java_android_jni_cts_LinkerNamespacesHelper_runAccessibilityTestImpl(
+        JNIEnv* env,
+        jclass clazz __attribute__((unused)),
+        jobjectArray java_vendor_public_libraries) {
+  std::string error;
+
+  std::unordered_set<std::string> vendor_public_libraries;
+  load_vendor_libraries(env, java_vendor_public_libraries, &vendor_public_libraries);
+
+  if (!check_libs(kSystemLibraryPath, kSystemPublicLibraries, &error) ||
+      !check_libs(kVendorLibraryPath, vendor_public_libraries, &error)) {
+    return env->NewStringUTF(error.c_str());
+  }
+
   return nullptr;
 }
 
diff --git a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
index 8a91c44..91ebe73 100644
--- a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
+++ b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
@@ -16,6 +16,7 @@
 
 package android.jni.cts;
 
+import java.io.IOException;
 
 /**
  * Basic static method tests. The "nonce" class being tested by this
@@ -34,7 +35,7 @@
      * Test library accessibility. Internal platform libraries should not
      * be accessible from the jni code.
      */
-    public void test_linker_namespaces() {
+    public void test_linker_namespaces() throws IOException {
         String error = LinkerNamespacesHelper.runAccessibilityTest();
         if (error != null) {
             fail(error);
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 2dff019..21bd71a 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -16,6 +16,31 @@
 
 package android.jni.cts;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.List;
+
 class LinkerNamespacesHelper {
-    public static native String runAccessibilityTest();
+    private final static String VENDOR_CONFIG_FILE = "/vendor/etc/public.libraries.txt";
+    public static String runAccessibilityTest() throws IOException {
+        List<String> libs = new ArrayList<>();
+        File file = new File(VENDOR_CONFIG_FILE);
+        if (file.exists()) {
+            try (BufferedReader br = new BufferedReader(new FileReader(file))) {
+                String line;
+                while ((line = br.readLine()) != null) {
+                    line = line.trim();
+                    if (line.isEmpty() || line.startsWith("#")) {
+                        continue;
+                    }
+                    libs.add(line);
+                }
+            }
+        }
+        return runAccessibilityTestImpl(libs.toArray(new String[libs.size()]));
+    }
+    private static native String runAccessibilityTestImpl(String[] publicVendorLibs);
 }