Upgrade native bridge to version 3 to support namespace

Native bridge implements namespace related interfaces in version 3.
The namespace semantic here is same as Android dynamic linker's.
Native loader wraps library loading functions of dynamic linker and
native bridge. Thus, Android runtime is able to load native library
of different ISA on one device by calling native loader directly.

Bug: http://b/28242460
Test: mm && make test-art-host -j48
Change-Id: Idde2b9d99fb6ebe547407c716b5478a231f745a7
Signed-off-by: Zhenhua WANG <zhenhua.wang@intel.com>
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
index 18300bc..26556f0 100644
--- a/include/nativebridge/native_bridge.h
+++ b/include/nativebridge/native_bridge.h
@@ -62,12 +62,19 @@
 bool NativeBridgeInitialized();
 
 // Load a shared library that is supported by the native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibraryExt() instead in namespace scenario.
 void* NativeBridgeLoadLibrary(const char* libpath, int flag);
 
 // Get a native bridge trampoline for specified native method.
 void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
 
-// True if native library is valid and is for an ABI that is supported by native bridge.
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// The *libpath* must point to a library.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsPathSupported() instead in namespace scenario.
 bool NativeBridgeIsSupported(const char* libpath);
 
 // Returns the version number of the native bridge. This information is available after a
@@ -91,6 +98,48 @@
 // This functionality is exposed mainly for testing.
 bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
 
+// Decrements the reference count on the dynamic library handler. If the reference count drops
+// to zero then the dynamic library is unloaded.
+int NativeBridgeUnloadLibrary(void* handle);
+
+// Get last error message of native bridge when fail to load library or search symbol.
+// This is reflection of dlerror() for native bridge.
+char* NativeBridgeGetError();
+
+struct native_bridge_namespace_t;
+
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing
+// libraries of an ABI.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsSupported() instead in non-namespace scenario.
+bool NativeBridgeIsPathSupported(const char* path);
+
+// Initializes public and anonymous namespace at native bridge side.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeInitNamespace(const char* public_ns_sonames,
+                               const char* anon_ns_library_path);
+
+// Create a namespace and pass the key of related namespaces to native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
+                                                       const char* ld_library_path,
+                                                       const char* default_library_path,
+                                                       uint64_t type,
+                                                       const char* permitted_when_isolated_path,
+                                                       native_bridge_namespace_t* parent_ns);
+
+// Load a shared library with namespace key that is supported by the native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+
 // Native bridge interfaces to runtime.
 struct NativeBridgeCallbacks {
   // Version number of the interface.
@@ -114,6 +163,9 @@
   //   flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
   // Returns:
   //   The opaque handle of the shared library if sucessful, otherwise NULL
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use loadLibraryExt instead in namespace scenario.
   void* (*loadLibrary)(const char* libpath, int flag);
 
   // Get a native bridge trampoline for specified native method. The trampoline has same
@@ -133,6 +185,9 @@
   //   libpath [IN] path to the shared library
   // Returns:
   //   TRUE if library is supported by native bridge, FALSE otherwise
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use isPathSupported instead in namespace scenario.
   bool (*isSupported)(const char* libpath);
 
   // Provide environment values required by the app running with native bridge according to the
@@ -169,6 +224,88 @@
   //     runtime.
   //     Otherwise, a pointer to the signal handler.
   NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
+
+  // Added callbacks in version 3.
+
+  // Decrements the reference count on the dynamic library handler. If the reference count drops
+  // to zero then the dynamic library is unloaded.
+  //
+  // Parameters:
+  //     handle [IN] the handler of a dynamic library.
+  //
+  // Returns:
+  //   0 on success, and nonzero on error.
+  int (*unloadLibrary)(void* handle);
+
+  // Dump the last failure message of native bridge when fail to load library or search symbol.
+  //
+  // Parameters:
+  //
+  // Returns:
+  //   A string describing the most recent error that occurred when load library
+  //   or lookup symbol via native bridge.
+  char* (*getError)();
+
+  // Check whether library paths are supported by native bridge.
+  //
+  // Parameters:
+  //   library_path [IN] search paths for native libraries (directories separated by ':')
+  // Returns:
+  //   TRUE if libraries within search paths are supported by native bridge, FALSE otherwise
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use isSupported instead in non-namespace scenario.
+  bool (*isPathSupported)(const char* library_path);
+
+  // Initializes anonymous namespace at native bridge side and pass the key of
+  // two namespaces(default and anonymous) owned by dynamic linker to native bridge.
+  //
+  // Parameters:
+  //     public_ns_sonames [IN] the name of "public" libraries.
+  //     anon_ns_library_path [IN] the library search path of (anonymous) namespace.
+  // Returns:
+  //     true if the pass is ok.
+  //     Otherwise, false.
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Should not use in non-namespace scenario.
+  bool (*initNamespace)(const char* public_ns_sonames,
+                        const char* anon_ns_library_path);
+
+
+  // Create a namespace and pass the key of releated namespaces to native bridge.
+  //
+  // Parameters:
+  //     name [IN] the name of the namespace.
+  //     ld_library_path [IN] the first set of library search paths of the namespace.
+  //     default_library_path [IN] the second set of library search path of the namespace.
+  //     type [IN] the attribute of the namespace.
+  //     permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
+  //     parent_ns [IN] the pointer of the parent namespace to be inherited from.
+  // Returns:
+  //     native_bridge_namespace_t* for created namespace or nullptr in the case of error.
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Should not use in non-namespace scenario.
+  native_bridge_namespace_t* (*createNamespace)(const char* name,
+                                                const char* ld_library_path,
+                                                const char* default_library_path,
+                                                uint64_t type,
+                                                const char* permitted_when_isolated_path,
+                                                native_bridge_namespace_t* parent_ns);
+
+  // Load a shared library within a namespace.
+  //
+  // Parameters:
+  //   libpath [IN] path to the shared library
+  //   flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
+  //   ns [IN] the pointer of the namespace in which the library should be loaded.
+  // Returns:
+  //   The opaque handle of the shared library if sucessful, otherwise NULL
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use loadLibrary instead in non-namespace scenario.
+  void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
 };
 
 // Runtime interfaces to native bridge.
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 32b2d27..43e6c0a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -80,6 +80,19 @@
 // Current state of the native bridge.
 static NativeBridgeState state = NativeBridgeState::kNotSetup;
 
