Move libnative{bridge,loader} to art/
This change moves system/core/libnative{bridge,loader} under art/.
Bug: 137364733
Test: m
Change-Id: I9be7333d00fcd3f36cd80520e50a30ea840187ad
diff --git a/libnativebridge/.clang-format b/libnativebridge/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativebridge/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
new file mode 100644
index 0000000..c97845d
--- /dev/null
+++ b/libnativebridge/Android.bp
@@ -0,0 +1,65 @@
+cc_defaults {
+ name: "libnativebridge-defaults",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=protected",
+ ],
+ header_libs: ["libnativebridge-headers"],
+ export_header_lib_headers: ["libnativebridge-headers"],
+}
+
+cc_library_headers {
+ name: "libnativebridge-headers",
+
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libnativebridge",
+ defaults: ["libnativebridge-defaults"],
+ // TODO(oth): remove after moving under art/ (b/137364733)
+ visibility: ["//visibility:public"],
+
+ host_supported: true,
+ srcs: ["native_bridge.cc"],
+ header_libs: [
+ "libbase_headers",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ // TODO(jiyong): remove this line after aosp/885921 lands
+ export_include_dirs: ["include"],
+
+ target: {
+ android: {
+ version_script: "libnativebridge.map.txt",
+ },
+ linux: {
+ version_script: "libnativebridge.map.txt",
+ },
+ },
+
+ stubs: {
+ symbol_file: "libnativebridge.map.txt",
+ versions: ["1"],
+ },
+}
+
+// TODO(b/124250621): eliminate the need for this library
+cc_library {
+ name: "libnativebridge_lazy",
+ defaults: ["libnativebridge-defaults"],
+ // TODO(oth): remove after moving under art/ (b/137364733)
+ visibility: ["//visibility:public"],
+
+ host_supported: false,
+ srcs: ["native_bridge_lazy.cc"],
+ required: ["libnativebridge"],
+}
+
+subdirs = ["tests"]
diff --git a/libnativebridge/CPPLINT.cfg b/libnativebridge/CPPLINT.cfg
new file mode 100644
index 0000000..578047b
--- /dev/null
+++ b/libnativebridge/CPPLINT.cfg
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2019 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.
+#
+
+filter=-build/header_guard
+filter=-whitespace/comments
+filter=-whitespace/parens
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
new file mode 100644
index 0000000..daf87f4
--- /dev/null
+++ b/libnativebridge/OWNERS
@@ -0,0 +1,4 @@
+dimitry@google.com
+eaeltsin@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
new file mode 100644
index 0000000..e9c9500
--- /dev/null
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef NATIVE_BRIDGE_H_
+#define NATIVE_BRIDGE_H_
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "jni.h"
+
+#ifdef __cplusplus
+namespace android {
+extern "C" {
+#endif // __cplusplus
+
+struct NativeBridgeRuntimeCallbacks;
+struct NativeBridgeRuntimeValues;
+
+// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
+// for the return type. The runtime needs to know whether the signal was handled or should be given
+// to the chain.
+typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
+
+// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
+// signals that we do not want to load a native bridge.
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+ const struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
+
+// Quick check whether a native bridge will be needed. This is based off of the instruction set
+// of the process.
+bool NeedsNativeBridge(const char* instruction_set);
+
+// Do the early initialization part of the native bridge, if necessary. This should be done under
+// high privileges.
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set);
+
+// Initialize the native bridge, if any. Should be called by Runtime::DidForkFromZygote. The JNIEnv*
+// will be used to modify the app environment for the bridge.
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set);
+
+// Unload the native bridge, if any. Should be called by Runtime::DidForkFromZygote.
+void UnloadNativeBridge();
+
+// Check whether a native bridge is available (opened or initialized). Requires a prior call to
+// LoadNativeBridge.
+bool NativeBridgeAvailable();
+
+// Check whether a native bridge is available (initialized). Requires a prior call to
+// LoadNativeBridge & InitializeNativeBridge.
+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 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
+// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable()
+// returns true. Returns 0 otherwise.
+uint32_t NativeBridgeGetVersion();
+
+// Returns a signal handler that the bridge would like to be managed. Only valid for a native
+// bridge supporting the version 2 interface. Will return null if the bridge does not support
+// version 2, or if it doesn't have a signal handler it wants to be known.
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal);
+
+// Returns whether we have seen a native bridge error. This could happen because the library
+// was not found, rejected, could not be initialized and so on.
+//
+// This functionality is mainly for testing.
+bool NativeBridgeError();
+
+// Returns whether a given string is acceptable as a native bridge library filename.
+//
+// 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. Returns 0 on success and non-zero on error.
+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.
+const 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 anonymous namespace.
+// NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+//
+// The anonymous namespace is used in the case when a NativeBridge implementation
+// cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+// by dynamic linker; for example calls from the mono-compiled code.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path);
+
+// Create new namespace in which native libraries will be loaded.
+// NativeBridge's peer of android_create_namespace() of dynamic linker.
+//
+// The libraries in the namespace are searched by folowing order:
+// 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+// 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+// 3. deault_library_path (This of this as namespace-local default library path)
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+struct 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, struct native_bridge_namespace_t* parent_ns);
+
+// Creates a link which shares some libraries from one namespace to another.
+// NativeBridge's peer of android_link_namespaces() of dynamic linker.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to,
+ const char* shared_libs_sonames);
+
+// Load a shared library with namespace key that is supported by the native bridge.
+// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
+// extension.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns);
+
+// Returns exported namespace by the name. This is a reflection of
+// android_get_exported_namespace function. Introduced in v5.
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name);
+
+// Native bridge interfaces to runtime.
+struct NativeBridgeCallbacks {
+ // Version number of the interface.
+ uint32_t version;
+
+ // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and
+ // that the native bridge is initialized only once. Thus it is OK to call this interface for an
+ // already initialized native bridge.
+ //
+ // Parameters:
+ // runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
+ // Returns:
+ // true if initialization was successful.
+ bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs,
+ const char* private_dir, const char* instruction_set);
+
+ // Load a shared library that is supported by the native bridge.
+ //
+ // Parameters:
+ // libpath [IN] path to the shared library
+ // 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
+ // sigature as the native method.
+ //
+ // Parameters:
+ // handle [IN] the handle returned from loadLibrary
+ // shorty [IN] short descriptor of native method
+ // len [IN] length of shorty
+ // Returns:
+ // address of trampoline if successful, otherwise NULL
+ void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
+
+ // Check whether native library is valid and is for an ABI that is supported by native bridge.
+ //
+ // Parameters:
+ // 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
+ // instruction set.
+ //
+ // Parameters:
+ // instruction_set [IN] the instruction set of the app
+ // Returns:
+ // NULL if not supported by native bridge.
+ // Otherwise, return all environment values to be set after fork.
+ const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
+
+ // Added callbacks in version 2.
+
+ // Check whether the bridge is compatible with the given version. A bridge may decide not to be
+ // forwards- or backwards-compatible, and libnativebridge will then stop using it.
+ //
+ // Parameters:
+ // bridge_version [IN] the version of libnativebridge.
+ // Returns:
+ // true if the native bridge supports the given version of libnativebridge.
+ bool (*isCompatibleWith)(uint32_t bridge_version);
+
+ // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime
+ // will ensure that the signal handler is being called after the runtime's own handler, but before
+ // all chained handlers. The native bridge should not try to install the handler by itself, as
+ // that will potentially lead to cycles.
+ //
+ // Parameters:
+ // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
+ // supported by the runtime.
+ // Returns:
+ // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
+ // 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.
+ const 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.
+ // NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+ //
+ // The anonymous namespace is used in the case when a NativeBridge implementation
+ // cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+ // by dynamic linker; for example calls from the mono-compiled code.
+ //
+ // 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 (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path);
+
+ // Create new namespace in which native libraries will be loaded.
+ // NativeBridge's peer of android_create_namespace() of dynamic linker.
+ //
+ // 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.
+ struct 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,
+ struct native_bridge_namespace_t* parent_ns);
+
+ // Creates a link which shares some libraries from one namespace to another.
+ // NativeBridge's peer of android_link_namespaces() of dynamic linker.
+ //
+ // Parameters:
+ // from [IN] the namespace where libraries are accessed.
+ // to [IN] the namespace where libraries are loaded.
+ // shared_libs_sonames [IN] the libraries to be shared.
+ //
+ // Returns:
+ // Whether successed or not.
+ //
+ // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+ // Should not use in non-namespace scenario.
+ bool (*linkNamespaces)(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to, const char* shared_libs_sonames);
+
+ // Load a shared library within a namespace.
+ // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
+ // extension.
+ //
+ // 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, struct native_bridge_namespace_t* ns);
+
+ // Get native bridge version of vendor namespace.
+ // The vendor namespace is the namespace used to load vendor public libraries.
+ // With O release this namespace can be different from the default namespace.
+ // For the devices without enable vendor namespaces this function should return null
+ //
+ // Returns:
+ // vendor namespace or null if it was not set up for the device
+ //
+ // Starting with v5 (Android Q) this function is no longer used.
+ // Use getExportedNamespace() below.
+ struct native_bridge_namespace_t* (*getVendorNamespace)();
+
+ // Get native bridge version of exported namespace. Peer of
+ // android_get_exported_namespace(const char*) function.
+ //
+ // Returns:
+ // exported namespace or null if it was not set up for the device
+ struct native_bridge_namespace_t* (*getExportedNamespace)(const char* name);
+};
+
+// Runtime interfaces to native bridge.
+struct NativeBridgeRuntimeCallbacks {
+ // Get shorty of a Java method. The shorty is supposed to be persistent in memory.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // mid [IN] Java methodID.
+ // Returns:
+ // short descriptor for method.
+ const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);
+
+ // Get number of native methods for specified class.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // clazz [IN] Java class object.
+ // Returns:
+ // number of native methods.
+ uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
+
+ // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed
+ // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.
+ //
+ // Parameters:
+ // env [IN] pointer to JNIenv.
+ // clazz [IN] Java class object.
+ // methods [OUT] array of method with the name, shorty, and fnPtr.
+ // method_count [IN] max number of elements in methods.
+ // Returns:
+ // number of method it actually wrote to methods.
+ uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+ uint32_t method_count);
+};
+
+#ifdef __cplusplus
+} // extern "C"
+} // namespace android
+#endif // __cplusplus
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
new file mode 100644
index 0000000..a6841a3
--- /dev/null
+++ b/libnativebridge/libnativebridge.map.txt
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2019 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.
+#
+
+# TODO(b/122710865): Most of these uses come from libnativeloader, which should be bundled
+# together with libnativebridge in the APEX. Once this happens, prune this list.
+LIBNATIVEBRIDGE_1 {
+ global:
+ NativeBridgeIsSupported;
+ NativeBridgeLoadLibrary;
+ NativeBridgeUnloadLibrary;
+ NativeBridgeGetError;
+ NativeBridgeIsPathSupported;
+ NativeBridgeCreateNamespace;
+ NativeBridgeGetExportedNamespace;
+ NativeBridgeLinkNamespaces;
+ NativeBridgeLoadLibraryExt;
+ NativeBridgeInitAnonymousNamespace;
+ NativeBridgeInitialized;
+ NativeBridgeGetTrampoline;
+ LoadNativeBridge;
+ PreInitializeNativeBridge;
+ InitializeNativeBridge;
+ NativeBridgeGetVersion;
+ NativeBridgeGetSignalHandler;
+ UnloadNativeBridge;
+ NativeBridgeAvailable;
+ NeedsNativeBridge;
+ NativeBridgeError;
+ NativeBridgeNameAcceptable;
+ local:
+ *;
+};
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
new file mode 100644
index 0000000..9adba9a
--- /dev/null
+++ b/libnativebridge/native_bridge.cc
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "nativebridge"
+
+#include "nativebridge/native_bridge.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstring>
+
+#include <android-base/macros.h>
+#include <log/log.h>
+
+namespace android {
+
+#ifdef __APPLE__
+template <typename T>
+void UNUSED(const T&) {}
+#endif
+
+extern "C" {
+
+// Environment values required by the apps running with native bridge.
+struct NativeBridgeRuntimeValues {
+ const char* os_arch;
+ const char* cpu_abi;
+ const char* cpu_abi2;
+ const char* *supported_abis;
+ int32_t abi_count;
+};
+
+// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
+static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
+
+enum class NativeBridgeState {
+ kNotSetup, // Initial state.
+ kOpened, // After successful dlopen.
+ kPreInitialized, // After successful pre-initialization.
+ kInitialized, // After successful initialization.
+ kClosed // Closed or errors.
+};
+
+static constexpr const char* kNotSetupString = "kNotSetup";
+static constexpr const char* kOpenedString = "kOpened";
+static constexpr const char* kPreInitializedString = "kPreInitialized";
+static constexpr const char* kInitializedString = "kInitialized";
+static constexpr const char* kClosedString = "kClosed";
+
+static const char* GetNativeBridgeStateString(NativeBridgeState state) {
+ switch (state) {
+ case NativeBridgeState::kNotSetup:
+ return kNotSetupString;
+
+ case NativeBridgeState::kOpened:
+ return kOpenedString;
+
+ case NativeBridgeState::kPreInitialized:
+ return kPreInitializedString;
+
+ case NativeBridgeState::kInitialized:
+ return kInitializedString;
+
+ case NativeBridgeState::kClosed:
+ return kClosedString;
+ }
+}
+
+// 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,
+ // The version with vendor namespaces
+ VENDOR_NAMESPACE_VERSION = 4,
+ // The version with runtime namespaces
+ RUNTIME_NAMESPACE_VERSION = 5,
+};
+
+// Whether we had an error at some point.
+static bool had_error = false;
+
+// Handle of the loaded library.
+static void* native_bridge_handle = nullptr;
+// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized
+// later.
+static const NativeBridgeCallbacks* callbacks = nullptr;
+// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge.
+static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
+
+// The app's code cache directory.
+static char* app_code_cache_dir = nullptr;
+
+// Code cache directory (relative to the application private directory)
+// Ideally we'd like to call into framework to retrieve this name. However that's considered an
+// implementation detail and will require either hacks or consistent refactorings. We compromise
+// and hard code the directory name again here.
+static constexpr const char* kCodeCacheDir = "code_cache";
+
+// 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) {
+ if (first) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+ } else {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
+ (c == '.') || (c == '_') || (c == '-');
+ }
+}
+
+static void ReleaseAppCodeCacheDir() {
+ if (app_code_cache_dir != nullptr) {
+ delete[] app_code_cache_dir;
+ app_code_cache_dir = nullptr;
+ }
+}
+
+// We only allow simple names for the library. It is supposed to be a file in
+// /system/lib or /vendor/lib. Only allow a small range of characters, that is
+// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
+bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
+ const char* ptr = nb_library_filename;
+ if (*ptr == 0) {
+ // Emptry string. Allowed, means no native bridge.
+ return true;
+ } else {
+ // First character must be [a-zA-Z].
+ if (!CharacterAllowed(*ptr, true)) {
+ // Found an invalid fist character, don't accept.
+ ALOGE("Native bridge library %s has been rejected for first character %c",
+ nb_library_filename,
+ *ptr);
+ return false;
+ } else {
+ // For the rest, be more liberal.
+ ptr++;
+ while (*ptr != 0) {
+ if (!CharacterAllowed(*ptr, false)) {
+ // Found an invalid character, don't accept.
+ ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
+ return false;
+ }
+ ptr++;
+ }
+ }
+ return true;
+ }
+}
+
+// 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 (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 (callbacks->version >= SIGNAL_VERSION) {
+ return callbacks->isCompatibleWith(version);
+ }
+
+ return true;
+}
+
+static void CloseNativeBridge(bool with_error) {
+ state = NativeBridgeState::kClosed;
+ had_error |= with_error;
+ ReleaseAppCodeCacheDir();
+}
+
+bool LoadNativeBridge(const char* nb_library_filename,
+ const NativeBridgeRuntimeCallbacks* runtime_cbs) {
+ // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
+ // multi-threaded, so we do not need locking here.
+
+ if (state != NativeBridgeState::kNotSetup) {
+ // Setup has been called before. Ignore this call.
+ if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm.
+ ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
+ GetNativeBridgeStateString(state));
+ }
+ // Note: counts as an error, even though the bridge may be functional.
+ had_error = true;
+ return false;
+ }
+
+ if (nb_library_filename == nullptr || *nb_library_filename == 0) {
+ CloseNativeBridge(false);
+ return false;
+ } else {
+ if (!NativeBridgeNameAcceptable(nb_library_filename)) {
+ CloseNativeBridge(true);
+ } else {
+ // Try to open the library.
+ void* handle = dlopen(nb_library_filename, RTLD_LAZY);
+ if (handle != nullptr) {
+ callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
+ kNativeBridgeInterfaceSymbol));
+ if (callbacks != nullptr) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ // Store the handle for later.
+ native_bridge_handle = handle;
+ } else {
+ callbacks = nullptr;
+ dlclose(handle);
+ ALOGW("Unsupported native bridge interface.");
+ }
+ } else {
+ dlclose(handle);
+ }
+ }
+
+ // Two failure conditions: could not find library (dlopen failed), or could not find native
+ // bridge interface (dlsym failed). Both are an error and close the native bridge.
+ if (callbacks == nullptr) {
+ CloseNativeBridge(true);
+ } else {
+ runtime_callbacks = runtime_cbs;
+ state = NativeBridgeState::kOpened;
+ }
+ }
+ return state == NativeBridgeState::kOpened;
+ }
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+ if (instruction_set == nullptr) {
+ ALOGE("Null instruction set in NeedsNativeBridge.");
+ return false;
+ }
+ return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
+ if (state != NativeBridgeState::kOpened) {
+ ALOGE("Invalid state: native bridge is expected to be opened.");
+ CloseNativeBridge(true);
+ return false;
+ }
+
+ if (app_data_dir_in == nullptr) {
+ ALOGE("Application private directory cannot be null.");
+ CloseNativeBridge(true);
+ return false;
+ }
+
+ // Create the path to the application code cache directory.
+ // The memory will be release after Initialization or when the native bridge is closed.
+ const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/'
+ app_code_cache_dir = new char[len];
+ snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
+
+ // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo.
+ // Failure is not fatal and will keep the native bridge in kPreInitialized.
+ state = NativeBridgeState::kPreInitialized;
+
+#ifndef __APPLE__
+ if (instruction_set == nullptr) {
+ return true;
+ }
+ size_t isa_len = strlen(instruction_set);
+ if (isa_len > 10) {
+ // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for
+ // x86_64 [including the trailing \0]). This is so we don't have to change here if there will
+ // be another instruction set in the future.
+ ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.",
+ instruction_set);
+ return true;
+ }
+
+ // If the file does not exist, the mount command will fail,
+ // so we save the extra file existence check.
+ char cpuinfo_path[1024];
+
+#if defined(__ANDROID__)
+ snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
+#ifdef __LP64__
+ "64"
+#endif // __LP64__
+ "/%s/cpuinfo", instruction_set);
+#else // !__ANDROID__
+ // To be able to test on the host, we hardwire a relative path.
+ snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
+#endif
+
+ // Bind-mount.
+ if (TEMP_FAILURE_RETRY(mount(cpuinfo_path, // Source.
+ "/proc/cpuinfo", // Target.
+ nullptr, // FS type.
+ MS_BIND, // Mount flags: bind mount.
+ nullptr)) == -1) { // "Data."
+ ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno));
+ }
+#else // __APPLE__
+ UNUSED(instruction_set);
+ ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible.");
+#endif
+
+ return true;
+}
+
+static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) {
+ if (value != nullptr) {
+ jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;");
+ if (field_id == nullptr) {
+ env->ExceptionClear();
+ ALOGW("Could not find %s field.", field);
+ return;
+ }
+
+ jstring str = env->NewStringUTF(value);
+ if (str == nullptr) {
+ env->ExceptionClear();
+ ALOGW("Could not create string %s.", value);
+ return;
+ }
+
+ env->SetStaticObjectField(build_class, field_id, str);
+ }
+}
+
+// Set up the environment for the bridged app.
+static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) {
+ // Need a JNIEnv* to do anything.
+ if (env == nullptr) {
+ ALOGW("No JNIEnv* to set up app environment.");
+ return;
+ }
+
+ // Query the bridge for environment values.
+ const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa);
+ if (env_values == nullptr) {
+ return;
+ }
+
+ // Keep the JNIEnv clean.
+ jint success = env->PushLocalFrame(16); // That should be small and large enough.
+ if (success < 0) {
+ // Out of memory, really borked.
+ ALOGW("Out of memory while setting up app environment.");
+ env->ExceptionClear();
+ return;
+ }
+
+ // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge.
+ if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr ||
+ env_values->abi_count >= 0) {
+ jclass bclass_id = env->FindClass("android/os/Build");
+ if (bclass_id != nullptr) {
+ SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi);
+ SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2);
+ } else {
+ // For example in a host test environment.
+ env->ExceptionClear();
+ ALOGW("Could not find Build class.");
+ }
+ }
+
+ if (env_values->os_arch != nullptr) {
+ jclass sclass_id = env->FindClass("java/lang/System");
+ if (sclass_id != nullptr) {
+ jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (set_prop_id != nullptr) {
+ // Init os.arch to the value reqired by the apps running with native bridge.
+ env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"),
+ env->NewStringUTF(env_values->os_arch));
+ } else {
+ env->ExceptionClear();
+ ALOGW("Could not find System#setUnchangeableSystemProperty.");
+ }
+ } else {
+ env->ExceptionClear();
+ ALOGW("Could not find System class.");
+ }
+ }
+
+ // Make it pristine again.
+ env->PopLocalFrame(nullptr);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+ // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that
+ // point we are not multi-threaded, so we do not need locking here.
+
+ if (state == NativeBridgeState::kPreInitialized) {
+ // Check for code cache: if it doesn't exist try to create it.
+ struct stat st;
+ if (stat(app_code_cache_dir, &st) == -1) {
+ if (errno == ENOENT) {
+ if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
+ ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ ReleaseAppCodeCacheDir();
+ }
+ } else {
+ ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ ReleaseAppCodeCacheDir();
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+ ReleaseAppCodeCacheDir();
+ }
+
+ // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
+ if (state == NativeBridgeState::kPreInitialized) {
+ if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) {
+ SetupEnvironment(callbacks, env, instruction_set);
+ state = NativeBridgeState::kInitialized;
+ // We no longer need the code cache path, release the memory.
+ ReleaseAppCodeCacheDir();
+ } else {
+ // Unload the library.
+ dlclose(native_bridge_handle);
+ CloseNativeBridge(true);
+ }
+ }
+ } else {
+ CloseNativeBridge(true);
+ }
+
+ return state == NativeBridgeState::kInitialized;
+}
+
+void UnloadNativeBridge() {
+ // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that
+ // point we are not multi-threaded, so we do not need locking here.
+
+ switch(state) {
+ case NativeBridgeState::kOpened:
+ case NativeBridgeState::kPreInitialized:
+ case NativeBridgeState::kInitialized:
+ // Unload.
+ dlclose(native_bridge_handle);
+ CloseNativeBridge(false);
+ break;
+
+ case NativeBridgeState::kNotSetup:
+ // Not even set up. Error.
+ CloseNativeBridge(true);
+ break;
+
+ case NativeBridgeState::kClosed:
+ // Ignore.
+ break;
+ }
+}
+
+bool NativeBridgeError() {
+ return had_error;
+}
+
+bool NativeBridgeAvailable() {
+ return state == NativeBridgeState::kOpened
+ || state == NativeBridgeState::kPreInitialized
+ || state == NativeBridgeState::kInitialized;
+}
+
+bool NativeBridgeInitialized() {
+ // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after
+ // Runtime::DidForkFromZygote. In that case we do not need a lock.
+ return state == NativeBridgeState::kInitialized;
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->loadLibrary(libpath, flag);
+ }
+ return nullptr;
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
+ uint32_t len) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->getTrampoline(handle, name, shorty, len);
+ }
+ return nullptr;
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+ if (NativeBridgeInitialized()) {
+ return callbacks->isSupported(libpath);
+ }
+ return false;
+}
+
+uint32_t NativeBridgeGetVersion() {
+ if (NativeBridgeAvailable()) {
+ return callbacks->version;
+ }
+ return 0;
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int 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;
+}
+
+const char* NativeBridgeGetError() {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->getError();
+ } else {
+ return "native bridge implementation is not compatible with version 3, cannot get message";
+ }
+ }
+ return "native bridge is not initialized";
+}
+
+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 NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->initAnonymousNamespace(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;
+}
+
+bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+ const char* shared_libs_sonames) {
+ if (NativeBridgeInitialized()) {
+ if (isCompatibleWith(NAMESPACE_VERSION)) {
+ return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+ } else {
+ ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+ }
+ }
+
+ return false;
+}
+
+native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+ if (!NativeBridgeInitialized()) {
+ return nullptr;
+ }
+
+ if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) {
+ return callbacks->getExportedNamespace(name);
+ }
+
+ // sphal is vendor namespace name -> use v4 callback in the case NB callbacks
+ // are not compatible with v5
+ if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) {
+ return callbacks->getVendorNamespace();
+ }
+
+ 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;
+}
+
+} // extern "C"
+
+} // namespace android
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
new file mode 100644
index 0000000..94c8084
--- /dev/null
+++ b/libnativebridge/native_bridge_lazy.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 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 "nativebridge/native_bridge.h"
+#define LOG_TAG "nativebridge"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+ const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
+ static auto f = GET_FUNC_PTR(LoadNativeBridge);
+ return f(native_bridge_library_filename, runtime_callbacks);
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(NeedsNativeBridge);
+ return f(instruction_set);
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
+ return f(app_data_dir, instruction_set);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(InitializeNativeBridge);
+ return f(env, instruction_set);
+}
+
+void UnloadNativeBridge() {
+ static auto f = GET_FUNC_PTR(UnloadNativeBridge);
+ return f();
+}
+
+bool NativeBridgeAvailable() {
+ static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
+ return f();
+}
+
+bool NativeBridgeInitialized() {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
+ return f();
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
+ return f(libpath, flag);
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
+ return f(handle, name, shorty, len);
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
+ return f(libpath);
+}
+
+uint32_t NativeBridgeGetVersion() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
+ return f();
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
+ return f(signal);
+}
+
+bool NativeBridgeError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeError);
+ return f();
+}
+
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
+ static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
+ return f(native_bridge_library_filename);
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+ static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
+ return f(handle);
+}
+
+const char* NativeBridgeGetError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetError);
+ return f();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
+ return f(path);
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
+ return f(public_ns_sonames, anon_ns_library_path);
+}
+
+struct 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, struct native_bridge_namespace_t* parent_ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
+ return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
+ parent_ns);
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to,
+ const char* shared_libs_sonames) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
+ return f(from, to, shared_libs_sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
+ return f(libpath, flag, ns);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
new file mode 100644
index 0000000..2bb8467
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_defaults {
+ name: "libnativebridge-dummy-defaults",
+
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ header_libs: ["libnativebridge-headers"],
+ cppflags: ["-fvisibility=protected"],
+}
+
+cc_library_shared {
+ name: "libnativebridge-dummy",
+ srcs: ["DummyNativeBridge.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge2-dummy",
+ srcs: ["DummyNativeBridge2.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+ name: "libnativebridge3-dummy",
+ srcs: ["DummyNativeBridge3.cpp"],
+ defaults: ["libnativebridge-dummy-defaults"],
+}
+
+// Build the unit tests.
+cc_defaults {
+ name: "libnativebridge-tests-defaults",
+ test_per_src: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "CodeCacheCreate_test.cpp",
+ "CodeCacheExists_test.cpp",
+ "CodeCacheStatFail_test.cpp",
+ "CompleteFlow_test.cpp",
+ "InvalidCharsNativeBridge_test.cpp",
+ "NativeBridge2Signal_test.cpp",
+ "NativeBridgeVersion_test.cpp",
+ "NeedsNativeBridge_test.cpp",
+ "PreInitializeNativeBridge_test.cpp",
+ "PreInitializeNativeBridgeFail1_test.cpp",
+ "PreInitializeNativeBridgeFail2_test.cpp",
+ "ReSetupNativeBridge_test.cpp",
+ "UnavailableNativeBridge_test.cpp",
+ "ValidNameNativeBridge_test.cpp",
+ "NativeBridge3UnloadLibrary_test.cpp",
+ "NativeBridge3GetError_test.cpp",
+ "NativeBridge3IsPathSupported_test.cpp",
+ "NativeBridge3InitAnonymousNamespace_test.cpp",
+ "NativeBridge3CreateNamespace_test.cpp",
+ "NativeBridge3LoadLibraryExt_test.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libnativebridge-dummy",
+ ],
+ header_libs: ["libbase_headers"],
+}
+
+cc_test {
+ name: "libnativebridge-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ host_supported: true,
+ shared_libs: ["libnativebridge"],
+}
+
+cc_test {
+ name: "libnativebridge-lazy-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ shared_libs: ["libnativebridge_lazy"],
+}
+
+// Build the test for the C API.
+cc_test {
+ name: "libnativebridge-api-tests",
+ host_supported: true,
+ test_per_src: true,
+ srcs: [
+ "NativeBridgeApi.c",
+ ],
+ header_libs: ["libnativebridge-headers"],
+}
diff --git a/libnativebridge/tests/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp
new file mode 100644
index 0000000..58270c4
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheCreate_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge initialization creates the code_cache if it doesn't
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheCreate) {
+ // Make sure that code_cache does not exists
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCache, &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Check that code_cache was created
+ ASSERT_EQ(0, stat(kCodeCache, &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ // Clean up
+ UnloadNativeBridge();
+ ASSERT_EQ(0, rmdir(kCodeCache));
+
+ ASSERT_FALSE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp
new file mode 100644
index 0000000..8ba0158
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheExists_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache already
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheExists) {
+ // Make sure that code_cache does not exists
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCache, &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Create the code_cache
+ ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH));
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Check that the code cache is still there
+ ASSERT_EQ(0, stat(kCodeCache, &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ // Clean up
+ UnloadNativeBridge();
+ ASSERT_EQ(0, rmdir(kCodeCache));
+
+ ASSERT_FALSE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644
index 0000000..4ea519e
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+ int fd = creat(kCodeCache, O_RDWR);
+ ASSERT_NE(-1, fd);
+ close(fd);
+
+ struct stat st;
+ ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+ ASSERT_EQ(ENOTDIR, errno);
+
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Clean up
+ UnloadNativeBridge();
+
+ ASSERT_FALSE(NativeBridgeError());
+ unlink(kCodeCache);
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp
new file mode 100644
index 0000000..b033792
--- /dev/null
+++ b/libnativebridge/tests/CompleteFlow_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, CompleteFlow) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ // Basic calls to check that nothing crashes
+ ASSERT_FALSE(NativeBridgeIsSupported(nullptr));
+ ASSERT_EQ(nullptr, NativeBridgeLoadLibrary(nullptr, 0));
+ ASSERT_EQ(nullptr, NativeBridgeGetTrampoline(nullptr, nullptr, nullptr, 0));
+
+ // Unload
+ UnloadNativeBridge();
+
+ ASSERT_FALSE(NativeBridgeAvailable());
+ ASSERT_FALSE(NativeBridgeError());
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/DummyNativeBridge.cpp b/libnativebridge/tests/DummyNativeBridge.cpp
new file mode 100644
index 0000000..b9894f6
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 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"
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge_getTrampoline(void* /* handle */, const char* /* name */,
+ const char* /* shorty */, uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge_isSupported(const char* /* libpath */) {
+ return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+ .version = 1,
+ .initialize = &native_bridge_initialize,
+ .loadLibrary = &native_bridge_loadLibrary,
+ .getTrampoline = &native_bridge_getTrampoline,
+ .isSupported = &native_bridge_isSupported,
+ .getAppEnv = &native_bridge_getAppEnv
+};
diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp
new file mode 100644
index 0000000..6920c74
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge2.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 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_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+ const char* /* app_code_cache_dir */,
+ const char* /* isa */) {
+ return true;
+}
+
+extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) {
+ return nullptr;
+}
+
+extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */,
+ const char* /* shorty */, uint32_t /* len */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge2_isSupported(const char* /* libpath */) {
+ return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv(
+ const char* /* abi */) {
+ return nullptr;
+}
+
+extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) {
+ // For testing, allow 1 and 2, but disallow 3+.
+ return version <= 2;
+}
+
+static bool native_bridge2_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_bridge2_get_signal_handler(int signal) {
+ if (signal == SIGSEGV) {
+ return &native_bridge2_dummy_signal_handler;
+ }
+ return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+ .version = 2,
+ .initialize = &native_bridge2_initialize,
+ .loadLibrary = &native_bridge2_loadLibrary,
+ .getTrampoline = &native_bridge2_getTrampoline,
+ .isSupported = &native_bridge2_isSupported,
+ .getAppEnv = &native_bridge2_getAppEnv,
+ .isCompatibleWith = &native_bridge2_is_compatible_compatible_with,
+ .getSignalHandler = &native_bridge2_get_signal_handler
+};
+
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
new file mode 100644
index 0000000..4ef1c82
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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" const char* native_bridge3_getError() {
+ return nullptr;
+}
+
+extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
+ return true;
+}
+
+extern "C" bool native_bridge3_initAnonymousNamespace(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" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
+ android::native_bridge_namespace_t* /* to */,
+ const char* /* shared_libs_soname */) {
+ return true;
+}
+
+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,
+ .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace,
+ .createNamespace = &native_bridge3_createNamespace,
+ .linkNamespaces = &native_bridge3_linkNamespaces,
+ .loadLibraryExt = &native_bridge3_loadLibraryExt};
diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
new file mode 100644
index 0000000..8f7973d
--- /dev/null
+++ b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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 {
+
+static const char* kTestName = "../librandom$@-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, InvalidChars) {
+ // Do one test actually calling setup.
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge(kTestName, nullptr);
+ // This should lead to an error for invalid characters.
+ EXPECT_EQ(true, NativeBridgeError());
+
+ // Further tests need to use NativeBridgeNameAcceptable, as the error
+ // state can't be changed back.
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("."));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable(".."));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("_"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("-"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so"));
+ EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so"));
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp
new file mode 100644
index 0000000..44e45e3
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <signal.h>
+#include <unistd.h>
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+
+TEST_F(NativeBridgeTest, V2_Signal) {
+ // Init
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+ ASSERT_TRUE(NativeBridgeAvailable());
+ ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+ ASSERT_TRUE(NativeBridgeAvailable());
+
+ ASSERT_EQ(2U, NativeBridgeGetVersion());
+ ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV));
+
+ // Clean-up code_cache
+ ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+} // namespace android
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/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
new file mode 100644
index 0000000..b0d6b09
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_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_InitAnonymousNamespace) {
+ // 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, NativeBridgeInitAnonymousNamespace(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/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c
new file mode 100644
index 0000000..7ab71fe
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeApi.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativebridge/native_bridge.h"
+
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ return 0;
+}
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
new file mode 100644
index 0000000..0f99816
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef NATIVE_BRIDGE_TEST_H_
+#define NATIVE_BRIDGE_TEST_H_
+
+#define LOG_TAG "NativeBridge_test"
+
+#include <nativebridge/native_bridge.h>
+#include <gtest/gtest.h>
+
+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 {
+
+class NativeBridgeTest : public testing::Test {
+};
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
+
diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp
new file mode 100644
index 0000000..d3f9a80
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeVersion_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, Version) {
+ // When a bridge isn't loaded, we expect 0.
+ EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+
+ // After our dummy bridge has been loaded, we expect 1.
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ EXPECT_EQ(NativeBridgeGetVersion(), 1U);
+
+ // Unload
+ UnloadNativeBridge();
+
+ // Version information is gone.
+ EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
new file mode 100644
index 0000000..c8ff743
--- /dev/null
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <android-base/macros.h>
+
+namespace android {
+
+static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
+ "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
+
+TEST_F(NativeBridgeTest, NeedsNativeBridge) {
+ EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
+
+ const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+ for (size_t i = 0; i < kISACount; i++) {
+ EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+ NeedsNativeBridge(kISAs[i]));
+ }
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
new file mode 100644
index 0000000..5a2b0a1
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) {
+ // Needs a valid application directory.
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+ ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+ ASSERT_TRUE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
new file mode 100644
index 0000000..af976b1
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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 <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) {
+ // Needs LoadNativeBridge() first
+ ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+ ASSERT_TRUE(NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
new file mode 100644
index 0000000..cd5a8e2
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 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 <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
+ ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+#if !defined(__APPLE__) // Mac OS does not support bind-mount.
+#if !defined(__ANDROID__) // Cannot write into the hard-wired location.
+ static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
+
+ // Try to create our mount namespace.
+ if (unshare(CLONE_NEWNS) != -1) {
+ // Create a dummy file.
+ FILE* cpuinfo = fopen("./cpuinfo", "w");
+ ASSERT_NE(nullptr, cpuinfo) << strerror(errno);
+ fprintf(cpuinfo, kTestData);
+ fclose(cpuinfo);
+
+ ASSERT_TRUE(PreInitializeNativeBridge("does not matter 1", "short 2"));
+
+ // Read /proc/cpuinfo
+ FILE* proc_cpuinfo = fopen("/proc/cpuinfo", "r");
+ ASSERT_NE(nullptr, proc_cpuinfo) << strerror(errno);
+ char buf[1024];
+ EXPECT_NE(nullptr, fgets(buf, sizeof(buf), proc_cpuinfo)) << "Error reading.";
+ fclose(proc_cpuinfo);
+
+ EXPECT_EQ(0, strcmp(buf, kTestData));
+
+ // Delete the file.
+ ASSERT_EQ(0, unlink("./cpuinfo")) << "Error unlinking temporary file.";
+ // Ending the test will tear down the mount namespace.
+ } else {
+ GTEST_LOG_(WARNING) << "Could not create mount namespace. Are you running this as root?";
+ }
+#endif
+#endif
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
new file mode 100644
index 0000000..944e5d7
--- /dev/null
+++ b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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 {
+
+TEST_F(NativeBridgeTest, ReSetup) {
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge("", nullptr);
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge("", nullptr);
+ // This should lead to an error for trying to re-setup a native bridge.
+ EXPECT_EQ(true, NativeBridgeError());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
new file mode 100644
index 0000000..ad374a5
--- /dev/null
+++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 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 {
+
+TEST_F(NativeBridgeTest, NoNativeBridge) {
+ EXPECT_EQ(false, NativeBridgeAvailable());
+ // Try to initialize. This should fail as we are not set up.
+ EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr));
+ EXPECT_EQ(true, NativeBridgeError());
+ EXPECT_EQ(false, NativeBridgeAvailable());
+}
+
+} // namespace android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
new file mode 100644
index 0000000..690be4a
--- /dev/null
+++ b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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 {
+
+static const char* kTestName = "librandom-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, ValidName) {
+ // Check that the name is acceptable.
+ EXPECT_EQ(true, NativeBridgeNameAcceptable(kTestName));
+
+ // Now check what happens on LoadNativeBridge.
+ EXPECT_EQ(false, NativeBridgeError());
+ LoadNativeBridge(kTestName, nullptr);
+ // This will lead to an error as the library doesn't exist.
+ EXPECT_EQ(true, NativeBridgeError());
+ EXPECT_EQ(false, NativeBridgeAvailable());
+}
+
+} // namespace android
diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativeloader/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
new file mode 100644
index 0000000..2ee9d28
--- /dev/null
+++ b/libnativeloader/Android.bp
@@ -0,0 +1,97 @@
+// Shared library for target
+// ========================================================
+cc_defaults {
+ name: "libnativeloader-defaults",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=hidden",
+ ],
+ header_libs: ["libnativeloader-headers"],
+ export_header_lib_headers: ["libnativeloader-headers"],
+}
+
+cc_library {
+ name: "libnativeloader",
+ defaults: ["libnativeloader-defaults"],
+ // TODO(oth): remove after moving under art/ (b/137364733)
+ visibility: ["//visibility:public"],
+ host_supported: true,
+ srcs: [
+ "native_loader.cpp",
+ ],
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libnativebridge",
+ "libbase",
+ ],
+ target: {
+ android: {
+ srcs: [
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
+ shared_libs: [
+ "libdl_android",
+ ],
+ },
+ },
+ required: [
+ "llndk.libraries.txt",
+ "vndksp.libraries.txt",
+ ],
+ stubs: {
+ symbol_file: "libnativeloader.map.txt",
+ versions: ["1"],
+ },
+}
+
+// TODO(b/124250621) eliminate the need for this library
+cc_library {
+ name: "libnativeloader_lazy",
+ defaults: ["libnativeloader-defaults"],
+ // TODO(oth): remove after moving under art/ (b/137364733)
+ visibility: ["//visibility:public"],
+ host_supported: false,
+ srcs: ["native_loader_lazy.cpp"],
+ required: ["libnativeloader"],
+}
+
+cc_library_headers {
+ name: "libnativeloader-headers",
+ // TODO(oth): remove after moving under art/ (b/137364733)
+ visibility: ["//visibility:public"],
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "libnativeloader_test",
+ srcs: [
+ "native_loader_test.cpp",
+ "native_loader.cpp",
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
+ cflags: ["-DANDROID"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libnativehelper",
+ "libgmock",
+ ],
+ header_libs: [
+ "libnativebridge-headers",
+ "libnativeloader-headers",
+ ],
+ system_shared_libs: [
+ "libc",
+ "libm",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/libnativeloader/CPPLINT.cfg b/libnativeloader/CPPLINT.cfg
new file mode 100644
index 0000000..db98533
--- /dev/null
+++ b/libnativeloader/CPPLINT.cfg
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2019 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.
+#
+
+filter=-build/header_guard
+filter=-readability/check
+filter=-build/namespaces
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
new file mode 100644
index 0000000..f735653
--- /dev/null
+++ b/libnativeloader/OWNERS
@@ -0,0 +1,6 @@
+dimitry@google.com
+jiyong@google.com
+ngeoffray@google.com
+oth@google.com
+mast@google.com
+rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
new file mode 100644
index 0000000..57b9001
--- /dev/null
+++ b/libnativeloader/README.md
@@ -0,0 +1,84 @@
+libnativeloader
+===============================================================================
+
+Overview
+-------------------------------------------------------------------------------
+libnativeloader is responsible for loading native shared libraries (`*.so`
+files) inside the Android Runtime (ART). The native shared libraries could be
+app-provided JNI libraries or public native libraries like `libc.so` provided
+by the platform.
+
+The most typical use case of this library is calling `System.loadLibrary(name)`.
+When the method is called, the ART runtime delegates the call to this library
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (named `classloader-namespace`) that is
+associated with the given classloader, and tries to load the requested library
+from the namespace. The actual searching, loading, and linking of the library
+is performed by the dynamic linker.
+
+The linker namespace is created when an APK is loaded into the process, and is
+associated with the classloader that loaded the APK. The linker namespace is
+configured so that only the JNI libraries embedded in the APK is accessible
+from the namespace, thus preventing an APK from loading JNI libraries of other
+APKs.
+
+The linker namespace is also configured differently depending on other
+characteristics of the APK such as whether or not the APK is bundled with the
+platform. In case of the unbundled, i.e., downloaded or updated APK, only the
+public native libraries that is listed in `/system/etc/public.libraries.txt`
+are available from the platform, whereas in case of the bundled, all libraries
+under `/system/lib` are available (i.e. shared). In case when the unbundled
+app is from `/vendor` or `/product` partition, the app is additionally provided
+with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
+libraries. As the platform is getting modularized with
+[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
+some libraries are no longer provided from platform, but from the APEXes which
+have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
+`libicui18n.so` are from the runtime APEX.
+
+The list of public native libraries is not static. The default set of libraries
+are defined in AOSP, but partners can extend it to include their own libraries.
+Currently, following extensions are available:
+
+- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
+specific to the underlying SoC, e.g. GPU, DSP, etc.
+- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
+`/{system|product}/lib` that a device manufacturer has newly added. The
+libraries should be named as `lib<name>.<companyname>.so` as in
+`libFoo.acme.so`.
+
+Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
+is prohibited for a device manufacturer to expose an AOSP-defined private
+library, e.g. libgui.so, libart.so, etc., to APKs.
+
+Lastly, libnativeloader is responsible for abstracting the two types of the
+dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
+for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
+loading libraries in a translated environment such as ARM-on-x86.
+
+Implementation
+-------------------------------------------------------------------------------
+Implementation wise, libnativeloader consists of four parts:
+
+- `native_loader.cpp`
+- `library_namespaces.cpp`
+- `native_loader_namespace.cpp`
+- `public_libraries.cpp`
+
+`native_loader.cpp` implements the public interface of this library. It is just
+a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
+
+`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
+is a manager-like entity that is responsible for creating and configuring
+linker namespaces and finding an already created linker namespace for a given
+classloader.
+
+`native_loader_namespace.cpp` implements the class `NativeLoaderNamespace` that
+models a linker namespace. Its main job is to abstract the two types of the
+dynamic linker interface so that other parts of this library do not have to know
+the differences of the interfaces.
+
+`public_libraries.cpp` is responsible for reading `*.txt` files for the public
+native libraries from the various partitions. It can be considered as a part of
+`LibraryNamespaces` but is separated from it to hide the details of the parsing
+routines.
diff --git a/libnativeloader/TEST_MAPPING b/libnativeloader/TEST_MAPPING
new file mode 100644
index 0000000..7becb77
--- /dev/null
+++ b/libnativeloader/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "libnativeloader_test"
+ }
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/tests/jni"
+ }
+ ]
+}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
new file mode 100644
index 0000000..8937636
--- /dev/null
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#ifndef __ANDROID_DLEXT_NAMESPACES_H__
+#define __ANDROID_DLEXT_NAMESPACES_H__
+
+#include <android/dlext.h>
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+enum {
+ /* A regular namespace is the namespace with a custom search path that does
+ * not impose any restrictions on the location of native libraries.
+ */
+ ANDROID_NAMESPACE_TYPE_REGULAR = 0,
+
+ /* An isolated namespace requires all the libraries to be on the search path
+ * or under permitted_when_isolated_path. The search path is the union of
+ * ld_library_path and default_library_path.
+ */
+ ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+
+ /* The shared namespace clones the list of libraries of the caller namespace upon creation
+ * which means that they are shared between namespaces - the caller namespace and the new one
+ * will use the same copy of a library if it was loaded prior to android_create_namespace call.
+ *
+ * Note that libraries loaded after the namespace is created will not be shared.
+ *
+ * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
+ * permitted_path from the caller's namespace.
+ */
+ ANDROID_NAMESPACE_TYPE_SHARED = 2,
+
+ /* This flag instructs linker to enable grey-list workaround for the namespace.
+ * See http://b/26394120 for details.
+ */
+ ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
+
+ /* This flag instructs linker to use this namespace as the anonymous
+ * namespace. The anonymous namespace is used in the case when linker cannot
+ * identify the caller of dlopen/dlsym. This happens for the code not loaded
+ * by dynamic linker; for example calls from the mono-compiled code. There can
+ * be only one anonymous namespace in a process. If there already is an
+ * anonymous namespace in the process, using this flag when creating a new
+ * namespace causes an error.
+ */
+ ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS = 0x10000000,
+
+ ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED =
+ ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED,
+};
+
+/*
+ * Creates new linker namespace.
+ * ld_library_path and default_library_path represent the search path
+ * for the libraries in the namespace.
+ *
+ * The libraries in the namespace are searched by folowing order:
+ * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+ * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+ * 3. deault_library_path (This of this as namespace-local default library path)
+ *
+ * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
+ * the libraries to be on the search path or under the permitted_when_isolated_path;
+ * the search_path is ld_library_path:default_library_path. Note that the
+ * permitted_when_isolated_path path is not part of the search_path and
+ * does not affect the search order. It is a way to allow loading libraries from specific
+ * locations when using absolute path.
+ * If a library or any of its dependencies are outside of the permitted_when_isolated_path
+ * and search_path, and it is not part of the public namespace dlopen will fail.
+ */
+extern struct android_namespace_t* android_create_namespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct android_namespace_t* parent);
+
+/*
+ * Creates a link between namespaces. Every link has list of sonames of
+ * shared libraries. These are the libraries which are accessible from
+ * namespace 'from' but loaded within namespace 'to' context.
+ * When to namespace is nullptr this function establishes a link between
+ * 'from' namespace and the default namespace.
+ *
+ * The lookup order of the libraries in namespaces with links is following:
+ * 1. Look inside current namespace using 'this' namespace search path.
+ * 2. Look in linked namespaces
+ * 2.1. Perform soname check - if library soname is not in the list of shared
+ * libraries sonames skip this link, otherwise
+ * 2.2. Search library using linked namespace search path. Note that this
+ * step will not go deeper into linked namespaces for this library but
+ * will do so for DT_NEEDED libraries.
+ */
+extern bool android_link_namespaces(struct android_namespace_t* from,
+ struct android_namespace_t* to,
+ const char* shared_libs_sonames);
+
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
+
+__END_DECLS
+
+#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
new file mode 100644
index 0000000..51fb875
--- /dev/null
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef NATIVE_LOADER_H_
+#define NATIVE_LOADER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "jni.h"
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
+
+#ifdef __cplusplus
+namespace android {
+extern "C" {
+#endif // __cplusplus
+
+// README: the char** error message parameter being passed
+// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage.
+// It's the caller's responsibility to call that method.
+
+__attribute__((visibility("default")))
+void InitializeNativeLoader();
+
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+ JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+ jstring library_path, jstring permitted_path);
+
+__attribute__((visibility("default"))) void* OpenNativeLibrary(
+ JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
+ const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
+
+__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
+ const bool needs_native_bridge,
+ char** error_msg);
+
+__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg);
+
+#if defined(__ANDROID__)
+// Look up linker namespace by class_loader. Returns nullptr if
+// there is no namespace associated with the class_loader.
+// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
+__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader(
+ JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+struct NativeLoaderNamespace;
+__attribute__((visibility("default"))) struct NativeLoaderNamespace*
+FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+// Load library. Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
+// not require access to JNIEnv either.
+__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace(
+ struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+ char** error_msg);
+#endif
+
+__attribute__((visibility("default")))
+void ResetNativeLoader();
+
+#ifdef __cplusplus
+} // extern "C"
+} // namespace android
+#endif // __cplusplus
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
new file mode 100644
index 0000000..40c30bd
--- /dev/null
+++ b/libnativeloader/libnativeloader.map.txt
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2019 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.
+#
+
+# TODO(b/122710865): Prune these uses once the runtime APEX is complete.
+LIBNATIVELOADER_1 {
+ global:
+ OpenNativeLibrary;
+ InitializeNativeLoader;
+ ResetNativeLoader;
+ CloseNativeLibrary;
+ OpenNativeLibraryInNamespace;
+ FindNamespaceByClassLoader;
+ FindNativeLoaderNamespaceByClassLoader;
+ CreateClassLoaderNamespace;
+ NativeLoaderFreeErrorMessage;
+ local:
+ *;
+};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..7246b97
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2019 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 "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "nativeloader/dlext_namespaces.h"
+#include "public_libraries.h"
+#include "utils.h"
+
+using android::base::Error;
+
+namespace android::nativeloader {
+
+namespace {
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kArtNamespaceName = "art";
+constexpr const char* kNeuralNetworksNamespaceName = "neuralnetworks";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
+// (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.
+// This list includes all directories app is allowed to access this way.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+constexpr const char* kVendorLibPath = "/vendor/" LIB;
+constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID get_parent =
+ env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+ return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+ return apk_origin;
+}
+
+} // namespace
+
+void LibraryNamespaces::Initialize() {
+ // Once public namespace is initialized there is no
+ // point in running this code - it will have no effect
+ // on the current list of public libraries.
+ if (initialized_) {
+ return;
+ }
+
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
+ //
+ // TODO(dimitry): this is a bit misleading since we do not know
+ // if the vendor public library is going to be opened from /vendor/lib
+ // we might as well end up loading them from /system/lib or /product/lib
+ // For now we rely on CTS test to catch things like this but
+ // it should probably be addressed in the future.
+ for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
+ LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+ "Error preloading public library %s: %s", soname.c_str(), dlerror());
+ }
+}
+
+Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared,
+ jstring dex_path,
+ jstring java_library_path,
+ jstring java_permitted_path) {
+ std::string library_path; // empty string by default.
+
+ if (java_library_path != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, java_library_path);
+ library_path = library_path_utf_chars.c_str();
+ }
+
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+ // (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.
+ //
+ // This part effectively allows such a classloader to access anything
+ // under /data and /mnt/expand
+ std::string permitted_path = kWhitelistedDirectories;
+
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ if (path.c_str() != nullptr && path.size() > 0) {
+ permitted_path = permitted_path + ":" + path.c_str();
+ }
+ }
+
+ LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr,
+ "There is already a namespace associated with this classloader");
+
+ std::string system_exposed_libraries = default_public_libraries();
+ const char* namespace_name = kClassloaderNamespaceName;
+ bool unbundled_vendor_or_product_app = false;
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ unbundled_vendor_or_product_app = true;
+ // For vendor / product apks, give access to the vendor / product lib even though
+ // they are treated as unbundled; the libs and apks are still bundled
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
+
+ // Also give access to LLNDK libraries since they are available to vendors
+ system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
+ } else {
+ // extended public libraries are NOT available to vendor apks, otherwise it
+ // would be system->vendor violation.
+ if (!extended_public_libraries().empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
+ }
+ }
+
+ // Create the app namespace
+ NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+ // Heuristic: the first classloader with non-empty library_path is assumed to
+ // be the main classloader for app
+ // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its
+ // friends) and then passing it down to here.
+ bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty();
+ // Policy: the namespace for the main classloader is also used as the
+ // anonymous namespace.
+ bool also_used_as_anonymous = is_main_classloader;
+ // Note: this function is executed with g_namespaces_mutex held, thus no
+ // racing here.
+ auto app_ns = NativeLoaderNamespace::Create(
+ namespace_name, library_path, permitted_path, parent_ns, is_shared,
+ target_sdk_version < 24 /* is_greylist_enabled */, also_used_as_anonymous);
+ if (!app_ns) {
+ return app_ns.error();
+ }
+ // ... and link to other namespaces to allow access to some public libraries
+ bool is_bridged = app_ns->IsBridged();
+
+ auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
+ if (!platform_ns) {
+ return platform_ns.error();
+ }
+
+ auto linked = app_ns->Link(*platform_ns, system_exposed_libraries);
+ if (!linked) {
+ return linked.error();
+ }
+
+ auto art_ns = NativeLoaderNamespace::GetExportedNamespace(kArtNamespaceName, is_bridged);
+ // ART APEX does not exist on host, and under certain build conditions.
+ if (art_ns) {
+ linked = app_ns->Link(*art_ns, art_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+
+ // Give access to NNAPI libraries (apex-updated LLNDK library).
+ auto nnapi_ns =
+ NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged);
+ if (nnapi_ns) {
+ linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+
+ // Give access to VNDK-SP libraries from the 'vndk' namespace.
+ if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
+ auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
+ if (vndk_ns) {
+ linked = app_ns->Link(*vndk_ns, vndksp_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+ }
+
+ if (!vendor_public_libraries().empty()) {
+ auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
+ // when vendor_ns is not configured, link to the platform namespace
+ auto target_ns = vendor_ns ? vendor_ns : platform_ns;
+ if (target_ns) {
+ linked = app_ns->Link(*target_ns, vendor_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+ }
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
+ if (is_main_classloader) {
+ app_main_namespace_ = &(*app_ns);
+ }
+
+ return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ if (it != namespaces_.end()) {
+ return &it->second;
+ }
+
+ return nullptr;
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+ while (parent_class_loader != nullptr) {
+ NativeLoaderNamespace* ns;
+ if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+ return ns;
+ }
+
+ parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+ }
+
+ return nullptr;
+}
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..7b3efff
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#pragma once
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include <android-base/result.h>
+#include <jni.h>
+
+namespace android::nativeloader {
+
+using android::base::Result;
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {}
+
+ LibraryNamespaces(LibraryNamespaces&&) = default;
+ LibraryNamespaces(const LibraryNamespaces&) = delete;
+ LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+ void Initialize();
+ void Reset() {
+ namespaces_.clear();
+ initialized_ = false;
+ app_main_namespace_ = nullptr;
+ }
+ Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared, jstring dex_path,
+ jstring java_library_path, jstring java_permitted_path);
+ NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+ Result<void> InitPublicNamespace(const char* library_path);
+ NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ bool initialized_;
+ NativeLoaderNamespace* app_main_namespace_;
+ std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+};
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
new file mode 100644
index 0000000..6d3c057
--- /dev/null
+++ b/libnativeloader/native_loader.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "nativeloader/native_loader.h"
+
+#include <dlfcn.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <nativebridge/native_bridge.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#ifdef __ANDROID__
+#include <log/log.h>
+#include "library_namespaces.h"
+#include "nativeloader/dlext_namespaces.h"
+#endif
+
+namespace android {
+
+namespace {
+#if defined(__ANDROID__)
+using android::nativeloader::LibraryNamespaces;
+
+constexpr const char* kApexPath = "/apex/";
+
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+ std::string location = caller_location;
+ // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+ // /apex/com.android...modulename/...
+ //
+ // And we extract from it 'modulename', which is the name of the linker namespace.
+ if (android::base::StartsWith(location, kApexPath)) {
+ size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+ LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+ "Error finding namespace of apex: no slash in path %s", caller_location);
+ size_t dot_index = location.find_last_of('.', slash_index);
+ LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+ "Error finding namespace of apex: no dot in apex name %s", caller_location);
+ std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+ android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+ LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+ "Error finding namespace of apex: no namespace called %s", name.c_str());
+ return boot_namespace;
+ }
+ return nullptr;
+}
+#endif // #if defined(__ANDROID__)
+} // namespace
+
+void InitializeNativeLoader() {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->Initialize();
+#endif
+}
+
+void ResetNativeLoader() {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->Reset();
+#endif
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
+ jstring permitted_path) {
+#if defined(__ANDROID__)
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+ library_path, permitted_path);
+ if (!ns) {
+ return env->NewStringUTF(ns.error().message().c_str());
+ }
+#else
+ UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
+#endif
+ return nullptr;
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, const char* caller_location, jstring library_path,
+ bool* needs_native_bridge, char** error_msg) {
+#if defined(__ANDROID__)
+ UNUSED(target_sdk_version);
+ if (class_loader == nullptr) {
+ *needs_native_bridge = false;
+ if (caller_location != nullptr) {
+ android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
+ if (boot_namespace != nullptr) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = boot_namespace,
+ };
+ void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
+ if (handle == nullptr) {
+ *error_msg = strdup(dlerror());
+ }
+ return handle;
+ }
+ }
+ void* handle = dlopen(path, RTLD_NOW);
+ if (handle == nullptr) {
+ *error_msg = strdup(dlerror());
+ }
+ return handle;
+ }
+
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ NativeLoaderNamespace* ns;
+
+ if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
+ // This is the case where the classloader was not created by ApplicationLoaders
+ // In this case we create an isolated not-shared namespace for it.
+ Result<NativeLoaderNamespace*> isolated_ns =
+ g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr,
+ library_path, nullptr);
+ if (!isolated_ns) {
+ *error_msg = strdup(isolated_ns.error().message().c_str());
+ return nullptr;
+ } else {
+ ns = *isolated_ns;
+ }
+ }
+
+ return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
+#else
+ UNUSED(env, target_sdk_version, class_loader, caller_location);
+
+ // Do some best effort to emulate library-path support. It will not
+ // work for dependencies.
+ //
+ // Note: null has a special meaning and must be preserved.
+ std::string c_library_path; // Empty string by default.
+ if (library_path != nullptr && path != nullptr && path[0] != '/') {
+ ScopedUtfChars library_path_utf_chars(env, library_path);
+ c_library_path = library_path_utf_chars.c_str();
+ }
+
+ std::vector<std::string> library_paths = base::Split(c_library_path, ":");
+
+ for (const std::string& lib_path : library_paths) {
+ *needs_native_bridge = false;
+ const char* path_arg;
+ std::string complete_path;
+ if (path == nullptr) {
+ // Preserve null.
+ path_arg = nullptr;
+ } else {
+ complete_path = lib_path;
+ if (!complete_path.empty()) {
+ complete_path.append("/");
+ }
+ complete_path.append(path);
+ path_arg = complete_path.c_str();
+ }
+ void* handle = dlopen(path_arg, RTLD_NOW);
+ if (handle != nullptr) {
+ return handle;
+ }
+ if (NativeBridgeIsSupported(path_arg)) {
+ *needs_native_bridge = true;
+ handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW);
+ if (handle != nullptr) {
+ return handle;
+ }
+ *error_msg = strdup(NativeBridgeGetError());
+ } else {
+ *error_msg = strdup(dlerror());
+ }
+ }
+ return nullptr;
+#endif
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+ bool success;
+ if (needs_native_bridge) {
+ success = (NativeBridgeUnloadLibrary(handle) == 0);
+ if (!success) {
+ *error_msg = strdup(NativeBridgeGetError());
+ }
+ } else {
+ success = (dlclose(handle) == 0);
+ if (!success) {
+ *error_msg = strdup(dlerror());
+ }
+ }
+
+ return success;
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+ // The error messages get allocated through strdup, so we must call free on them.
+ free(msg);
+}
+
+#if defined(__ANDROID__)
+void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
+ bool* needs_native_bridge, char** error_msg) {
+ auto handle = ns->Load(path);
+ if (!handle && error_msg != nullptr) {
+ *error_msg = strdup(handle.error().message().c_str());
+ }
+ if (needs_native_bridge != nullptr) {
+ *needs_native_bridge = ns->IsBridged();
+ }
+ return handle ? *handle : nullptr;
+}
+
+// native_bridge_namespaces are not supported for callers of this function.
+// This function will return nullptr in the case when application is running
+// on native bridge.
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+ if (ns != nullptr && !ns->IsBridged()) {
+ return ns->ToRawAndroidNamespace();
+ }
+ return nullptr;
+}
+
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
+#endif
+
+}; // namespace android
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
new file mode 100644
index 0000000..2eb1203
--- /dev/null
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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 "nativeloader/native_loader.h"
+#define LOG_TAG "nativeloader"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+void InitializeNativeLoader() {
+ static auto f = GET_FUNC_PTR(InitializeNativeLoader);
+ return f();
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
+ jstring permitted_path) {
+ static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
+ return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
+ permitted_path);
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, const char* caller_location, jstring library_path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibrary);
+ return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
+ needs_native_bridge, error_msg);
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(CloseNativeLibrary);
+ return f(handle, needs_native_bridge, error_msg);
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+ static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
+ return f(msg);
+}
+
+struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
+ return f(ns, path, needs_native_bridge, error_msg);
+}
+
+void ResetNativeLoader() {
+ static auto f = GET_FUNC_PTR(ResetNativeLoader);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
new file mode 100644
index 0000000..a81fddf
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <dlfcn.h>
+
+#include <functional>
+
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+#include "nativeloader/dlext_namespaces.h"
+
+using android::base::Error;
+using android::base::Errorf;
+
+namespace android {
+
+namespace {
+
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+
+std::string GetLinkerError(bool is_bridged) {
+ const char* msg = is_bridged ? NativeBridgeGetError() : dlerror();
+ if (msg == nullptr) {
+ return "no error";
+ }
+ return std::string(msg);
+}
+
+} // namespace
+
+Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
+ bool is_bridged) {
+ if (!is_bridged) {
+ auto raw = android_get_exported_namespace(name.c_str());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ } else {
+ auto raw = NativeBridgeGetExportedNamespace(name.c_str());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ }
+ return Errorf("namespace {} does not exist or exported", name);
+}
+
+// The platform namespace is called "default" for binaries in /system and
+// "platform" for those in the Runtime APEX. Try "platform" first since
+// "default" always exists.
+Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
+ auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
+ if (ns) return ns;
+ ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+ if (ns) return ns;
+
+ // If nothing is found, return NativeLoaderNamespace constructed from nullptr.
+ // nullptr also means default namespace to the linker.
+ if (!is_bridged) {
+ return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr));
+ } else {
+ return NativeLoaderNamespace(kDefaultNamespaceName,
+ static_cast<native_bridge_namespace_t*>(nullptr));
+ }
+}
+
+Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
+ const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled,
+ bool also_used_as_anonymous) {
+ bool is_bridged = false;
+ if (parent != nullptr) {
+ is_bridged = parent->IsBridged();
+ } else if (!search_paths.empty()) {
+ is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
+ }
+
+ // Fall back to the platform namespace if no parent is set.
+ auto platform_ns = GetPlatformNamespace(is_bridged);
+ if (!platform_ns) {
+ return platform_ns.error();
+ }
+ const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns;
+
+ // All namespaces for apps are isolated
+ uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+
+ // The namespace is also used as the anonymous namespace
+ // which is used when the linker fails to determine the caller address
+ if (also_used_as_anonymous) {
+ type |= ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
+ }
+
+ // Bundled apps have access to all system libraries that are currently loaded
+ // in the default namespace
+ if (is_shared) {
+ type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+ if (is_greylist_enabled) {
+ type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+ }
+
+ if (!is_bridged) {
+ android_namespace_t* raw =
+ android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
+ permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ } else {
+ native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
+ name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
+ effective_parent.ToRawNativeBridgeNamespace());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ }
+ return Errorf("failed to create {} namespace name:{}, search_paths:{}, permitted_paths:{}",
+ is_bridged ? "bridged" : "native", name, search_paths, permitted_paths);
+}
+
+Result<void> NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
+ const std::string& shared_libs) const {
+ LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
+ this->name().c_str(), target.name().c_str());
+ if (!IsBridged()) {
+ if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
+ shared_libs.c_str())) {
+ return {};
+ }
+ } else {
+ if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
+ target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) {
+ return {};
+ }
+ }
+ return Error() << GetLinkerError(IsBridged());
+}
+
+Result<void*> NativeLoaderNamespace::Load(const char* lib_name) const {
+ if (!IsBridged()) {
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = this->ToRawAndroidNamespace();
+ void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+ if (handle != nullptr) {
+ return handle;
+ }
+ } else {
+ void* handle =
+ NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
+ if (handle != nullptr) {
+ return handle;
+ }
+ }
+ return Error() << GetLinkerError(IsBridged());
+}
+
+} // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..7200ee7
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#pragma once
+#if defined(__ANDROID__)
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <android/dlext.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+namespace android {
+
+using android::base::Result;
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+ static Result<NativeLoaderNamespace> Create(const std::string& name,
+ const std::string& search_paths,
+ const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent, bool is_shared,
+ bool is_greylist_enabled,
+ bool also_used_as_anonymous);
+
+ NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+ NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+ NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+ android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
+ native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
+
+ std::string name() const { return name_; }
+ bool IsBridged() const { return raw_.index() == 1; }
+
+ Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
+ Result<void*> Load(const char* lib_name) const;
+
+ static Result<NativeLoaderNamespace> GetExportedNamespace(const std::string& name,
+ bool is_bridged);
+ static Result<NativeLoaderNamespace> GetPlatformNamespace(bool is_bridged);
+
+ private:
+ explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+ explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+
+ std::string name_;
+ std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
+};
+
+} // namespace android
+#endif // #if defined(__ANDROID__)
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
new file mode 100644
index 0000000..8bd7386
--- /dev/null
+++ b/libnativeloader/native_loader_test.cpp
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2019 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 <dlfcn.h>
+#include <memory>
+#include <unordered_map>
+
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <jni.h>
+
+#include "native_loader_namespace.h"
+#include "nativeloader/dlext_namespaces.h"
+#include "nativeloader/native_loader.h"
+#include "public_libraries.h"
+
+using namespace ::testing;
+using namespace ::android::nativeloader::internal;
+
+namespace android {
+namespace nativeloader {
+
+// gmock interface that represents interested platform APIs on libdl and libnativebridge
+class Platform {
+ public:
+ virtual ~Platform() {}
+
+ // libdl APIs
+ virtual void* dlopen(const char* filename, int flags) = 0;
+ virtual int dlclose(void* handle) = 0;
+ virtual char* dlerror(void) = 0;
+
+ // These mock_* are the APIs semantically the same across libdl and libnativebridge.
+ // Instead of having two set of mock APIs for the two, define only one set with an additional
+ // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
+ typedef char* mock_namespace_handle;
+ virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
+ const char* search_paths) = 0;
+ virtual mock_namespace_handle mock_create_namespace(
+ bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
+ uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
+ virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
+ mock_namespace_handle to, const char* sonames) = 0;
+ virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
+ virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
+ mock_namespace_handle ns) = 0;
+
+ // libnativebridge APIs for which libdl has no corresponding APIs
+ virtual bool NativeBridgeInitialized() = 0;
+ virtual const char* NativeBridgeGetError() = 0;
+ virtual bool NativeBridgeIsPathSupported(const char*) = 0;
+ virtual bool NativeBridgeIsSupported(const char*) = 0;
+
+ // To mock "ClassLoader Object.getParent()"
+ virtual const char* JniObject_getParent(const char*) = 0;
+};
+
+// The mock does not actually create a namespace object. But simply casts the pointer to the
+// string for the namespace name as the handle to the namespace object.
+#define TO_ANDROID_NAMESPACE(str) \
+ reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
+
+#define TO_BRIDGED_NAMESPACE(str) \
+ reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
+
+#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
+
+// These represents built-in namespaces created by the linker according to ld.config.txt
+static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
+ {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
+ {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
+ {"art", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("art"))},
+ {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
+ {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
+ {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
+};
+
+// The actual gmock object
+class MockPlatform : public Platform {
+ public:
+ explicit MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
+ ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, mock_get_exported_namespace(_, _))
+ .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle {
+ if (namespaces.find(name) != namespaces.end()) {
+ return namespaces[name];
+ }
+ return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
+ }));
+ }
+
+ // Mocking libdl APIs
+ MOCK_METHOD2(dlopen, void*(const char*, int));
+ MOCK_METHOD1(dlclose, int(void*));
+ MOCK_METHOD0(dlerror, char*());
+
+ // Mocking the common APIs
+ MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
+ MOCK_METHOD7(mock_create_namespace,
+ mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
+ const char*, mock_namespace_handle));
+ MOCK_METHOD4(mock_link_namespaces,
+ bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
+ MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
+ MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
+
+ // Mocking libnativebridge APIs
+ MOCK_METHOD0(NativeBridgeInitialized, bool());
+ MOCK_METHOD0(NativeBridgeGetError, const char*());
+ MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
+ MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
+
+ // Mocking "ClassLoader Object.getParent()"
+ MOCK_METHOD1(JniObject_getParent, const char*(const char*));
+
+ private:
+ bool is_bridged_;
+};
+
+static std::unique_ptr<MockPlatform> mock;
+
+// Provide C wrappers for the mock object.
+extern "C" {
+void* dlopen(const char* file, int flag) {
+ return mock->dlopen(file, flag);
+}
+
+int dlclose(void* handle) {
+ return mock->dlclose(handle);
+}
+
+char* dlerror(void) {
+ return mock->dlerror();
+}
+
+bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
+ return mock->mock_init_anonymous_namespace(false, sonames, search_path);
+}
+
+struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ struct android_namespace_t* parent) {
+ return TO_ANDROID_NAMESPACE(
+ mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
+ const char* sonames) {
+ return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+struct android_namespace_t* android_get_exported_namespace(const char* name) {
+ return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
+}
+
+void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
+ return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
+}
+
+// libnativebridge APIs
+bool NativeBridgeIsSupported(const char* libpath) {
+ return mock->NativeBridgeIsSupported(libpath);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+ return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
+}
+
+struct 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, struct native_bridge_namespace_t* parent) {
+ return TO_BRIDGED_NAMESPACE(
+ mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to, const char* sonames) {
+ return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
+}
+
+bool NativeBridgeInitialized() {
+ return mock->NativeBridgeInitialized();
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
+}
+
+const char* NativeBridgeGetError() {
+ return mock->NativeBridgeGetError();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ return mock->NativeBridgeIsPathSupported(path);
+}
+
+} // extern "C"
+
+// A very simple JNI mock.
+// jstring is a pointer to utf8 char array. We don't need utf16 char here.
+// jobject, jclass, and jmethodID are also a pointer to utf8 char array
+// Only a few JNI methods that are actually used in libnativeloader are mocked.
+JNINativeInterface* CreateJNINativeInterface() {
+ JNINativeInterface* inf = new JNINativeInterface();
+ memset(inf, 0, sizeof(JNINativeInterface));
+
+ inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
+ return reinterpret_cast<const char*>(s);
+ };
+
+ inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
+
+ inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
+ return reinterpret_cast<jstring>(const_cast<char*>(bytes));
+ };
+
+ inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
+ return reinterpret_cast<jclass>(const_cast<char*>(name));
+ };
+
+ inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
+ if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
+ // JniObject_getParent can be a valid jobject or nullptr if there is
+ // no parent classloader.
+ const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
+ return reinterpret_cast<jobject>(const_cast<char*>(ret));
+ }
+ return nullptr;
+ };
+
+ inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
+ return reinterpret_cast<jmethodID>(const_cast<char*>(name));
+ };
+
+ inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
+
+ inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
+ return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
+ };
+
+ return inf;
+}
+
+static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
+
+// Custom matcher for comparing namespace handles
+MATCHER_P(NsEq, other, "") {
+ *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
+ return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
+}
+
+/////////////////////////////////////////////////////////////////
+
+// Test fixture
+class NativeLoaderTest : public ::testing::TestWithParam<bool> {
+ protected:
+ bool IsBridged() { return GetParam(); }
+
+ void SetUp() override {
+ mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged());
+
+ env = std::make_unique<JNIEnv>();
+ env->functions = CreateJNINativeInterface();
+ }
+
+ void SetExpectations() {
+ std::vector<std::string> default_public_libs =
+ android::base::Split(preloadable_public_libraries(), ":");
+ for (auto l : default_public_libs) {
+ EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
+ .WillOnce(Return(any_nonnull));
+ }
+ }
+
+ void RunTest() { InitializeNativeLoader(); }
+
+ void TearDown() override {
+ ResetNativeLoader();
+ delete env->functions;
+ mock.reset();
+ }
+
+ std::unique_ptr<JNIEnv> env;
+};
+
+/////////////////////////////////////////////////////////////////
+
+TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
+ SetExpectations();
+ RunTest();
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
+
+/////////////////////////////////////////////////////////////////
+
+class NativeLoaderTest_Create : public NativeLoaderTest {
+ protected:
+ // Test inputs (initialized to the default values). Overriding these
+ // must be done before calling SetExpectations() and RunTest().
+ uint32_t target_sdk_version = 29;
+ std::string class_loader = "my_classloader";
+ bool is_shared = false;
+ std::string dex_path = "/data/app/foo/classes.dex";
+ std::string library_path = "/data/app/foo/lib/arm";
+ std::string permitted_path = "/data/app/foo/lib";
+
+ // expected output (.. for the default test inputs)
+ std::string expected_namespace_name = "classloader-namespace";
+ uint64_t expected_namespace_flags =
+ ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
+ std::string expected_library_path = library_path;
+ std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
+ std::string expected_parent_namespace = "platform";
+ bool expected_link_with_platform_ns = true;
+ bool expected_link_with_art_ns = true;
+ bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
+ bool expected_link_with_vndk_ns = false;
+ bool expected_link_with_default_ns = false;
+ bool expected_link_with_neuralnetworks_ns = true;
+ std::string expected_shared_libs_to_platform_ns = default_public_libraries();
+ std::string expected_shared_libs_to_art_ns = art_public_libraries();
+ std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
+ std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
+ std::string expected_shared_libs_to_default_ns = default_public_libraries();
+ std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
+
+ void SetExpectations() {
+ NativeLoaderTest::SetExpectations();
+
+ ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
+
+ EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber());
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber());
+
+ EXPECT_CALL(*mock, mock_create_namespace(
+ Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(expected_library_path), expected_namespace_flags,
+ StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
+ if (expected_link_with_platform_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
+ StrEq(expected_shared_libs_to_platform_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_art_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("art"),
+ StrEq(expected_shared_libs_to_art_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_sphal_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
+ StrEq(expected_shared_libs_to_sphal_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_vndk_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
+ StrEq(expected_shared_libs_to_vndk_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_default_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
+ StrEq(expected_shared_libs_to_default_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_neuralnetworks_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
+ StrEq(expected_shared_libs_to_neuralnetworks_ns)))
+ .WillOnce(Return(true));
+ }
+ }
+
+ void RunTest() {
+ NativeLoaderTest::RunTest();
+
+ jstring err = CreateClassLoaderNamespace(
+ env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
+ env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
+ env()->NewStringUTF(permitted_path.c_str()));
+
+ // no error
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for this apk
+ EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns =
+ FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for the this apk
+ EXPECT_STREQ(dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+ }
+
+ JNIEnv* env() { return NativeLoaderTest::env.get(); }
+};
+
+TEST_P(NativeLoaderTest_Create, DownloadedApp) {
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
+ dex_path = "/system/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = false;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/vendor/lib";
+ expected_permitted_path = expected_permitted_path + ":/vendor/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+ target_sdk_version = 30;
+
+ expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ target_sdk_version = 30;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
+ expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
+ if (IsBridged()) {
+ // There is no shared lib in translated arch
+ // TODO(jiyong): revisit this
+ return;
+ }
+ // compared to apks, for java shared libs, library_path is empty; java shared
+ // libs don't have their own native libs. They use platform's.
+ library_path = "";
+ expected_library_path = library_path;
+ // no ALSO_USED_AS_ANONYMOUS
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, TwoApks) {
+ SetExpectations();
+ const uint32_t second_app_target_sdk_version = 29;
+ const std::string second_app_class_loader = "second_app_classloader";
+ const bool second_app_is_shared = false;
+ const std::string second_app_dex_path = "/data/app/bar/classes.dex";
+ const std::string second_app_library_path = "/data/app/bar/lib/arm";
+ const std::string second_app_permitted_path = "/data/app/bar/lib";
+ const std::string expected_second_app_permitted_path =
+ std::string("/data:/mnt/expand:") + second_app_permitted_path;
+ const std::string expected_second_app_parent_namespace = "classloader-namespace";
+ // no ALSO_USED_AS_ANONYMOUS
+ const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+
+ // The scenario is that second app is loaded by the first app.
+ // So the first app's classloader (`classloader`) is parent of the second
+ // app's classloader.
+ ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
+ .WillByDefault(Return(class_loader.c_str()));
+
+ // namespace for the second app is created. Its parent is set to the namespace
+ // of the first app.
+ EXPECT_CALL(*mock, mock_create_namespace(
+ Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(second_app_library_path), expected_second_namespace_flags,
+ StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
+ .WillRepeatedly(Return(true));
+
+ RunTest();
+ jstring err = CreateClassLoaderNamespace(
+ env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
+ second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
+ env()->NewStringUTF(second_app_library_path.c_str()),
+ env()->NewStringUTF(second_app_permitted_path.c_str()));
+
+ // success
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
+ env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_STREQ(second_app_dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
+
+TEST(NativeLoaderConfigParser, NamesAndComments) {
+ const char file_content[] = R"(
+######
+
+libA.so
+#libB.so
+
+
+ libC.so
+libD.so
+ #### libE.so
+)";
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithBitness) {
+ const char file_content[] = R"(
+libA.so 32
+libB.so 64
+libC.so
+)";
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
+#else
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
+#endif
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreload) {
+ const char file_content[] = R"(
+libA.so nopreload
+libB.so nopreload
+libC.so
+)";
+
+ const std::vector<std::string> expected_result = {"libC.so"};
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
+ const char file_content[] = R"(
+libA.so nopreload 32
+libB.so 64 nopreload
+libC.so 32
+libD.so 64
+libE.so nopreload
+)";
+
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libD.so"};
+#else
+ const std::vector<std::string> expected_result = {"libC.so"};
+#endif
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, RejectMalformed) {
+ ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
+ ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
+ ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
+}
+
+} // namespace nativeloader
+} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
new file mode 100644
index 0000000..11c3070
--- /dev/null
+++ b/libnativeloader/public_libraries.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "public_libraries.h"
+
+#include <dirent.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+
+#include "utils.h"
+
+namespace android::nativeloader {
+
+using namespace internal;
+using namespace ::std::string_literals;
+using android::base::ErrnoError;
+using android::base::Errorf;
+using android::base::Result;
+
+namespace {
+
+constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
+constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
+constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
+constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
+constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
+constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kArtApexPublicLibraries = {
+ "libicuuc.so",
+ "libicui18n.so",
+};
+
+constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
+
+constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
+
+// TODO(b/130388701): do we need this?
+std::string root_dir() {
+ static const char* android_root_env = getenv("ANDROID_ROOT");
+ return android_root_env != nullptr ? android_root_env : "/system";
+}
+
+bool debuggable() {
+ static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ return debuggable;
+}
+
+std::string vndk_version_str() {
+ static std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+ return "";
+}
+
+// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+// variable to add libraries to the list. This is intended for platform tests only.
+std::string additional_public_libraries() {
+ if (debuggable()) {
+ const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+ return val ? val : "";
+ }
+ return "";
+}
+
+void InsertVndkVersionStr(std::string* file_name) {
+ CHECK(file_name != nullptr);
+ size_t insert_pos = file_name->find_last_of(".");
+ if (insert_pos == std::string::npos) {
+ insert_pos = file_name->length();
+ }
+ file_name->insert(insert_pos, vndk_version_str());
+}
+
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
+
+Result<std::vector<std::string>> ReadConfig(
+ const std::string& configFile,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
+ std::string file_content;
+ if (!base::ReadFileToString(configFile, &file_content)) {
+ return ErrnoError();
+ }
+ Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
+ if (!result) {
+ return Errorf("Cannot parse {}: {}", configFile, result.error().message());
+ }
+ return result;
+}
+
+void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+ if (dir != nullptr) {
+ // Failing to opening the dir is not an error, which can happen in
+ // webview_zygote.
+ while (struct dirent* ent = readdir(dir.get())) {
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+ continue;
+ }
+ const std::string filename(ent->d_name);
+ std::string_view fn = filename;
+ if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
+ android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
+ const std::string company_name(fn);
+ const std::string config_file_path = dirname + "/"s + filename;
+ LOG_ALWAYS_FATAL_IF(
+ company_name.empty(),
+ "Error extracting company name from public native library list file path \"%s\"",
+ config_file_path.c_str());
+
+ auto ret = ReadConfig(
+ config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
+ if (android::base::StartsWith(entry.soname, "lib") &&
+ android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
+ return true;
+ } else {
+ return Errorf("Library name \"{}\" does not end with the company name {}.",
+ entry.soname, company_name);
+ }
+ });
+ if (ret) {
+ sonames->insert(sonames->end(), ret->begin(), ret->end());
+ } else {
+ LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
+ config_file_path.c_str(), ret.error().message().c_str());
+ }
+ }
+ }
+ }
+}
+
+static std::string InitDefaultPublicLibraries(bool for_preload) {
+ std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
+ auto sonames =
+ ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
+ if (for_preload) {
+ return !entry.nopreload;
+ } else {
+ return true;
+ }
+ });
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
+ config_file.c_str(), sonames.error().message().c_str());
+ return "";
+ }
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ auto vec = base::Split(additional_libs, ":");
+ std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
+ }
+
+ // If this is for preloading libs, don't remove the libs from APEXes.
+ if (for_preload) {
+ return android::base::Join(*sonames, ':');
+ }
+
+ // Remove the public libs in the art namespace.
+ // These libs are listed in public.android.txt, but we don't want the rest of android
+ // in default namespace to dlopen the libs.
+ // For example, libicuuc.so is exposed to classloader namespace from art namespace.
+ // Unfortunately, it does not have stable C symbols, and default namespace should only use
+ // stable symbols in libandroidicu.so. http://b/120786417
+ for (const std::string& lib_name : kArtApexPublicLibraries) {
+ std::string path(kArtApexLibPath);
+ path.append("/").append(lib_name);
+
+ struct stat s;
+ // Do nothing if the path in /apex does not exist.
+ // Runtime APEX must be mounted since libnativeloader is in the same APEX
+ if (stat(path.c_str(), &s) != 0) {
+ continue;
+ }
+
+ auto it = std::find(sonames->begin(), sonames->end(), lib_name);
+ if (it != sonames->end()) {
+ sonames->erase(it);
+ }
+ }
+
+ // Remove the public libs in the nnapi namespace.
+ auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary);
+ if (it != sonames->end()) {
+ sonames->erase(it);
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitArtPublicLibraries() {
+ CHECK(sizeof(kArtApexPublicLibraries) > 0);
+ std::string list = android::base::Join(kArtApexPublicLibraries, ":");
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ list = list + ':' + additional_libs;
+ }
+ return list;
+}
+
+static std::string InitVendorPublicLibraries() {
+ // This file is optional, quietly ignore if the file does not exist.
+ auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
+ if (!sonames) {
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+// read /system/etc/public.libraries-<companyname>.txt and
+// /product/etc/public.libraries-<companyname>.txt which contain partner defined
+// system libs that are exposed to apps. The libs in the txt files must be
+// named as lib<name>.<companyname>.so.
+static std::string InitExtendedPublicLibraries() {
+ std::vector<std::string> sonames;
+ ReadExtensionLibraries("/system/etc", &sonames);
+ ReadExtensionLibraries("/product/etc", &sonames);
+ return android::base::Join(sonames, ':');
+}
+
+static std::string InitLlndkLibraries() {
+ std::string config_file = kLlndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ auto sonames = ReadConfig(config_file, always_true);
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitVndkspLibraries() {
+ std::string config_file = kVndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ auto sonames = ReadConfig(config_file, always_true);
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitNeuralNetworksPublicLibraries() {
+ return kNeuralNetworksApexPublicLibrary;
+}
+
+} // namespace
+
+const std::string& preloadable_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
+ return list;
+}
+
+const std::string& default_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
+ return list;
+}
+
+const std::string& art_public_libraries() {
+ static std::string list = InitArtPublicLibraries();
+ return list;
+}
+
+const std::string& vendor_public_libraries() {
+ static std::string list = InitVendorPublicLibraries();
+ return list;
+}
+
+const std::string& extended_public_libraries() {
+ static std::string list = InitExtendedPublicLibraries();
+ return list;
+}
+
+const std::string& neuralnetworks_public_libraries() {
+ static std::string list = InitNeuralNetworksPublicLibraries();
+ return list;
+}
+
+const std::string& llndk_libraries() {
+ static std::string list = InitLlndkLibraries();
+ return list;
+}
+
+const std::string& vndksp_libraries() {
+ static std::string list = InitVndkspLibraries();
+ return list;
+}
+
+namespace internal {
+// Exported for testing
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
+ if (tokens.size() < 1 || tokens.size() > 3) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
+ size_t i = tokens.size();
+ while (i-- > 0) {
+ if (tokens[i] == "nopreload") {
+ entry.nopreload = true;
+ } else if (tokens[i] == "32" || tokens[i] == "64") {
+ if (entry.bitness != ALL) {
+ return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
+ }
+ entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
+ } else {
+ if (i != 0) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ entry.soname = tokens[i];
+ }
+ }
+
+ // skip 32-bit lib on 64-bit process and vice versa
+#if defined(__LP64__)
+ if (entry.bitness == ONLY_32) continue;
+#else
+ if (entry.bitness == ONLY_64) continue;
+#endif
+
+ Result<bool> ret = filter_fn(entry);
+ if (!ret) {
+ return ret.error();
+ }
+ if (*ret) {
+ // filter_fn has returned true.
+ sonames.push_back(entry.soname);
+ }
+ }
+ return sonames;
+}
+
+} // namespace internal
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
new file mode 100644
index 0000000..b892e6f
--- /dev/null
+++ b/libnativeloader/public_libraries.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#pragma once
+
+#include <algorithm>
+#include <string>
+
+#include <android-base/result.h>
+
+namespace android::nativeloader {
+
+using android::base::Result;
+
+// These provide the list of libraries that are available to the namespace for apps.
+// Not all of the libraries are available to apps. Depending on the context,
+// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& preloadable_public_libraries();
+const std::string& default_public_libraries();
+const std::string& art_public_libraries();
+const std::string& vendor_public_libraries();
+const std::string& extended_public_libraries();
+const std::string& neuralnetworks_public_libraries();
+const std::string& llndk_libraries();
+const std::string& vndksp_libraries();
+
+// These are exported for testing
+namespace internal {
+
+enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
+
+struct ConfigEntry {
+ std::string soname;
+ bool nopreload;
+ Bitness bitness;
+};
+
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
+
+} // namespace internal
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
new file mode 100644
index 0000000..4d5c53d
--- /dev/null
+++ b/libnativeloader/test/Android.bp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_library {
+ name: "libfoo.oem1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libbar.oem1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libfoo.oem2",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libbar.oem2",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libfoo.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libbar.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+// Build the test for the C API.
+cc_test {
+ name: "libnativeloader-api-tests",
+ host_supported: true,
+ test_per_src: true,
+ srcs: [
+ "api_test.c",
+ ],
+ header_libs: ["libnativeloader-headers"],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
new file mode 100644
index 0000000..65e7b09
--- /dev/null
+++ b/libnativeloader/test/Android.mk
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2017 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem2.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-product1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-system
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
+include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c
new file mode 100644
index 0000000..e7025fd
--- /dev/null
+++ b/libnativeloader/test/api_test.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativeloader/native_loader.h"
+
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ return 0;
+}
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
new file mode 100644
index 0000000..f9433e2
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem1.txt
@@ -0,0 +1,2 @@
+libfoo.oem1.so
+libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
new file mode 100644
index 0000000..de6bdb0
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem2.txt
@@ -0,0 +1,2 @@
+libfoo.oem2.so
+libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
new file mode 100755
index 0000000..40beb5b
--- /dev/null
+++ b/libnativeloader/test/runtest.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+adb root
+adb remount
+adb sync
+adb shell stop
+adb shell start
+sleep 5 # wait until device reboots
+adb logcat -c;
+adb shell am start -n android.test.app.system/android.test.app.TestActivity
+adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
+adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
new file mode 100644
index 0000000..a7a455d
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.test.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ tryLoadingLib("foo.oem1");
+ tryLoadingLib("bar.oem1");
+ tryLoadingLib("foo.oem2");
+ tryLoadingLib("bar.oem2");
+ tryLoadingLib("foo.product1");
+ tryLoadingLib("bar.product1");
+ }
+
+ private void tryLoadingLib(String name) {
+ try {
+ System.loadLibrary(name);
+ Log.d(getPackageName(), "library " + name + " is successfully loaded");
+ } catch (UnsatisfiedLinkError e) {
+ Log.d(getPackageName(), "failed to load libarary " + name, e);
+ }
+ }
+}
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml
new file mode 100644
index 0000000..c304889
--- /dev/null
+++ b/libnativeloader/test/system/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system">
+
+ <application>
+ <activity android:name="android.test.app.TestActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp
new file mode 100644
index 0000000..b166928
--- /dev/null
+++ b/libnativeloader/test/test.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#define LOG_TAG "oemlib"
+#include <android-base/logging.h>
+
+static __attribute__((constructor)) void test_lib_init() {
+ LOG(DEBUG) << LIBNAME << " loaded";
+}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
new file mode 100644
index 0000000..c4c1a9c
--- /dev/null
+++ b/libnativeloader/test/vendor/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.vendor">
+
+ <application>
+ <activity android:name="android.test.app.TestActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/libnativeloader/utils.h b/libnativeloader/utils.h
new file mode 100644
index 0000000..a1c2be5
--- /dev/null
+++ b/libnativeloader/utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#pragma once
+
+namespace android::nativeloader {
+
+#if defined(__LP64__)
+#define LIB "lib64"
+#else
+#define LIB "lib"
+#endif
+
+} // namespace android::nativeloader