Merge "radio: fix compatibility for 64 bit processes"
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 369dec9..002d061 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -155,7 +155,24 @@
     }
 #endif
 
+#if !defined(_WIN32)
+    // adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
+    // If set, move it out of the way so that libbase logging doesn't try to parse it.
+    std::string log_tags;
+    char* ANDROID_LOG_TAGS = getenv("ANDROID_LOG_TAGS");
+    if (ANDROID_LOG_TAGS) {
+        log_tags = ANDROID_LOG_TAGS;
+        unsetenv("ANDROID_LOG_TAGS");
+    }
+#endif
+
     android::base::InitLogging(argv, &AdbLogger);
+
+#if !defined(_WIN32)
+    // Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
+    if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
+#endif
+
     setup_trace_mask();
 
     VLOG(ADB) << adb_version();
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index c348dd5..9b59d05 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -237,8 +237,7 @@
         // Uses a default value until device provides the proper name
         dest_file = "bugreport.zip";
     } else {
-        if (!android::base::EndsWith(dest_file, ".zip")) {
-            // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
+        if (!android::base::EndsWithIgnoreCase(dest_file, ".zip")) {
             dest_file += ".zip";
         }
     }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index fe5d280..1f03c1a 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1983,20 +1983,15 @@
 static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
     // The last argument must be the APK file
     const char* file = argv[argc - 1];
-    const char* dot = strrchr(file, '.');
-    bool found_apk = false;
-    struct stat sb;
-    if (dot && !strcasecmp(dot, ".apk")) {
-        if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-            fprintf(stderr, "Invalid APK file: %s\n", file);
-            return EXIT_FAILURE;
-        }
-        found_apk = true;
+    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
+        fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
+        return EXIT_FAILURE;
     }
 
-    if (!found_apk) {
-        fprintf(stderr, "Missing APK file\n");
-        return EXIT_FAILURE;
+    struct stat sb;
+    if (stat(file, &sb) == -1) {
+        fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+        return 1;
     }
 
     int localFd = adb_open(file, O_RDONLY);
@@ -2042,22 +2037,16 @@
 static int install_multiple_app(TransportType transport, const char* serial, int argc,
                                 const char** argv)
 {
-    int i;
-    struct stat sb;
-    uint64_t total_size = 0;
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
     int first_apk = -1;
-    for (i = argc - 1; i >= 0; i--) {
+    uint64_t total_size = 0;
+    for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
-        const char* dot = strrchr(file, '.');
-        if (dot && !strcasecmp(dot, ".apk")) {
-            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-                fprintf(stderr, "Invalid APK file: %s\n", file);
-                return EXIT_FAILURE;
-            }
 
-            total_size += sb.st_size;
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            struct stat sb;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
         } else {
             break;
@@ -2065,7 +2054,7 @@
     }
 
     if (first_apk == -1) {
-        fprintf(stderr, "Missing APK file\n");
+        fprintf(stderr, "No APK file on command line\n");
         return 1;
     }
 
@@ -2077,7 +2066,7 @@
     }
 
     std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
-    for (i = 1; i < first_apk; i++) {
+    for (int i = 1; i < first_apk; i++) {
         cmd += " " + escape_arg(argv[i]);
     }
 
@@ -2109,10 +2098,11 @@
 
     // Valid session, now stream the APKs
     int success = 1;
-    for (i = first_apk; i < argc; i++) {
+    for (int i = first_apk; i < argc; i++) {
         const char* file = argv[i];
+        struct stat sb;
         if (stat(file, &sb) == -1) {
-            fprintf(stderr, "Failed to stat %s\n", file);
+            fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
             success = 0;
             goto finalize_session;
         }
@@ -2212,10 +2202,8 @@
     static const char *const DATA_DEST = "/data/local/tmp/%s";
     static const char *const SD_DEST = "/sdcard/tmp/%s";
     const char* where = DATA_DEST;
-    int i;
-    struct stat sb;
 
-    for (i = 1; i < argc; i++) {
+    for (int i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "-s")) {
             where = SD_DEST;
         }
@@ -2224,22 +2212,15 @@
     // Find last APK argument.
     // All other arguments passed through verbatim.
     int last_apk = -1;
-    for (i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-        const char* dot = strrchr(file, '.');
-        if (dot && !strcasecmp(dot, ".apk")) {
-            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-                fprintf(stderr, "Invalid APK file: %s\n", file);
-                return EXIT_FAILURE;
-            }
-
+    for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
             last_apk = i;
             break;
         }
     }
 
     if (last_apk == -1) {
-        fprintf(stderr, "Missing APK file\n");
+        fprintf(stderr, "No APK file on command line\n");
         return EXIT_FAILURE;
     }
 
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 7fe6763..031b042 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -341,6 +341,17 @@
     return 0;
 }
 
+static int test_access(char *device) {
+    int tries = 25;
+    while (tries--) {
+        if (!access(device, F_OK) || errno != ENOENT) {
+            return 0;
+        }
+        usleep(40 * 1000);
+    }
+    return -1;
+}
+
 static int check_verity_restart(const char *fname)
 {
     char buffer[VERITY_KMSG_BUFSIZE + 1];
@@ -1031,6 +1042,11 @@
     fstab->blk_device = verity_blk_name;
     verity_blk_name = 0;
 
+    // make sure we've set everything up properly
+    if (test_access(fstab->blk_device) < 0) {
+        goto out;
+    }
+
     retval = FS_MGR_SETUP_VERITY_SUCCESS;
 
 out:
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 3f0e047..aa6735d 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -297,12 +297,18 @@
 }
 
 static void healthd_mainloop(void) {
+    int nevents = 0;
     while (1) {
         struct epoll_event events[eventct];
-        int nevents;
         int timeout = awake_poll_interval;
         int mode_timeout;
 
+        /* Don't wait for first timer timeout to run periodic chores */
+        if (!nevents)
+            periodic_chores();
+
+        healthd_mode_ops->heartbeat();
+
         mode_timeout = healthd_mode_ops->preparetowait();
         if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
             timeout = mode_timeout;
@@ -318,11 +324,6 @@
             if (events[n].data.ptr)
                 (*(void (*)(int))events[n].data.ptr)(events[n].events);
         }
-
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
     }
 
     return;
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/init/property_service.cpp b/init/property_service.cpp
index 1e569af..e7176c6 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -141,23 +141,24 @@
     }
 }
 
