Home | History | Annotate | Download | only in libnativebridge
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "nativebridge"
     18 
     19 #include "nativebridge/native_bridge.h"
     20 
     21 #include <dlfcn.h>
     22 #include <errno.h>
     23 #include <fcntl.h>
     24 #include <stdio.h>
     25 #include <sys/mount.h>
     26 #include <sys/stat.h>
     27 #include <unistd.h>
     28 
     29 #include <cstring>
     30 
     31 #include <android-base/macros.h>
     32 #include <log/log.h>
     33 
     34 namespace android {
     35 
     36 // Environment values required by the apps running with native bridge.
     37 struct NativeBridgeRuntimeValues {
     38     const char* os_arch;
     39     const char* cpu_abi;
     40     const char* cpu_abi2;
     41     const char* *supported_abis;
     42     int32_t abi_count;
     43 };
     44 
     45 // The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
     46 static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
     47 
     48 enum class NativeBridgeState {
     49   kNotSetup,                        // Initial state.
     50   kOpened,                          // After successful dlopen.
     51   kPreInitialized,                  // After successful pre-initialization.
     52   kInitialized,                     // After successful initialization.
     53   kClosed                           // Closed or errors.
     54 };
     55 
     56 static constexpr const char* kNotSetupString = "kNotSetup";
     57 static constexpr const char* kOpenedString = "kOpened";
     58 static constexpr const char* kPreInitializedString = "kPreInitialized";
     59 static constexpr const char* kInitializedString = "kInitialized";
     60 static constexpr const char* kClosedString = "kClosed";
     61 
     62 static const char* GetNativeBridgeStateString(NativeBridgeState state) {
     63   switch (state) {
     64     case NativeBridgeState::kNotSetup:
     65       return kNotSetupString;
     66 
     67     case NativeBridgeState::kOpened:
     68       return kOpenedString;
     69 
     70     case NativeBridgeState::kPreInitialized:
     71       return kPreInitializedString;
     72 
     73     case NativeBridgeState::kInitialized:
     74       return kInitializedString;
     75 
     76     case NativeBridgeState::kClosed:
     77       return kClosedString;
     78   }
     79 }
     80 
     81 // Current state of the native bridge.
     82 static NativeBridgeState state = NativeBridgeState::kNotSetup;
     83 
     84 // The version of NativeBridge implementation.
     85 // Different Nativebridge interface needs the service of different version of
     86 // Nativebridge implementation.
     87 // Used by isCompatibleWith() which is introduced in v2.
     88 enum NativeBridgeImplementationVersion {
     89   // first version, not used.
     90   DEFAULT_VERSION = 1,
     91   // The version which signal semantic is introduced.
     92   SIGNAL_VERSION = 2,
     93   // The version which namespace semantic is introduced.
     94   NAMESPACE_VERSION = 3,
     95   // The version with vendor namespaces
     96   VENDOR_NAMESPACE_VERSION = 4,
     97 };
     98 
     99 // Whether we had an error at some point.
    100 static bool had_error = false;
    101 
    102 // Handle of the loaded library.
    103 static void* native_bridge_handle = nullptr;
    104 // Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized
    105 // later.
    106 static const NativeBridgeCallbacks* callbacks = nullptr;
    107 // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge.
    108 static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
    109 
    110 // The app's code cache directory.
    111 static char* app_code_cache_dir = nullptr;
    112 
    113 // Code cache directory (relative to the application private directory)
    114 // Ideally we'd like to call into framework to retrieve this name. However that's considered an
    115 // implementation detail and will require either hacks or consistent refactorings. We compromise
    116 // and hard code the directory name again here.
    117 static constexpr const char* kCodeCacheDir = "code_cache";
    118 
    119 // Characters allowed in a native bridge filename. The first character must
    120 // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
    121 static bool CharacterAllowed(char c, bool first) {
    122   if (first) {
    123     return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
    124   } else {
    125     return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
    126            (c == '.') || (c == '_') || (c == '-');
    127   }
    128 }
    129 
    130 static void ReleaseAppCodeCacheDir() {
    131   if (app_code_cache_dir != nullptr) {
    132     delete[] app_code_cache_dir;
    133     app_code_cache_dir = nullptr;
    134   }
    135 }
    136 
    137 // We only allow simple names for the library. It is supposed to be a file in
    138 // /system/lib or /vendor/lib. Only allow a small range of characters, that is
    139 // names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
    140 bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
    141   const char* ptr = nb_library_filename;
    142   if (*ptr == 0) {
    143     // Emptry string. Allowed, means no native bridge.
    144     return true;
    145   } else {
    146     // First character must be [a-zA-Z].
    147     if (!CharacterAllowed(*ptr, true))  {
    148       // Found an invalid fist character, don't accept.
    149       ALOGE("Native bridge library %s has been rejected for first character %c",
    150             nb_library_filename,
    151             *ptr);
    152       return false;
    153     } else {
    154       // For the rest, be more liberal.
    155       ptr++;
    156       while (*ptr != 0) {
    157         if (!CharacterAllowed(*ptr, false)) {
    158           // Found an invalid character, don't accept.
    159           ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
    160           return false;
    161         }
    162         ptr++;
    163       }
    164     }
    165     return true;
    166   }
    167 }
    168 
    169 // The policy of invoking Nativebridge changed in v3 with/without namespace.
    170 // Suggest Nativebridge implementation not maintain backward-compatible.
    171 static bool isCompatibleWith(const uint32_t version) {
    172   // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
    173   // version.
    174   if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
    175     return false;
    176   }
    177 
    178   // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
    179   if (callbacks->version >= SIGNAL_VERSION) {
    180     return callbacks->isCompatibleWith(version);
    181   }
    182 
    183   return true;
    184 }
    185 
    186 static void CloseNativeBridge(bool with_error) {
    187   state = NativeBridgeState::kClosed;
    188   had_error |= with_error;
    189   ReleaseAppCodeCacheDir();
    190 }
    191 
    192 bool LoadNativeBridge(const char* nb_library_filename,
    193                       const NativeBridgeRuntimeCallbacks* runtime_cbs) {
    194   // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
    195   // multi-threaded, so we do not need locking here.
    196 
    197   if (state != NativeBridgeState::kNotSetup) {
    198     // Setup has been called before. Ignore this call.
    199     if (nb_library_filename != nullptr) {  // Avoids some log-spam for dalvikvm.
    200       ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
    201             GetNativeBridgeStateString(state));
    202     }
    203     // Note: counts as an error, even though the bridge may be functional.
    204     had_error = true;
    205     return false;
    206   }
    207 
    208   if (nb_library_filename == nullptr || *nb_library_filename == 0) {
    209     CloseNativeBridge(false);
    210     return false;
    211   } else {
    212     if (!NativeBridgeNameAcceptable(nb_library_filename)) {
    213       CloseNativeBridge(true);
    214     } else {
    215       // Try to open the library.
    216       void* handle = dlopen(nb_library_filename, RTLD_LAZY);
    217       if (handle != nullptr) {
    218         callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
    219                                                                    kNativeBridgeInterfaceSymbol));
    220         if (callbacks != nullptr) {
    221           if (isCompatibleWith(NAMESPACE_VERSION)) {
    222             // Store the handle for later.
    223             native_bridge_handle = handle;
    224           } else {
    225             callbacks = nullptr;
    226             dlclose(handle);
    227             ALOGW("Unsupported native bridge interface.");
    228           }
    229         } else {
    230           dlclose(handle);
    231         }
    232       }
    233 
    234       // Two failure conditions: could not find library (dlopen failed), or could not find native
    235       // bridge interface (dlsym failed). Both are an error and close the native bridge.
    236       if (callbacks == nullptr) {
    237         CloseNativeBridge(true);
    238       } else {
    239         runtime_callbacks = runtime_cbs;
    240         state = NativeBridgeState::kOpened;
    241       }
    242     }
    243     return state == NativeBridgeState::kOpened;
    244   }
    245 }
    246 
    247 bool NeedsNativeBridge(const char* instruction_set) {
    248   if (instruction_set == nullptr) {
    249     ALOGE("Null instruction set in NeedsNativeBridge.");
    250     return false;
    251   }
    252   return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
    253 }
    254 
    255 #ifdef __APPLE__
    256 template<typename T> void UNUSED(const T&) {}
    257 #endif
    258 
    259 bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
    260   if (state != NativeBridgeState::kOpened) {
    261     ALOGE("Invalid state: native bridge is expected to be opened.");
    262     CloseNativeBridge(true);
    263     return false;
    264   }
    265 
    266   if (app_data_dir_in == nullptr) {
    267     ALOGE("Application private directory cannot be null.");
    268     CloseNativeBridge(true);
    269     return false;
    270   }
    271 
    272   // Create the path to the application code cache directory.
    273   // The memory will be release after Initialization or when the native bridge is closed.
    274   const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/'
    275   app_code_cache_dir = new char[len];
    276   snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
    277 
    278   // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo.
    279   // Failure is not fatal and will keep the native bridge in kPreInitialized.
    280   state = NativeBridgeState::kPreInitialized;
    281 
    282 #ifndef __APPLE__
    283   if (instruction_set == nullptr) {
    284     return true;
    285   }
    286   size_t isa_len = strlen(instruction_set);
    287   if (isa_len > 10) {
    288     // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for
    289     // x86_64 [including the trailing \0]). This is so we don't have to change here if there will
    290     // be another instruction set in the future.
    291     ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.",
    292           instruction_set);
    293     return true;
    294   }
    295 
    296   // If the file does not exist, the mount command will fail,
    297   // so we save the extra file existence check.
    298   char cpuinfo_path[1024];
    299 
    300 #if defined(__ANDROID__)
    301   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
    302 #ifdef __LP64__
    303       "64"
    304 #endif  // __LP64__
    305       "/%s/cpuinfo", instruction_set);
    306 #else   // !__ANDROID__
    307   // To be able to test on the host, we hardwire a relative path.
    308   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
    309 #endif
    310 
    311   // Bind-mount.
    312   if (TEMP_FAILURE_RETRY(mount(cpuinfo_path,        // Source.
    313                                "/proc/cpuinfo",     // Target.
    314                                nullptr,             // FS type.
    315                                MS_BIND,             // Mount flags: bind mount.
    316                                nullptr)) == -1) {   // "Data."
    317     ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno));
    318   }
    319 #else  // __APPLE__
    320   UNUSED(instruction_set);
    321   ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible.");
    322 #endif
    323 
    324   return true;
    325 }
    326 
    327 static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) {
    328   if (value != nullptr) {
    329     jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;");
    330     if (field_id == nullptr) {
    331       env->ExceptionClear();
    332       ALOGW("Could not find %s field.", field);
    333       return;
    334     }
    335 
    336     jstring str = env->NewStringUTF(value);
    337     if (str == nullptr) {
    338       env->ExceptionClear();
    339       ALOGW("Could not create string %s.", value);
    340       return;
    341     }
    342 
    343     env->SetStaticObjectField(build_class, field_id, str);
    344   }
    345 }
    346 
    347 // Set up the environment for the bridged app.
    348 static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) {
    349   // Need a JNIEnv* to do anything.
    350   if (env == nullptr) {
    351     ALOGW("No JNIEnv* to set up app environment.");
    352     return;
    353   }
    354 
    355   // Query the bridge for environment values.
    356   const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa);
    357   if (env_values == nullptr) {
    358     return;
    359   }
    360 
    361   // Keep the JNIEnv clean.
    362   jint success = env->PushLocalFrame(16);  // That should be small and large enough.
    363   if (success < 0) {
    364     // Out of memory, really borked.
    365     ALOGW("Out of memory while setting up app environment.");
    366     env->ExceptionClear();
    367     return;
    368   }
    369 
    370   // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge.
    371   if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr ||
    372       env_values->abi_count >= 0) {
    373     jclass bclass_id = env->FindClass("android/os/Build");
    374     if (bclass_id != nullptr) {
    375       SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi);
    376       SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2);
    377     } else {
    378       // For example in a host test environment.
    379       env->ExceptionClear();
    380       ALOGW("Could not find Build class.");
    381     }
    382   }
    383 
    384   if (env_values->os_arch != nullptr) {
    385     jclass sclass_id = env->FindClass("java/lang/System");
    386     if (sclass_id != nullptr) {
    387       jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty",
    388           "(Ljava/lang/String;Ljava/lang/String;)V");
    389       if (set_prop_id != nullptr) {
    390         // Init os.arch to the value reqired by the apps running with native bridge.
    391         env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"),
    392             env->NewStringUTF(env_values->os_arch));
    393       } else {
    394         env->ExceptionClear();
    395         ALOGW("Could not find System#setUnchangeableSystemProperty.");
    396       }
    397     } else {
    398       env->ExceptionClear();
    399       ALOGW("Could not find System class.");
    400     }
    401   }
    402 
    403   // Make it pristine again.
    404   env->PopLocalFrame(nullptr);
    405 }
    406 
    407 bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
    408   // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that
    409   // point we are not multi-threaded, so we do not need locking here.
    410 
    411   if (state == NativeBridgeState::kPreInitialized) {
    412     // Check for code cache: if it doesn't exist try to create it.
    413     struct stat st;
    414     if (stat(app_code_cache_dir, &st) == -1) {
    415       if (errno == ENOENT) {
    416         if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
    417           ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
    418           ReleaseAppCodeCacheDir();
    419         }
    420       } else {
    421         ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
    422         ReleaseAppCodeCacheDir();
    423       }
    424     } else if (!S_ISDIR(st.st_mode)) {
    425       ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
    426       ReleaseAppCodeCacheDir();
    427     }
    428 
    429     // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
    430     if (state == NativeBridgeState::kPreInitialized) {
    431       if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) {
    432         SetupEnvironment(callbacks, env, instruction_set);
    433         state = NativeBridgeState::kInitialized;
    434         // We no longer need the code cache path, release the memory.
    435         ReleaseAppCodeCacheDir();
    436       } else {
    437         // Unload the library.
    438         dlclose(native_bridge_handle);
    439         CloseNativeBridge(true);
    440       }
    441     }
    442   } else {
    443     CloseNativeBridge(true);
    444   }
    445 
    446   return state == NativeBridgeState::kInitialized;
    447 }
    448 
    449 void UnloadNativeBridge() {
    450   // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that
    451   // point we are not multi-threaded, so we do not need locking here.
    452 
    453   switch(state) {
    454     case NativeBridgeState::kOpened:
    455     case NativeBridgeState::kPreInitialized:
    456     case NativeBridgeState::kInitialized:
    457       // Unload.
    458       dlclose(native_bridge_handle);
    459       CloseNativeBridge(false);
    460       break;
    461 
    462     case NativeBridgeState::kNotSetup:
    463       // Not even set up. Error.
    464       CloseNativeBridge(true);
    465       break;
    466 
    467     case NativeBridgeState::kClosed:
    468       // Ignore.
    469       break;
    470   }
    471 }
    472 
    473 bool NativeBridgeError() {
    474   return had_error;
    475 }
    476 
    477 bool NativeBridgeAvailable() {
    478   return state == NativeBridgeState::kOpened
    479       || state == NativeBridgeState::kPreInitialized
    480       || state == NativeBridgeState::kInitialized;
    481 }
    482 
    483 bool NativeBridgeInitialized() {
    484   // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after
    485   // Runtime::DidForkFromZygote. In that case we do not need a lock.
    486   return state == NativeBridgeState::kInitialized;
    487 }
    488 
    489 void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
    490   if (NativeBridgeInitialized()) {
    491     return callbacks->loadLibrary(libpath, flag);
    492   }
    493   return nullptr;
    494 }
    495 
    496 void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
    497                                 uint32_t len) {
    498   if (NativeBridgeInitialized()) {
    499     return callbacks->getTrampoline(handle, name, shorty, len);
    500   }
    501   return nullptr;
    502 }
    503 
    504 bool NativeBridgeIsSupported(const char* libpath) {
    505   if (NativeBridgeInitialized()) {
    506     return callbacks->isSupported(libpath);
    507   }
    508   return false;
    509 }
    510 
    511 uint32_t NativeBridgeGetVersion() {
    512   if (NativeBridgeAvailable()) {
    513     return callbacks->version;
    514   }
    515   return 0;
    516 }
    517 
    518 NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
    519   if (NativeBridgeInitialized()) {
    520     if (isCompatibleWith(SIGNAL_VERSION)) {
    521       return callbacks->getSignalHandler(signal);
    522     } else {
    523       ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
    524     }
    525   }
    526   return nullptr;
    527 }
    528 
    529 int NativeBridgeUnloadLibrary(void* handle) {
    530   if (NativeBridgeInitialized()) {
    531     if (isCompatibleWith(NAMESPACE_VERSION)) {
    532       return callbacks->unloadLibrary(handle);
    533     } else {
    534       ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
    535     }
    536   }
    537   return -1;
    538 }
    539 
    540 const char* NativeBridgeGetError() {
    541   if (NativeBridgeInitialized()) {
    542     if (isCompatibleWith(NAMESPACE_VERSION)) {
    543       return callbacks->getError();
    544     } else {
    545       return "native bridge implementation is not compatible with version 3, cannot get message";
    546     }
    547   }
    548   return "native bridge is not initialized";
    549 }
    550 
    551 bool NativeBridgeIsPathSupported(const char* path) {
    552   if (NativeBridgeInitialized()) {
    553     if (isCompatibleWith(NAMESPACE_VERSION)) {
    554       return callbacks->isPathSupported(path);
    555     } else {
    556       ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
    557     }
    558   }
    559   return false;
    560 }
    561 
    562 bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
    563                                         const char* anon_ns_library_path) {
    564   if (NativeBridgeInitialized()) {
    565     if (isCompatibleWith(NAMESPACE_VERSION)) {
    566       return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
    567     } else {
    568       ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
    569     }
    570   }
    571 
    572   return false;
    573 }
    574 
    575 native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
    576                                                        const char* ld_library_path,
    577                                                        const char* default_library_path,
    578                                                        uint64_t type,
    579                                                        const char* permitted_when_isolated_path,
    580                                                        native_bridge_namespace_t* parent_ns) {
    581   if (NativeBridgeInitialized()) {
    582     if (isCompatibleWith(NAMESPACE_VERSION)) {
    583       return callbacks->createNamespace(name,
    584                                         ld_library_path,
    585                                         default_library_path,
    586                                         type,
    587                                         permitted_when_isolated_path,
    588                                         parent_ns);
    589     } else {
    590       ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
    591     }
    592   }
    593 
    594   return nullptr;
    595 }
    596 
    597 bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
    598                                 const char* shared_libs_sonames) {
    599   if (NativeBridgeInitialized()) {
    600     if (isCompatibleWith(NAMESPACE_VERSION)) {
    601       return callbacks->linkNamespaces(from, to, shared_libs_sonames);
    602     } else {
    603       ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
    604     }
    605   }
    606 
    607   return false;
    608 }
    609 
    610 native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
    611   if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
    612     return nullptr;
    613   }
    614 
    615   return callbacks->getVendorNamespace();
    616 }
    617 
    618 void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
    619   if (NativeBridgeInitialized()) {
    620     if (isCompatibleWith(NAMESPACE_VERSION)) {
    621       return callbacks->loadLibraryExt(libpath, flag, ns);
    622     } else {
    623       ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
    624     }
    625   }
    626   return nullptr;
    627 }
    628 
    629 };  // namespace android
    630