/* * 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 "nativebridge/native_bridge.h" #include #include #include namespace android { // 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. kInitialized, // After successful initialization. kClosed // Closed or errors. }; static const char* kNotSetupString = "kNotSetup"; static const char* kOpenedString = "kOpened"; static const char* kInitializedString = "kInitialized"; static const char* kClosedString = "kClosed"; static const char* GetNativeBridgeStateString(NativeBridgeState state) { switch (state) { case NativeBridgeState::kNotSetup: return kNotSetupString; case NativeBridgeState::kOpened: return kOpenedString; case NativeBridgeState::kInitialized: return kInitializedString; case NativeBridgeState::kClosed: return kClosedString; } } // Current state of the native bridge. static NativeBridgeState state = NativeBridgeState::kNotSetup; // 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 NativeBridgeCallbacks* callbacks = nullptr; // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; // 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 == '-'); } } // 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; } } 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. 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) { state = NativeBridgeState::kClosed; return true; } else { if (!NativeBridgeNameAcceptable(nb_library_filename)) { state = NativeBridgeState::kClosed; had_error = true; } else { // Try to open the library. void* handle = dlopen(nb_library_filename, RTLD_LAZY); if (handle != nullptr) { callbacks = reinterpret_cast(dlsym(handle, kNativeBridgeInterfaceSymbol)); if (callbacks != nullptr) { // Store the handle for later. native_bridge_handle = handle; } 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) { had_error = true; state = NativeBridgeState::kClosed; } else { runtime_callbacks = runtime_cbs; state = NativeBridgeState::kOpened; } } return state == NativeBridgeState::kOpened; } } bool InitializeNativeBridge() { // 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::kOpened) { // Try to initialize. if (callbacks->initialize(runtime_callbacks)) { state = NativeBridgeState::kInitialized; } else { // Unload the library. dlclose(native_bridge_handle); had_error = true; state = NativeBridgeState::kClosed; } } else { had_error = true; state = NativeBridgeState::kClosed; } 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::kInitialized: // Unload. dlclose(native_bridge_handle); break; case NativeBridgeState::kNotSetup: // Not even set up. Error. had_error = true; break; case NativeBridgeState::kClosed: // Ignore. break; } state = NativeBridgeState::kClosed; } bool NativeBridgeError() { return had_error; } bool NativeBridgeAvailable() { return state == NativeBridgeState::kOpened || 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; } }; // namespace android