Merge "Upgrade native bridge to version 3 to support namespace"

am: 28c0c0762e

Change-Id: Ic4263e89f59b1ad34f1f8f28b52f4b45a4df351d
This commit is contained in:
Dimitry Ivanov 2016-10-26 20:11:25 +00:00 committed by android-build-merger
commit 996cfc70a0
15 changed files with 845 additions and 68 deletions

View file

@ -62,12 +62,19 @@ bool NativeBridgeAvailable();
bool NativeBridgeInitialized();
// Load a shared library that is supported by the native bridge.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeLoadLibraryExt() instead in namespace scenario.
void* NativeBridgeLoadLibrary(const char* libpath, int flag);
// Get a native bridge trampoline for specified native method.
void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
// True if native library is valid and is for an ABI that is supported by native bridge.
// True if native library paths are valid and is for an ABI that is supported by native bridge.
// The *libpath* must point to a library.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeIsPathSupported() instead in namespace scenario.
bool NativeBridgeIsSupported(const char* libpath);
// Returns the version number of the native bridge. This information is available after a
@ -91,6 +98,48 @@ bool NativeBridgeError();
// This functionality is exposed mainly for testing.
bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
// Decrements the reference count on the dynamic library handler. If the reference count drops
// to zero then the dynamic library is unloaded.
int NativeBridgeUnloadLibrary(void* handle);
// Get last error message of native bridge when fail to load library or search symbol.
// This is reflection of dlerror() for native bridge.
char* NativeBridgeGetError();
struct native_bridge_namespace_t;
// True if native library paths are valid and is for an ABI that is supported by native bridge.
// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing
// libraries of an ABI.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeIsSupported() instead in non-namespace scenario.
bool NativeBridgeIsPathSupported(const char* path);
// Initializes public and anonymous namespace at native bridge side.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
bool NativeBridgeInitNamespace(const char* public_ns_sonames,
const char* anon_ns_library_path);
// Create a namespace and pass the key of related namespaces to native bridge.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
const char* ld_library_path,
const char* default_library_path,
uint64_t type,
const char* permitted_when_isolated_path,
native_bridge_namespace_t* parent_ns);
// Load a shared library with namespace key that is supported by the native bridge.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
// Native bridge interfaces to runtime.
struct NativeBridgeCallbacks {
// Version number of the interface.
@ -114,6 +163,9 @@ struct NativeBridgeCallbacks {
// flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
// Returns:
// The opaque handle of the shared library if sucessful, otherwise NULL
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use loadLibraryExt instead in namespace scenario.
void* (*loadLibrary)(const char* libpath, int flag);
// Get a native bridge trampoline for specified native method. The trampoline has same
@ -133,6 +185,9 @@ struct NativeBridgeCallbacks {
// libpath [IN] path to the shared library
// Returns:
// TRUE if library is supported by native bridge, FALSE otherwise
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use isPathSupported instead in namespace scenario.
bool (*isSupported)(const char* libpath);
// Provide environment values required by the app running with native bridge according to the
@ -169,6 +224,88 @@ struct NativeBridgeCallbacks {
// runtime.
// Otherwise, a pointer to the signal handler.
NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
// Added callbacks in version 3.
// Decrements the reference count on the dynamic library handler. If the reference count drops
// to zero then the dynamic library is unloaded.
//
// Parameters:
// handle [IN] the handler of a dynamic library.
//
// Returns:
// 0 on success, and nonzero on error.
int (*unloadLibrary)(void* handle);
// Dump the last failure message of native bridge when fail to load library or search symbol.
//
// Parameters:
//
// Returns:
// A string describing the most recent error that occurred when load library
// or lookup symbol via native bridge.
char* (*getError)();
// Check whether library paths are supported by native bridge.
//
// Parameters:
// library_path [IN] search paths for native libraries (directories separated by ':')
// Returns:
// TRUE if libraries within search paths are supported by native bridge, FALSE otherwise
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use isSupported instead in non-namespace scenario.
bool (*isPathSupported)(const char* library_path);
// Initializes anonymous namespace at native bridge side and pass the key of
// two namespaces(default and anonymous) owned by dynamic linker to native bridge.
//
// Parameters:
// public_ns_sonames [IN] the name of "public" libraries.
// anon_ns_library_path [IN] the library search path of (anonymous) namespace.
// Returns:
// true if the pass is ok.
// Otherwise, false.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
bool (*initNamespace)(const char* public_ns_sonames,
const char* anon_ns_library_path);
// Create a namespace and pass the key of releated namespaces to native bridge.
//
// Parameters:
// name [IN] the name of the namespace.
// ld_library_path [IN] the first set of library search paths of the namespace.
// default_library_path [IN] the second set of library search path of the namespace.
// type [IN] the attribute of the namespace.
// permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
// parent_ns [IN] the pointer of the parent namespace to be inherited from.
// Returns:
// native_bridge_namespace_t* for created namespace or nullptr in the case of error.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
native_bridge_namespace_t* (*createNamespace)(const char* name,
const char* ld_library_path,
const char* default_library_path,
uint64_t type,
const char* permitted_when_isolated_path,
native_bridge_namespace_t* parent_ns);
// Load a shared library within a namespace.
//
// Parameters:
// libpath [IN] path to the shared library
// flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
// ns [IN] the pointer of the namespace in which the library should be loaded.
// Returns:
// The opaque handle of the shared library if sucessful, otherwise NULL
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use loadLibrary instead in non-namespace scenario.
void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
};
// Runtime interfaces to native bridge.

View file

@ -80,6 +80,19 @@ static const char* GetNativeBridgeStateString(NativeBridgeState state) {
// Current state of the native bridge.
static NativeBridgeState state = NativeBridgeState::kNotSetup;
// The version of NativeBridge implementation.
// Different Nativebridge interface needs the service of different version of
// Nativebridge implementation.
// Used by isCompatibleWith() which is introduced in v2.
enum NativeBridgeImplementationVersion {
// first version, not used.
DEFAULT_VERSION = 1,
// The version which signal semantic is introduced.
SIGNAL_VERSION = 2,
// The version which namespace semantic is introduced.
NAMESPACE_VERSION = 3,
};
// Whether we had an error at some point.
static bool had_error = false;
@ -100,8 +113,6 @@ static char* app_code_cache_dir = nullptr;
// and hard code the directory name again here.
static constexpr const char* kCodeCacheDir = "code_cache";
static constexpr uint32_t kLibNativeBridgeVersion = 2;
// Characters allowed in a native bridge filename. The first character must
// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
static bool CharacterAllowed(char c, bool first) {
@ -152,19 +163,18 @@ bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
}
}
static bool VersionCheck(const NativeBridgeCallbacks* cb) {
// The policy of invoking Nativebridge changed in v3 with/without namespace.
// Suggest Nativebridge implementation not maintain backward-compatible.
static bool isCompatibleWith(const uint32_t version) {
// Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
// version.
if (cb == nullptr || cb->version == 0) {
if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
return false;
}
// If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
if (cb->version >= 2) {
if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) {
// TODO: Scan which version is supported, and fall back to handle it.
return false;
}
if (callbacks->version >= SIGNAL_VERSION) {
return callbacks->isCompatibleWith(version);
}
return true;
@ -205,7 +215,7 @@ bool LoadNativeBridge(const char* nb_library_filename,
callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
kNativeBridgeInterfaceSymbol));
if (callbacks != nullptr) {
if (VersionCheck(callbacks)) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
// Store the handle for later.
native_bridge_handle = handle;
} else {
@ -520,8 +530,91 @@ uint32_t NativeBridgeGetVersion() {
}
NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
if (NativeBridgeInitialized() && callbacks->version >= 2) {
return callbacks->getSignalHandler(signal);
if (NativeBridgeInitialized()) {
if (isCompatibleWith(SIGNAL_VERSION)) {
return callbacks->getSignalHandler(signal);
} else {
ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
}
}
return nullptr;
}
int NativeBridgeUnloadLibrary(void* handle) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->unloadLibrary(handle);
} else {
ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
}
}
return -1;
}
char* NativeBridgeGetError() {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->getError();
} else {
ALOGE("not compatible with version %d, cannot get message", NAMESPACE_VERSION);
}
}
return nullptr;
}
bool NativeBridgeIsPathSupported(const char* path) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->isPathSupported(path);
} else {
ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
}
}
return false;
}
bool NativeBridgeInitNamespace(const char* public_ns_sonames,
const char* anon_ns_library_path) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->initNamespace(public_ns_sonames, anon_ns_library_path);
} else {
ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
}
}
return false;
}
native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
const char* ld_library_path,
const char* default_library_path,
uint64_t type,
const char* permitted_when_isolated_path,
native_bridge_namespace_t* parent_ns) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->createNamespace(name,
ld_library_path,
default_library_path,
type,
permitted_when_isolated_path,
parent_ns);
} else {
ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
}
}
return nullptr;
}
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
if (NativeBridgeInitialized()) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
return callbacks->loadLibraryExt(libpath, flag, ns);
} else {
ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
}
}
return nullptr;
}