+// The version of NativeBridge implementation.
+// Different Nativebridge interface needs the service of different version of
+// Nativebridge implementation.
+// Used by isCompatibleWith() which is introduced in v2.
+enum NativeBridgeImplementationVersion {
+    // first version, not used.
+    DEFAULT_VERSION = 1,
+    // The version which signal semantic is introduced.
+    SIGNAL_VERSION = 2,
+    // The version which namespace semantic is introduced.
+    NAMESPACE_VERSION = 3,
+};
+
 // Whether we had an error at some point.
 static bool had_error = false;
 
@@ -100,8 +113,6 @@
 // and hard code the directory name again here.
 static constexpr const char* kCodeCacheDir = "code_cache";
 
-static constexpr uint32_t kLibNativeBridgeVersion = 2;
-
 // Characters allowed in a native bridge filename. The first character must
 // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
 static bool CharacterAllowed(char c, bool first) {
@@ -152,19 +163,18 @@
   }
 }
 
-static bool VersionCheck(const NativeBridgeCallbacks* cb) {
+// The policy of invoking Nativebridge changed in v3 with/without namespace.
+// Suggest Nativebridge implementation not maintain backward-compatible.
+static bool isCompatibleWith(const uint32_t version) {
   // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
   // version.
-  if (cb == nullptr || cb->version == 0) {
+  if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
     return false;
   }
 
   // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
-  if (cb->version >= 2) {
-    if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) {
-      // TODO: Scan which version is supported, and fall back to handle it.
-      return false;
-    }
+  if (callbacks->version >= SIGNAL_VERSION) {
+    return callbacks->isCompatibleWith(version);
   }
 
   return true;
@@ -205,7 +215,7 @@
         callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
                                                                    kNativeBridgeInterfaceSymbol));
         if (callbacks != nullptr) {
-          if (VersionCheck(callbacks)) {
+          if (isCompatibleWith(NAMESPACE_VERSION)) {
             // Store the handle for later.
             native_bridge_handle = handle;
           } else {
@@ -520,8 +530,91 @@
 }
 
 NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
-  if (NativeBridgeInitialized() && callbacks->version >= 2) {
-    return callbacks->getSignalHandler(signal);
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(SIGNAL_VERSION)) {
+      return callbacks->getSignalHandler(signal);
+    } else {
+      ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
+    }
+  }
+  return nullptr;
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->unloadLibrary(handle);
+    } else {
+      ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
+    }
+  }
+  return -1;
+}
+
+char* NativeBridgeGetError() {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->getError();
+    } else {
+      ALOGE("not compatible with version %d, cannot get message", NAMESPACE_VERSION);
+    }
+  }
+  return nullptr;
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->isPathSupported(path);
+    } else {
+      ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
+    }
+  }
+  return false;
+}
+
+bool NativeBridgeInitNamespace(const char* public_ns_sonames,
+                               const char* anon_ns_library_path) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->initNamespace(public_ns_sonames, anon_ns_library_path);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+    }
+  }
+
+  return false;
+}
+
+native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
+                                                       const char* ld_library_path,
+                                                       const char* default_library_path,
+                                                       uint64_t type,
+                                                       const char* permitted_when_isolated_path,
+                                                       native_bridge_namespace_t* parent_ns) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->createNamespace(name,
+                                        ld_library_path,
+                                        default_library_path,
+                                        type,
+                                        permitted_when_isolated_path,
+                                        parent_ns);
+    } else {
+      ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
+    }
+  }
+
+  return nullptr;
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->loadLibraryExt(libpath, flag, ns);
+    } else {
+      ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
+    }
   }
   return nullptr;
 }
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 5ad1569..4c3e862 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -20,7 +20,13 @@
     PreInitializeNativeBridgeFail2_test.cpp \
     ReSetupNativeBridge_test.cpp \
     UnavailableNativeBridge_test.cpp \
