void* OpenSystemLibrary(constchar* path, int flags) { #ifdef ART_TARGET_ANDROID // The system namespace is called "default" for binaries in /system and // "system" for those in the Runtime APEX. Try "system" first since // "default" always exists. // TODO(b/185587109): Get rid of this error prone logic.
android_namespace_t* system_ns = android_get_exported_namespace("system"); if (system_ns == nullptr) {
system_ns = android_get_exported_namespace("default");
LOG_ALWAYS_FATAL_IF(system_ns == nullptr, "Failed to get system namespace for loading %s", path);
} const android_dlextinfo dlextinfo = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = system_ns,
}; return android_dlopen_ext(path, flags, &dlextinfo); #else return dlopen(path, flags); #endif
}
// Environment values required by the apps running with native bridge. struct NativeBridgeRuntimeValues { constchar* os_arch; constchar* cpu_abi; constchar* cpu_abi2; constchar* *supported_abis;
int32_t abi_count;
};
// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. static constexpr constchar* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
enumclass NativeBridgeState {
kNotSetup, // Initial state.
kOpened, // After successful dlopen.
kPreInitialized, // After successful pre-initialization.
kInitialized, // After successful initialization.
kClosed // Closed or errors.
};
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 g_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, // The version with pre-zygote-fork hook to support app-zygotes.
PRE_ZYGOTE_FORK_VERSION = 6, // The version with critical_native support
CRITICAL_NATIVE_SUPPORT_VERSION = 7, // The version with native bridge detection fallback for function pointers
IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION = 8,
};
// Whether we had an error at some point. staticbool g_had_error = false;
// Handle of the loaded library. staticvoid* g_native_bridge_handle = nullptr; // Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized // later. staticconst NativeBridgeCallbacks* g_callbacks = nullptr; // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. staticconst NativeBridgeRuntimeCallbacks* g_runtime_callbacks = nullptr;
// The app's code cache directory. staticchar* g_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 constchar* 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._-]. staticbool 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(constchar* nb_library_filename) { constchar* ptr = nb_library_filename; if (*ptr == 0) { // Emptry string. Allowed, means no native bridge. returntrue;
} 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); returnfalse;
} 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); returnfalse;
}
ptr++;
}
} returntrue;
}
}
// The policy of invoking Nativebridge changed in v3 with/without namespace. // Suggest Nativebridge implementation not maintain backward-compatible. staticbool isCompatibleWith(const uint32_t version) { // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported // version. if (g_callbacks == nullptr || g_callbacks->version == 0 || version == 0) { returnfalse;
}
// If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check. if (g_callbacks->version >= SIGNAL_VERSION) { return g_callbacks->isCompatibleWith(version);
}
bool LoadNativeBridge(constchar* 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 (g_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(g_state));
} // Note: counts as an error, even though the bridge may be functional.
g_had_error = true; returnfalse;
}
if (nb_library_filename == nullptr || *nb_library_filename == 0) {
CloseNativeBridge(false); returnfalse;
} else { if (!NativeBridgeNameAcceptable(nb_library_filename)) {
CloseNativeBridge(true);
} else { // Try to open the library. We assume this library is provided by the // platform rather than the ART APEX itself, so use the system namespace // to avoid requiring a static linker config link to it from the // com_android_art namespace. void* handle = OpenSystemLibrary(nb_library_filename, RTLD_LAZY);
if (handle != nullptr) {
g_callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, kNativeBridgeInterfaceSymbol)); if (g_callbacks != nullptr) { if (isCompatibleWith(NAMESPACE_VERSION)) { // Store the handle for later.
g_native_bridge_handle = handle;
} else {
ALOGW("Unsupported native bridge API in %s (is version %d not compatible with %d)",
nb_library_filename,
g_callbacks->version,
NAMESPACE_VERSION);
g_callbacks = nullptr;
dlclose(handle);
}
} else {
dlclose(handle);
ALOGW("Unsupported native bridge API in %s: %s not found",
nb_library_filename, kNativeBridgeInterfaceSymbol);
}
} else {
ALOGW("Failed to load native bridge implementation: %s", dlerror());
}
// 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 (g_callbacks == nullptr) {
CloseNativeBridge(true);
} else {
g_runtime_callbacks = runtime_cbs;
g_state = NativeBridgeState::kOpened;
}
} return g_state == NativeBridgeState::kOpened;
}
}
bool NeedsNativeBridge(constchar* instruction_set) { if (instruction_set == nullptr) {
ALOGE("Null instruction set in NeedsNativeBridge."); returnfalse;
} return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
}
bool PreInitializeNativeBridge(constchar* app_data_dir_in,
[[maybe_unused]] constchar* instruction_set) { if (g_state != NativeBridgeState::kOpened) {
ALOGE("Invalid state: native bridge is expected to be opened.");
CloseNativeBridge(true); returnfalse;
}
if (app_data_dir_in != nullptr) { // 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' + '/'
g_app_code_cache_dir = newchar[len];
snprintf(g_app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
} else {
ALOGW("Application private directory isn't available.");
g_app_code_cache_dir = nullptr;
}
void PreZygoteForkNativeBridge() { if (NativeBridgeInitialized()) { if (isCompatibleWith(PRE_ZYGOTE_FORK_VERSION)) { return g_callbacks->preZygoteFork();
} else {
ALOGE("not compatible with version %d, preZygoteFork() isn't invoked",
PRE_ZYGOTE_FORK_VERSION);
}
}
}
// Set up the environment for the bridged app. staticvoid SetupEnvironment(const NativeBridgeCallbacks* cbs, JNIEnv* env, constchar* 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. conststruct NativeBridgeRuntimeValues* env_values = cbs->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;
}
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, constchar* 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 (g_state != NativeBridgeState::kPreInitialized) {
CloseNativeBridge(true); returnfalse;
}
if (g_app_code_cache_dir != nullptr) { // Check for code cache: if it doesn't exist try to create it. struct stat st; if (stat(g_app_code_cache_dir, &st) == -1) { if (errno == ENOENT) { if (mkdir(g_app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
ALOGW( "Cannot create code cache directory %s: %s.", g_app_code_cache_dir, strerror(errno));
ReleaseAppCodeCacheDir();
}
} else {
ALOGW("Cannot stat code cache directory %s: %s.", g_app_code_cache_dir, strerror(errno));
ReleaseAppCodeCacheDir();
}
} elseif (!S_ISDIR(st.st_mode)) {
ALOGW("Code cache is not a directory %s.", g_app_code_cache_dir);
ReleaseAppCodeCacheDir();
}
}
if (g_callbacks->initialize(g_runtime_callbacks, g_app_code_cache_dir, instruction_set)) { // TODO(b/419835068): SetupEnvironment is likely not needed anymore and can be removed.
SetupEnvironment(g_callbacks, env, instruction_set);
g_state = NativeBridgeState::kInitialized; // We no longer need the code cache path, release the memory.
ReleaseAppCodeCacheDir();
} else { // Unload the library.
dlclose(g_native_bridge_handle);
CloseNativeBridge(true);
}
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 (g_state) { case NativeBridgeState::kOpened: case NativeBridgeState::kPreInitialized: case NativeBridgeState::kInitialized: // Unload.
dlclose(g_native_bridge_handle);
CloseNativeBridge(false); break;
case NativeBridgeState::kNotSetup: // Not even set up. Error.
CloseNativeBridge(true); break;
case NativeBridgeState::kClosed: // Ignore. break;
}
}
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 g_state == NativeBridgeState::kInitialized;
}
void* NativeBridgeLoadLibrary(constchar* libpath, int flag) { if (NativeBridgeInitialized()) { return g_callbacks->loadLibrary(libpath, flag);
} return nullptr;
}
// For version 1 isCompatibleWith is always true, even though the extensions // are not supported, so we need to handle it separately. if (g_callbacks != nullptr && g_callbacks->version == DEFAULT_VERSION) { return g_callbacks->getTrampoline(handle, name, shorty, len);
}
if (isCompatibleWith(CRITICAL_NATIVE_SUPPORT_VERSION)) { return g_callbacks->getTrampolineWithJNICallType(handle, name, shorty, len, jni_call_type);
}
NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) { if (NativeBridgeInitialized()) { if (isCompatibleWith(SIGNAL_VERSION)) { return g_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 g_callbacks->unloadLibrary(handle);
} else {
ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
}
} return -1;
}
constchar* NativeBridgeGetError() { if (NativeBridgeInitialized()) { if (isCompatibleWith(NAMESPACE_VERSION)) { return g_callbacks->getError();
} else { return"native bridge implementation is not compatible with version 3, cannot get message";
}
} return"native bridge is not initialized";
}
bool NativeBridgeIsPathSupported(constchar* path) { if (NativeBridgeInitialized()) { if (isCompatibleWith(NAMESPACE_VERSION)) { return g_callbacks->isPathSupported(path);
} else {
ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
}
} returnfalse;
}
bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, constchar* shared_libs_sonames) { if (NativeBridgeInitialized()) { if (isCompatibleWith(NAMESPACE_VERSION)) { return g_callbacks->linkNamespaces(from, to, shared_libs_sonames);
} else {
ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
}
}
returnfalse;
}
native_bridge_namespace_t* NativeBridgeGetExportedNamespace(constchar* name) { if (!NativeBridgeInitialized()) { return nullptr;
}
if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) { return g_callbacks->getExportedNamespace(name);
}
// sphal is vendor namespace name -> use v4 callback in the case NB g_callbacks // are not compatible with v5 if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) { return g_callbacks->getVendorNamespace();
}
return nullptr;
}
void* NativeBridgeLoadLibraryExt(constchar* libpath, int flag, native_bridge_namespace_t* ns) { if (NativeBridgeInitialized()) { if (isCompatibleWith(NAMESPACE_VERSION)) { return g_callbacks->loadLibraryExt(libpath, flag, ns);
} else {
ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
}
} return nullptr;
}
bool NativeBridgeIsNativeBridgeFunctionPointer(constvoid* method) { if (NativeBridgeInitialized()) { if (isCompatibleWith(IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION)) { return g_callbacks->isNativeBridgeFunctionPointer(method);
} else {
ALOGW("not compatible with version %d, unable to call isNativeBridgeFunctionPointer",
IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION);
}
} returnfalse;
}
} // extern "C"
} // namespace android
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.