View file

@ -20,7 +20,13 @@ test_src_files := \
PreInitializeNativeBridgeFail2_test.cpp \
ReSetupNativeBridge_test.cpp \
UnavailableNativeBridge_test.cpp \
ValidNameNativeBridge_test.cpp
ValidNameNativeBridge_test.cpp \
NativeBridge3UnloadLibrary_test.cpp \
NativeBridge3GetError_test.cpp \
NativeBridge3IsPathSupported_test.cpp \
NativeBridge3InitNamespace_test.cpp \
NativeBridge3CreateNamespace_test.cpp \
NativeBridge3LoadLibraryExt_test.cpp
shared_libraries := \

View file

@ -68,3 +68,41 @@ LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)
# v3.
NATIVE_BRIDGE3_COMMON_SRC_FILES := \
DummyNativeBridge3.cpp
# Shared library for target
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE:= libnativebridge3-dummy
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
include $(BUILD_SHARED_LIBRARY)
# Shared library for host
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE:= libnativebridge3-dummy
LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES)
LOCAL_CLANG := true
LOCAL_CFLAGS += -Werror -Wall
LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
include $(BUILD_HOST_SHARED_LIBRARY)

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// A dummy implementation of the native-bridge interface.
#include "nativebridge/native_bridge.h"
#include <signal.h>
// NativeBridgeCallbacks implementations
extern "C" bool native_bridge3_initialize(
const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
const char* /* app_code_cache_dir */,
const char* /* isa */) {
return true;
}
extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) {
return nullptr;
}
extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */,
const char* /* shorty */, uint32_t /* len */) {
return nullptr;
}
extern "C" bool native_bridge3_isSupported(const char* /* libpath */) {
return false;
}
extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv(
const char* /* abi */) {
return nullptr;
}
extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) {
// For testing, allow 1-3, but disallow 4+.
return version <= 3;
}
static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) {
// TODO: Implement something here. We'd either have to have a death test with a log here, or
// we'd have to be able to resume after the faulting instruction...
return true;
}
extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) {
if (signal == SIGSEGV) {
return &native_bridge3_dummy_signal_handler;
}
return nullptr;
}
extern "C" int native_bridge3_unloadLibrary(void* /* handle */) {
return 0;
}
extern "C" char* native_bridge3_getError() {
return nullptr;
}
extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
return true;
}
extern "C" bool native_bridge3_initNamespace(const char* /* public_ns_sonames */,
const char* /* anon_ns_library_path */) {
return true;
}
extern "C" android::native_bridge_namespace_t*
native_bridge3_createNamespace(const char* /* name */,
const char* /* ld_library_path */,
const char* /* default_library_path */,
uint64_t /* type */,
const char* /* permitted_when_isolated_path */,
android::native_bridge_namespace_t* /* parent_ns */) {
return nullptr;
}
extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
int /* flag */,
android::native_bridge_namespace_t* /* ns */) {
return nullptr;
}
android::NativeBridgeCallbacks NativeBridgeItf {
// v1
.version = 3,
.initialize = &native_bridge3_initialize,
.loadLibrary = &native_bridge3_loadLibrary,
.getTrampoline = &native_bridge3_getTrampoline,
.isSupported = &native_bridge3_isSupported,
.getAppEnv = &native_bridge3_getAppEnv,
// v2
.isCompatibleWith = &native_bridge3_isCompatibleWith,
.getSignalHandler = &native_bridge3_getSignalHandler,
// v3
.unloadLibrary = &native_bridge3_unloadLibrary,
.getError = &native_bridge3_getError,
.isPathSupported = &native_bridge3_isPathSupported,
.initNamespace = &native_bridge3_initNamespace,
.createNamespace = &native_bridge3_createNamespace,
.loadLibraryExt = &native_bridge3_loadLibraryExt
};

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "NativeBridgeTest.h"
namespace android {
constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
TEST_F(NativeBridgeTest, V3_InitNamespace) {
// Init
ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
ASSERT_TRUE(NativeBridgeAvailable());
ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
ASSERT_TRUE(NativeBridgeAvailable());
ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
ASSERT_TRUE(NativeBridgeAvailable());
ASSERT_EQ(3U, NativeBridgeGetVersion());
ASSERT_EQ(true, NativeBridgeInitNamespace(nullptr, nullptr));
// Clean-up code_cache
ASSERT_EQ(0, rmdir(kCodeCache));
}
} // namespace android

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -25,6 +25,8 @@
constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
constexpr const char* kCodeCache = "./code_cache";
constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
namespace android {

View file

@ -8,6 +8,7 @@ cc_library {
"libnativehelper",
"liblog",
"libcutils",
"libnativebridge",
],
static_libs: ["libbase"],
target: {

View file

@ -19,6 +19,7 @@
#include "jni.h"
#include <stdint.h>
#include <string>
#if defined(__ANDROID__)
#include <android/dlext.h>
#endif
@ -41,10 +42,12 @@ void* OpenNativeLibrary(JNIEnv* env,
int32_t target_sdk_version,
const char* path,
jobject class_loader,
jstring library_path);
jstring library_path,
bool* needs_native_bridge,
std::string* error_msg);
__attribute__((visibility("default")))
bool CloseNativeLibrary(void* handle);
bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
#if defined(__ANDROID__)
// Look up linker namespace by class_loader. Returns nullptr if

View file

@ -24,6 +24,7 @@
#include "android/log.h"
#include "cutils/properties.h"
#endif
#include "nativebridge/native_bridge.h"
#include <algorithm>
#include <vector>
@ -34,11 +35,53 @@
#include <android-base/macros.h>
#include <android-base/strings.h>
#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
"%s:%d: %s CHECK '" #predicate "' failed.",\
__FILE__, __LINE__, __FUNCTION__)
namespace android {
#if defined(__ANDROID__)
static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot = "/etc/public.libraries.txt";
static constexpr const char* kPublicNativeLibrariesVendorConfig = "/vendor/etc/public.libraries.txt";
class NativeLoaderNamespace {
public:
NativeLoaderNamespace()
: android_ns_(nullptr), native_bridge_ns_(nullptr) { }
explicit NativeLoaderNamespace(android_namespace_t* ns)
: android_ns_(ns), native_bridge_ns_(nullptr) { }
explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
: android_ns_(nullptr), native_bridge_ns_(ns) { }
NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
android_namespace_t* get_android_ns() const {
CHECK(native_bridge_ns_ == nullptr);
return android_ns_;
}
native_bridge_namespace_t* get_native_bridge_ns() const {
CHECK(android_ns_ == nullptr);
return native_bridge_ns_;
}
bool is_android_namespace() const {
return native_bridge_ns_ == nullptr;
}
private:
// Only one of them can be not null
android_namespace_t* android_ns_;
native_bridge_namespace_t* native_bridge_ns_;
};
static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
"/etc/public.libraries.txt";
static constexpr const char* kPublicNativeLibrariesVendorConfig =
"/vendor/etc/public.libraries.txt";
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
// System.load() with an absolute path which is outside of the classloader library search path.
@ -55,11 +98,13 @@ class LibraryNamespaces {
public:
LibraryNamespaces() : initialized_(false) { }
android_namespace_t* Create(JNIEnv* env,
jobject class_loader,
bool is_shared,
jstring java_library_path,
jstring java_permitted_path) {
bool Create(JNIEnv* env,
jobject class_loader,
bool is_shared,
jstring java_library_path,
jstring java_permitted_path,
NativeLoaderNamespace* ns,
std::string* error_msg) {
std::string library_path; // empty string by default.
if (java_library_path != nullptr) {
@ -82,13 +127,13 @@ class LibraryNamespaces {
}
}
if (!initialized_ && !InitPublicNamespace(library_path.c_str())) {
return nullptr;
if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
return false;
}
android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
bool found = FindNamespaceByClassLoader(env, class_loader, nullptr);
LOG_ALWAYS_FATAL_IF(ns != nullptr,
LOG_ALWAYS_FATAL_IF(found,
"There is already a namespace associated with this classloader");
uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
@ -96,28 +141,66 @@ class LibraryNamespaces {
namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
android_namespace_t* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
NativeLoaderNamespace parent_ns;
bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns);
ns = android_create_namespace("classloader-namespace",
nullptr,
library_path.c_str(),
namespace_type,
permitted_path.c_str(),
parent_ns);
bool is_native_bridge = false;
if (ns != nullptr) {
namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
if (found_parent_namespace) {
is_native_bridge = !parent_ns.is_android_namespace();
} else if (!library_path.empty()) {
is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
}
return ns;
NativeLoaderNamespace native_loader_ns;
if (!is_native_bridge) {
android_namespace_t* ns = android_create_namespace("classloader-namespace",
nullptr,
library_path.c_str(),
namespace_type,
permitted_path.c_str(),
parent_ns.get_android_ns());
if (ns == nullptr) {
*error_msg = dlerror();
return false;
}
native_loader_ns = NativeLoaderNamespace(ns);
} else {
native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
nullptr,
library_path.c_str(),
namespace_type,
permitted_path.c_str(),
parent_ns.get_native_bridge_ns());
if (ns == nullptr) {
*error_msg = NativeBridgeGetError();
return false;
}
native_loader_ns = NativeLoaderNamespace(ns);
}
namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
*ns = native_loader_ns;
return true;
}
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
[&](const std::pair<jweak, android_namespace_t*>& value) {
[&](const std::pair<jweak, NativeLoaderNamespace>& value) {
return env->IsSameObject(value.first, class_loader);
});
return it != namespaces_.end() ? it->second : nullptr;
if (it != namespaces_.end()) {
if (ns != nullptr) {
*ns = it->second;
}
return true;
}
return false;
}
void Initialize() {
@ -217,12 +300,25 @@ class LibraryNamespaces {
return true;
}
bool InitPublicNamespace(const char* library_path) {
bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
// Ask native bride if this apps library path should be handled by it
bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
// (http://b/25844435) - Some apps call dlopen from generated code (mono jited
// code is one example) unknown to linker in which case linker uses anonymous
// namespace. The second argument specifies the search path for the anonymous
// namespace which is the library_path of the classloader.
initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
if (!is_native_bridge) {
initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
if (!initialized_) {
*error_msg = dlerror();
}
} else {
initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(), library_path);
if (!initialized_) {
*error_msg = NativeBridgeGetError();
}
}
return initialized_;
}
@ -236,22 +332,24 @@ class LibraryNamespaces {
return env->CallObjectMethod(class_loader, get_parent);
}
android_namespace_t* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
bool FindParentNamespaceByClassLoader(JNIEnv* env,
jobject class_loader,
NativeLoaderNamespace* ns) {
jobject parent_class_loader = GetParentClassLoader(env, class_loader);
while (parent_class_loader != nullptr) {
android_namespace_t* ns = FindNamespaceByClassLoader(env, parent_class_loader);
if (ns != nullptr) {
return ns;
if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) {
return true;
}
parent_class_loader = GetParentClassLoader(env, parent_class_loader);
}
return nullptr;
return false;
}
bool initialized_;
std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
std::string public_libraries_;
@ -285,13 +383,18 @@ jstring CreateClassLoaderNamespace(JNIEnv* env,
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
android_namespace_t* ns = g_namespaces->Create(env,
class_loader,
is_shared,
library_path,
permitted_path);
if (ns == nullptr) {
return env->NewStringUTF(dlerror());
std::string error_msg;
NativeLoaderNamespace ns;
bool success = g_namespaces->Create(env,
class_loader,
is_shared,
library_path,
permitted_path,
&ns,
&error_msg);
if (!success) {
return env->NewStringUTF(error_msg.c_str());
}
#else
UNUSED(env, target_sdk_version, class_loader, is_shared,
@ -304,44 +407,83 @@ void* OpenNativeLibrary(JNIEnv* env,
int32_t target_sdk_version,
const char* path,
jobject class_loader,
jstring library_path) {
jstring library_path,
bool* needs_native_bridge,
std::string* error_msg) {
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
*needs_native_bridge = false;
return dlopen(path, RTLD_NOW);
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
NativeLoaderNamespace ns;
if (ns == nullptr) {
if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
if (ns == nullptr) {
if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) {
return nullptr;
}
}
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns;
if (ns.is_android_namespace()) {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns.get_android_ns();
return android_dlopen_ext(path, RTLD_NOW, &extinfo);
void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
if (handle == nullptr) {
*error_msg = dlerror();
}
*needs_native_bridge = false;
return handle;
} else {
void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
if (handle == nullptr) {
*error_msg = NativeBridgeGetError();
}
*needs_native_bridge = true;
return handle;
}
#else
UNUSED(env, target_sdk_version, class_loader, library_path);
return dlopen(path, RTLD_NOW);
*needs_native_bridge = false;
void* handle = dlopen(path, RTLD_NOW);
if (handle == nullptr) {
if (NativeBridgeIsSupported(path)) {
*needs_native_bridge = true;
handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
if (handle == nullptr) {
*error_msg = NativeBridgeGetError();
}
} else {
*needs_native_bridge = false;
*error_msg = dlerror();
}
}
return handle;
#endif
}
bool CloseNativeLibrary(void* handle) {
return dlclose(handle) == 0;
bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
dlclose(handle);
}
#if defined(__ANDROID__)
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
// native_bridge_namespaces are not supported for callers of this function.
// At the moment this is libwebviewchromium_loader and vulkan.
NativeLoaderNamespace ns;
if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
CHECK(ns.is_android_namespace());
return ns.get_android_ns();
}
return nullptr;
}
#endif