-    ValidNameNativeBridge_test.cpp
+    ValidNameNativeBridge_test.cpp \
+    NativeBridge3UnloadLibrary_test.cpp \
+    NativeBridge3GetError_test.cpp \
+    NativeBridge3IsPathSupported_test.cpp \
+    NativeBridge3InitNamespace_test.cpp \
+    NativeBridge3CreateNamespace_test.cpp \
+    NativeBridge3LoadLibraryExt_test.cpp
 
 
 shared_libraries := \
diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk
index e556f80..2d78be0 100644
--- a/libnativebridge/tests/Android.nativebridge-dummy.mk
+++ b/libnativebridge/tests/Android.nativebridge-dummy.mk
@@ -68,3 +68,41 @@
 LOCAL_MULTILIB := both
 
 include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+# v3.
+
+NATIVE_BRIDGE3_COMMON_SRC_FILES := \
+  DummyNativeBridge3.cpp
+
+# Shared library for target
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge3-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge3-dummy
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
+LOCAL_CLANG := true
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
new file mode 100644
index 0000000..c538fa0
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+#include <signal.h>
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge3_initialize(
+                      const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+                      const char* /* app_code_cache_dir */,
+                      const char* /* isa */) {
+  return true;
+}
+
+extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) {
+  return nullptr;
+}
+
+extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */,
+                                             const char* /* shorty */, uint32_t /* len */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isSupported(const char* /* libpath */) {
+  return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv(
+    const char* /* abi */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) {
+  // For testing, allow 1-3, but disallow 4+.
+  return version <= 3;
+}
+
+static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) {
+  // TODO: Implement something here. We'd either have to have a death test with a log here, or
+  //       we'd have to be able to resume after the faulting instruction...
+  return true;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) {
+  if (signal == SIGSEGV) {
+    return &native_bridge3_dummy_signal_handler;
+  }
+  return nullptr;
+}
+
+extern "C" int native_bridge3_unloadLibrary(void* /* handle */) {
+  return 0;
+}
+
+extern "C" char* native_bridge3_getError() {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
+  return true;
+}
+
+extern "C" bool native_bridge3_initNamespace(const char* /* public_ns_sonames */,
+                                        const char* /* anon_ns_library_path */) {
+  return true;
+}
+
+extern "C" android::native_bridge_namespace_t*
+native_bridge3_createNamespace(const char* /* name */,
+                               const char* /* ld_library_path */,
+                               const char* /* default_library_path */,
+                               uint64_t /* type */,
+                               const char* /* permitted_when_isolated_path */,
+                               android::native_bridge_namespace_t* /* parent_ns */) {
+  return nullptr;
+}
+
+extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
+                                               int /* flag */,
+                                               android::native_bridge_namespace_t* /* ns */) {
+  return nullptr;
+}
+
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+  // v1
+  .version = 3,
+  .initialize = &native_bridge3_initialize,
+  .loadLibrary = &native_bridge3_loadLibrary,
+  .getTrampoline = &native_bridge3_getTrampoline,
+  .isSupported = &native_bridge3_isSupported,
+  .getAppEnv = &native_bridge3_getAppEnv,
+  // v2
+  .isCompatibleWith = &native_bridge3_isCompatibleWith,
+  .getSignalHandler = &native_bridge3_getSignalHandler,
+  // v3
+  .unloadLibrary = &native_bridge3_unloadLibrary,
+  .getError = &native_bridge3_getError,
+  .isPathSupported  = &native_bridge3_isPathSupported,
+  .initNamespace = &native_bridge3_initNamespace,
+  .createNamespace = &native_bridge3_createNamespace,
+  .loadLibraryExt = &native_bridge3_loadLibraryExt
+};
+
diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
new file mode 100644
index 0000000..668d942
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_CreateNamespace) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeCreateNamespace(nullptr, nullptr, nullptr,
+                                                   0, nullptr, nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp
new file mode 100644
index 0000000..0b9f582
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3GetError_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_GetError) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeGetError());
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
new file mode 100644
index 0000000..ae0fd2b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_InitNamespace) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(true, NativeBridgeInitNamespace(nullptr, nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
new file mode 100644
index 0000000..325e40b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_IsPathSupported) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
new file mode 100644
index 0000000..4caeb44
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_LoadLibraryExt) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
new file mode 100644
index 0000000..93a979c
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_UnloadLibrary) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index d489420..0f99816 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -25,6 +25,8 @@
 constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
 constexpr const char* kCodeCache = "./code_cache";
 constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
 
 namespace android {
 
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 30531bc..9d33899 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -8,6 +8,7 @@
         "libnativehelper",
         "liblog",
         "libcutils",
+        "libnativebridge",
     ],
     static_libs: ["libbase"],
     target: {
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 2a6aaec..99ae3a7 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 #include <stdint.h>
+#include <string>
 #if defined(__ANDROID__)
 #include <android/dlext.h>
 #endif
@@ -41,10 +42,12 @@
                         int32_t target_sdk_version,
                         const char* path,
                         jobject class_loader,
-                        jstring library_path);
+                        jstring library_path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg);
 
 __attribute__((visibility("default")))