-static bool is_legal_property_name(const char* name, size_t namelen)
+bool is_legal_property_name(const std::string &name)
 {
-    size_t i;
+    size_t namelen = name.size();
+
     if (namelen >= PROP_NAME_MAX) return false;
     if (namelen < 1) return false;
     if (name[0] == '.') return false;
     if (name[namelen - 1] == '.') return false;
 
-    /* Only allow alphanumeric, plus '.', '-', or '_' */
+    /* Only allow alphanumeric, plus '.', '-', '@', or '_' */
     /* Don't allow ".." to appear in a property name */
-    for (i = 0; i < namelen; i++) {
+    for (size_t i = 0; i < namelen; i++) {
         if (name[i] == '.') {
             // i=0 is guaranteed to never have a dot. See above.
             if (name[i-1] == '.') return false;
             continue;
         }
-        if (name[i] == '_' || name[i] == '-') continue;
+        if (name[i] == '_' || name[i] == '-' || name[i] == '@') continue;
         if (name[i] >= 'a' && name[i] <= 'z') continue;
         if (name[i] >= 'A' && name[i] <= 'Z') continue;
         if (name[i] >= '0' && name[i] <= '9') continue;
@@ -168,10 +169,9 @@
 }
 
 static int property_set_impl(const char* name, const char* value) {
-    size_t namelen = strlen(name);
     size_t valuelen = strlen(value);
 
-    if (!is_legal_property_name(name, namelen)) return -1;
+    if (!is_legal_property_name(name)) return -1;
     if (valuelen >= PROP_VALUE_MAX) return -1;
 
     if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
@@ -188,7 +188,7 @@
 
         __system_property_update(pi, value, valuelen);
     } else {
-        int rc = __system_property_add(name, namelen, value, valuelen);
+        int rc = __system_property_add(name, strlen(name), value, valuelen);
         if (rc < 0) {
             return rc;
         }
@@ -272,7 +272,7 @@
         msg.name[PROP_NAME_MAX-1] = 0;
         msg.value[PROP_VALUE_MAX-1] = 0;
 
-        if (!is_legal_property_name(msg.name, strlen(msg.name))) {
+        if (!is_legal_property_name(msg.name)) {
             LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\"";
             close(s);
             return;
diff --git a/init/property_service.h b/init/property_service.h
index dbaed34..e3a2acb 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -34,6 +34,7 @@
 extern void start_property_service(void);
 std::string property_get(const char* name);
 extern int property_set(const char *name, const char *value);
+extern bool is_legal_property_name(const std::string &name);
 
 
 #endif  /* _INIT_PROPERTY_H */
diff --git a/init/service.cpp b/init/service.cpp
index 685befd..6460e71 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -996,13 +996,5 @@
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
-    if (name.size() > PROP_NAME_MAX - sizeof("init.svc.")) {
-        return false;
-    }
-    for (const auto& c : name) {
-        if (!isalnum(c) && (c != '_') && (c != '-')) {
-            return false;
-        }
-    }
-    return true;
+    return is_legal_property_name("init.svc." + name);
 }
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index c2b68bc..a05a6d8 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -719,7 +719,7 @@
   auto binary_or_err = llvm::object::createBinary(buffer_or_err.get()->getMemBufferRef());
   if (!binary_or_err) {
     BACK_LOGW("failed to create binary for %s in %s: %s", elf_file.c_str(), apk_file.c_str(),
-              binary_or_err.getError().message().c_str());
+              llvm::toString(binary_or_err.takeError()).c_str());
     return nothing;
   }
   return llvm::object::OwningBinary<llvm::object::Binary>(std::move(binary_or_err.get()),
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 132d96f..dda09e0 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,8 +23,6 @@
 #include <sys/_system_properties.h>
 #include <unistd.h>
 
-#include <android/log.h>
-#include <log/logger.h>
 #include <private/android_logger.h>
 
 #include "log_portability.h"
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
 
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 84594c8..c17f3c7 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -144,17 +144,17 @@
  * usb_device_get_product_name and usb_device_get_serial.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_string(struct usb_device *device, int id);
+char* usb_device_get_string(struct usb_device *device, int id, int timeout);
 
 /* Returns the manufacturer name for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_manufacturer_name(struct usb_device *device);
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout);
 
 /* Returns the product name for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_product_name(struct usb_device *device);
+char* usb_device_get_product_name(struct usb_device *device, int timeout);
 
 /* Returns the version number for the USB device.
  */
@@ -163,7 +163,7 @@
 /* Returns the USB serial number for the USB device.
  * Call free() to free the result when you are done with it.
  */
-char* usb_device_get_serial(struct usb_device *device);
+char* usb_device_get_serial(struct usb_device *device, int timeout);
 
 /* Returns true if we have write access to the USB device,
  * and false if we only have access to the USB device configuration.
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 68aca17..9bec6e3 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -449,7 +449,7 @@
     return (struct usb_device_descriptor*)device->desc;
 }
 
-char* usb_device_get_string(struct usb_device *device, int id)
+char* usb_device_get_string(struct usb_device *device, int id, int timeout)
 {
     char string[256];
     __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
@@ -465,7 +465,8 @@
     // read list of supported languages
     result = usb_device_control_transfer(device,
             USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
-            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0);
+            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages),
+            timeout);
     if (result > 0)
         languageCount = (result - 2) / 2;
 
@@ -474,7 +475,8 @@
 
         result = usb_device_control_transfer(device,
                 USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
-                (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0);
+                (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer),
+                timeout);
         if (result > 0) {
             int i;
             // skip first word, and copy the rest to the string, changing shorts to bytes.
@@ -489,16 +491,16 @@
     return NULL;
 }
 
-char* usb_device_get_manufacturer_name(struct usb_device *device)
+char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout)
 {
     struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iManufacturer);
+    return usb_device_get_string(device, desc->iManufacturer, timeout);
 }
 
-char* usb_device_get_product_name(struct usb_device *device)
+char* usb_device_get_product_name(struct usb_device *device, int timeout)
 {
     struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iProduct);
+    return usb_device_get_string(device, desc->iProduct, timeout);
 }
 
 int usb_device_get_version(struct usb_device *device)
@@ -507,10 +509,10 @@
     return desc->bcdUSB;
 }
 
-char* usb_device_get_serial(struct usb_device *device)
+char* usb_device_get_serial(struct usb_device *device, int timeout)
 {
     struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
-    return usb_device_get_string(device, desc->iSerialNumber);
+    return usb_device_get_string(device, desc->iSerialNumber, timeout);
 }
 
 int usb_device_is_writeable(struct usb_device *device)
@@ -711,4 +713,3 @@
     struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
     return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);
 }
-
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 5cab7a8..a009433 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+// for manual checking of stale entries during LogBuffer::erase()
+//#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
 #include <ctype.h>
 #include <errno.h>
@@ -256,6 +258,11 @@
     log_id_for_each(i) {
         doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
     }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+    LogBufferElementCollection::iterator bad = it;
+    int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY)) ?
+            element->getTag() : element->getUid();
+#endif
     it = mLogElements.erase(it);
     if (doSetLast) {
         log_id_for_each(i) {
@@ -269,6 +276,27 @@
             }
         }
     }
+#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
+    log_id_for_each(i) {
+        for(auto b : mLastWorst[i]) {
+            if (bad == b.second) {
+                android::prdebug("stale mLastWorst[%d] key=%d mykey=%d\n",
+                                 i, b.first, key);
+            }
+        }
+        for(auto b : mLastWorstPidOfSystem[i]) {
+            if (bad == b.second) {
+                android::prdebug("stale mLastWorstPidOfSystem[%d] pid=%d\n",
+                                 i, b.first);
+            }
+        }
+        if (mLastSet[i] && (bad == mLast[i])) {
+            android::prdebug("stale mLast[%d]\n", i);
+            mLastSet[i] = false;
+            mLast[i] = mLogElements.begin();
+        }
+    }
+#endif
     if (coalesce) {
         stats.erase(element);
     } else {
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index a939002..f5c60c7 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -36,29 +36,24 @@
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
                                    uid_t uid, pid_t pid, pid_t tid,
                                    const char *msg, unsigned short len) :
-        mLogId(log_id),
         mUid(uid),
         mPid(pid),
         mTid(tid),
-        mMsgLen(len),
         mSequence(sequence.fetch_add(1, memory_order_relaxed)),
-        mRealTime(realtime) {
+        mRealTime(realtime),
+        mMsgLen(len),
+        mLogId(log_id) {
     mMsg = new char[len];
     memcpy(mMsg, msg, len);
+    mTag = (isBinary() && (mMsgLen >= sizeof(uint32_t))) ?
+        le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag) :
+        0;
 }
 
 LogBufferElement::~LogBufferElement() {
     delete [] mMsg;
 }
 
-uint32_t LogBufferElement::getTag() const {
-    if (((mLogId != LOG_ID_EVENTS) && (mLogId != LOG_ID_SECURITY)) ||
-            !mMsg || (mMsgLen < sizeof(uint32_t))) {
-        return 0;
-    }
-    return le32toh(reinterpret_cast<android_event_header_t *>(mMsg)->tag);
-}
-
 // caller must own and free character string
 char *android::tidToName(pid_t tid) {
     char *retval = NULL;
@@ -164,7 +159,7 @@
     size_t hdrLen;
     // LOG_ID_SECURITY not strictly needed since spam filter not activated,
     // but required for accuracy.
-    if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) {
+    if (isBinary()) {
         hdrLen = sizeof(android_log_event_string_t);
     } else {
         hdrLen = 1 + sizeof(tag);
@@ -178,7 +173,7 @@
     }
 
     size_t retval = hdrLen + len;
-    if ((mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY)) {
+    if (isBinary()) {
         android_log_event_string_t *event =
             reinterpret_cast<android_log_event_string_t *>(buffer);
 
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 2c7fd44..fb7fbed 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -36,33 +36,40 @@
 
     friend LogBuffer;
 
-    const log_id_t mLogId;
-    const uid_t mUid;
-    const pid_t mPid;
-    const pid_t mTid;
-    char *mMsg;
-    union {
-        const unsigned short mMsgLen; // mMSg != NULL
-        unsigned short mDropped;      // mMsg == NULL
-    };
+    // sized to match reality of incoming log packets
+    uint32_t mTag; // only valid for isBinary()
+    const uint32_t mUid;
+    const uint32_t mPid;
+    const uint32_t mTid;
     const uint64_t mSequence;
     log_time mRealTime;
+    char *mMsg;
+    union {
+        const uint16_t mMsgLen; // mMSg != NULL
+        uint16_t mDropped;      // mMsg == NULL
+    };
+    const uint8_t mLogId;
+
     static atomic_int_fast64_t sequence;
 
     // assumption: mMsg == NULL
     size_t populateDroppedMessage(char *&buffer,
                                   LogBuffer *parent);
-
 public:
     LogBufferElement(log_id_t log_id, log_time realtime,
                      uid_t uid, pid_t pid, pid_t tid,
                      const char *msg, unsigned short len);
     virtual ~LogBufferElement();
 
-    log_id_t getLogId() const { return mLogId; }
+    bool isBinary(void) const {
+        return (mLogId == LOG_ID_EVENTS) || (mLogId == LOG_ID_SECURITY);
+    }
+
+    log_id_t getLogId() const { return static_cast<log_id_t>(mLogId); }
     uid_t getUid(void) const { return mUid; }
     pid_t getPid(void) const { return mPid; }
     pid_t getTid(void) const { return mTid; }
+    uint32_t getTag() const { return mTag; }
     unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; }
     unsigned short setDropped(unsigned short value) {
         if (mMsg) {
@@ -76,8 +83,6 @@
     static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
     log_time getRealTime(void) const { return mRealTime; }
 
-    uint32_t getTag(void) const;
-
     static const uint64_t FLUSH_ERROR;
     uint64_t flushTo(SocketClient *writer, LogBuffer *parent, bool privileged);
 };