-bool CloseNativeLibrary(void* handle);
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
 
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 3a6e54d..fb95cb6 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -24,6 +24,7 @@
 #include "android/log.h"
 #include "cutils/properties.h"
 #endif
+#include "nativebridge/native_bridge.h"
 
 #include <algorithm>
 #include <vector>
@@ -34,11 +35,53 @@
 #include <android-base/macros.h>
 #include <android-base/strings.h>
 
+#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
+                                             "%s:%d: %s CHECK '" #predicate "' failed.",\
+                                             __FILE__, __LINE__, __FUNCTION__)
+
 namespace android {
 
 #if defined(__ANDROID__)
-static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot = "/etc/public.libraries.txt";
-static constexpr const char* kPublicNativeLibrariesVendorConfig = "/vendor/etc/public.libraries.txt";
+class NativeLoaderNamespace {
+ public:
+  NativeLoaderNamespace()
+      : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+
+  explicit NativeLoaderNamespace(android_namespace_t* ns)
+      : android_ns_(ns), native_bridge_ns_(nullptr) { }
+
+  explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
+      : android_ns_(nullptr), native_bridge_ns_(ns) { }
+
+  NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
+  NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
+
+  NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
+
+  android_namespace_t* get_android_ns() const {
+    CHECK(native_bridge_ns_ == nullptr);
+    return android_ns_;
+  }
+
+  native_bridge_namespace_t* get_native_bridge_ns() const {
+    CHECK(android_ns_ == nullptr);
+    return native_bridge_ns_;
+  }
+
+  bool is_android_namespace() const {
+    return native_bridge_ns_ == nullptr;
+  }
+
+ private:
+  // Only one of them can be not null
+  android_namespace_t* android_ns_;
+  native_bridge_namespace_t* native_bridge_ns_;
+};
+
+static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
+                                  "/etc/public.libraries.txt";
+static constexpr const char* kPublicNativeLibrariesVendorConfig =
+                                  "/vendor/etc/public.libraries.txt";
 
 // (http://b/27588281) This is a workaround for apps using custom classloaders and calling
 // System.load() with an absolute path which is outside of the classloader library search path.
@@ -55,11 +98,13 @@
  public:
   LibraryNamespaces() : initialized_(false) { }
 
-  android_namespace_t* Create(JNIEnv* env,
-                              jobject class_loader,
-                              bool is_shared,
-                              jstring java_library_path,
-                              jstring java_permitted_path) {
+  bool Create(JNIEnv* env,
+              jobject class_loader,
+              bool is_shared,
+              jstring java_library_path,
+              jstring java_permitted_path,
+              NativeLoaderNamespace* ns,
+              std::string* error_msg) {
     std::string library_path; // empty string by default.
 
     if (java_library_path != nullptr) {
@@ -82,13 +127,13 @@
       }
     }
 
-    if (!initialized_ && !InitPublicNamespace(library_path.c_str())) {
-      return nullptr;
+    if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
+      return false;
     }
 
-    android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
+    bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
 
-    LOG_ALWAYS_FATAL_IF(ns != nullptr,
+    LOG_ALWAYS_FATAL_IF(found,
                         "There is already a namespace associated with this classloader");
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
@@ -96,28 +141,66 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
     }
 
-    android_namespace_t* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+    NativeLoaderNamespace parent_ns;
+    bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
 
-    ns = android_create_namespace("classloader-namespace",
-                                  nullptr,
-                                  library_path.c_str(),
-                                  namespace_type,
-                                  permitted_path.c_str(),
-                                  parent_ns);
+    bool is_native_bridge = false;
 
-    if (ns != nullptr) {
-      namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+    if (found_parent_namespace) {
+      is_native_bridge = !parent_ns.is_android_namespace();
+    } else if (!library_path.empty()) {
+      is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
     }
 
-    return ns;
+    NativeLoaderNamespace native_loader_ns;
+    if (!is_native_bridge) {
+      android_namespace_t* ns = android_create_namespace("classloader-namespace",
+                                                         nullptr,
+                                                         library_path.c_str(),
+                                                         namespace_type,
+                                                         permitted_path.c_str(),
+                                                         parent_ns.get_android_ns());
+      if (ns == nullptr) {
+        *error_msg = dlerror();
+        return false;
+      }
+
+      native_loader_ns = NativeLoaderNamespace(ns);
+    } else {
+      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
+                                                                  nullptr,
+                                                                  library_path.c_str(),
+                                                                  namespace_type,
+                                                                  permitted_path.c_str(),
+                                                                  parent_ns.get_native_bridge_ns());
+      if (ns == nullptr) {
+        *error_msg = NativeBridgeGetError();
+        return false;
+      }
+
+      native_loader_ns = NativeLoaderNamespace(ns);
+    }
+
+    namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
+
+    *ns = native_loader_ns;
+    return true;
   }
 
-  android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
     auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
-                [&](const std::pair<jweak, android_namespace_t*>& value) {
+                [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                   return env->IsSameObject(value.first, class_loader);
                 });
-    return it != namespaces_.end() ? it->second : nullptr;
+    if (it != namespaces_.end()) {
+      if (ns != nullptr) {
+        *ns = it->second;
+      }
+
+      return true;
+    }
+
+    return false;
   }
 
   void Initialize() {
@@ -217,12 +300,25 @@
     return true;
   }
 
-  bool InitPublicNamespace(const char* library_path) {
+  bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
+    // Ask native bride if this apps library path should be handled by it
+    bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
     // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
     // code is one example) unknown to linker in which  case linker uses anonymous
     // namespace. The second argument specifies the search path for the anonymous
     // namespace which is the library_path of the classloader.
-    initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
+    if (!is_native_bridge) {
+      initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
+      if (!initialized_) {
+        *error_msg = dlerror();
+      }
+    } else {
+      initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(), library_path);
+      if (!initialized_) {
+        *error_msg = NativeBridgeGetError();
+      }
+    }
 
     return initialized_;
   }
@@ -236,22 +332,24 @@
     return env->CallObjectMethod(class_loader, get_parent);
   }
 
-  android_namespace_t* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  bool FindParentNamespaceByClassLoader(JNIEnv* env,
+                                        jobject class_loader,
+                                        NativeLoaderNamespace* ns) {
     jobject parent_class_loader = GetParentClassLoader(env, class_loader);
 
     while (parent_class_loader != nullptr) {
-      android_namespace_t* ns = FindNamespaceByClassLoader(env, parent_class_loader);
-      if (ns != nullptr) {
-        return ns;
+      if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
+        return true;
       }
 
       parent_class_loader = GetParentClassLoader(env, parent_class_loader);
     }
-    return nullptr;
+
+    return false;
   }
 
   bool initialized_;
-  std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
+  std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
   std::string public_libraries_;
 
 
@@ -285,13 +383,18 @@
 #if defined(__ANDROID__)
   UNUSED(target_sdk_version);
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  android_namespace_t* ns = g_namespaces->Create(env,
-                                                 class_loader,
-                                                 is_shared,
-                                                 library_path,
-                                                 permitted_path);
-  if (ns == nullptr) {
-    return env->NewStringUTF(dlerror());
+
+  std::string error_msg;
+  NativeLoaderNamespace ns;
+  bool success = g_namespaces->Create(env,
+                                      class_loader,
+                                      is_shared,
+                                      library_path,
+                                      permitted_path,
+                                      &ns,
+                                      &error_msg);
+  if (!success) {
+    return env->NewStringUTF(error_msg.c_str());
   }
 #else
   UNUSED(env, target_sdk_version, class_loader, is_shared,
@@ -304,44 +407,83 @@
                         int32_t target_sdk_version,
                         const char* path,
                         jobject class_loader,
-                        jstring library_path) {
+                        jstring library_path,
+                        bool* needs_native_bridge,
+                        std::string* error_msg) {
 #if defined(__ANDROID__)
   UNUSED(target_sdk_version);
   if (class_loader == nullptr) {
+    *needs_native_bridge = false;
     return dlopen(path, RTLD_NOW);
   }
 
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  NativeLoaderNamespace ns;
 
-  if (ns == nullptr) {
+  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
-    if (ns == nullptr) {
+    if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) {
       return nullptr;
     }
   }
 
-  android_dlextinfo extinfo;
-  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-  extinfo.library_namespace = ns;
+  if (ns.is_android_namespace()) {
+    android_dlextinfo extinfo;
+    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+    extinfo.library_namespace = ns.get_android_ns();
 
-  return android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      *error_msg = dlerror();
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = NativeBridgeGetError();
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
 #else
   UNUSED(env, target_sdk_version, class_loader, library_path);
-  return dlopen(path, RTLD_NOW);
+  *needs_native_bridge = false;
+  void* handle = dlopen(path, RTLD_NOW);
+  if (handle == nullptr) {
+    if (NativeBridgeIsSupported(path)) {
+      *needs_native_bridge = true;
+      handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
+      if (handle == nullptr) {
+        *error_msg = NativeBridgeGetError();
+      }
+    } else {
+      *needs_native_bridge = false;
+      *error_msg = dlerror();
+    }
+  }
+  return handle;
 #endif
 }
 
-bool CloseNativeLibrary(void* handle) {
-  return dlclose(handle) == 0;
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
+    return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
+                                 dlclose(handle);
 }
 
 #if defined(__ANDROID__)
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  // native_bridge_namespaces are not supported for callers of this function.
+  // At the moment this is libwebviewchromium_loader and vulkan.
+  NativeLoaderNamespace ns;
+  if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
+    CHECK(ns.is_android_namespace());
+    return ns.get_android_ns();
+  }
+
+  return nullptr;
 }
 #endif