Android6.0中的NativeBridge

来源:互联网 发布:php 返回页面 编辑:程序博客网 时间:2024/05/29 15:50

主要参考:http://blog.csdn.net/roland_sun/article/details/49688311 一文,换成了Android6.0的实现过程。

从Android从5.0开始,在其ART虚拟机的实现中,引入了一个叫做NativeBridge的中间模块。这个模块基本上就是为了在JNI调用时进行动态转码用的,自带了基本上所有的处理逻辑。
首先,我们来看看NativeBridge的初始化过程。大家知道,ART虚拟机的初始化是在Runtime::Init函数中进行的,在这个函数的最后,有下面这行代码(代码位于art\runtime\runtime.cc文件中):

 782 bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) {    ...1088   // Look for a native bridge.1089   //1090   // The intended flow here is, in the case of a running system:1091   //1092   // Runtime::Init() (zygote):1093   //   LoadNativeBridge -> dlopen from cmd line parameter.1094   //  |1095   //  V1096   // Runtime::Start() (zygote):1097   //   No-op wrt native bridge.1098   //  |1099   //  | start app1100   //  V1101   // DidForkFromZygote(action)1102   //   action = kUnload -> dlclose native bridge.1103   //   action = kInitialize -> initialize library1104   //1105   //1106   // The intended flow here is, in the case of a simple dalvikvm call:1107   //1108   // Runtime::Init():1109   //   LoadNativeBridge -> dlopen from cmd line parameter.1110   //  |1111   //  V1112   // Runtime::Start():1113   //   DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.1114   //   No-op wrt native bridge.1115   {1116     std::string native_bridge_file_name = runtime_options.ReleaseOrDefault(Opt::NativeBridge);1117     is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);1118   }    ...1124   return true;1125 }

可以看到在初始化的最后,调用了LoadNativeBridge函数,并传入了一个文件名作为参数。
LoadNativeBridge函数的实现如下:

106 bool LoadNativeBridge(std::string& native_bridge_library_filename) {107   VLOG(startup) << "Runtime::Setup native bridge library: "108       << (native_bridge_library_filename.empty() ? "(empty)" : native_bridge_library_filename);109   return android::LoadNativeBridge(native_bridge_library_filename.c_str(),110                                    &native_bridge_art_callbacks_);111 }

其中直接调用了android::LoadNativeBridge函数,但是多了一个参数native_bridge_art_callbacks_,看名字应该可以猜出来是一组回调函数表。而其中的多增加的参数的含义如下:

 93 // Native bridge library runtime callbacks. They represent the runtime interface to native bridge. 94 //     95 // The interface is expected to expose the following methods: 96 // getMethodShorty(): in the case of native method calling JNI native function CallXXXXMethodY(), 97 //   native bridge calls back to VM for the shorty of the method so that it can prepare based on 98 //   host calling convention. 99 // getNativeMethodCount() and getNativeMethods(): in case of JNI function UnregisterNatives(),100 //   native bridge can call back to get all native methods of specified class so that all101 //   corresponding trampolines can be destroyed.102 static android::NativeBridgeRuntimeCallbacks native_bridge_art_callbacks_ {103   GetMethodShorty, GetNativeMethodCount, GetNativeMethods104 };

而NativeBridgeRuntimeCallbacks的定义如下:

//~/android-6.0.1_r62/system/core/include/nativebridge/native_bridge.h中175 struct NativeBridgeRuntimeCallbacks {176   // Get shorty of a Java method. The shorty is supposed to be persistent in memory.177   //178   // Parameters:179   //   env [IN] pointer to JNIenv.180   //   mid [IN] Java methodID.181   // Returns:182   //   short descriptor for method.183   const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);184   185   // Get number of native methods for specified class.186   //187   // Parameters:188   //   env [IN] pointer to JNIenv.189   //   clazz [IN] Java class object.190   // Returns:191   //   number of native methods.192   uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);193   194   // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed195   // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.196   //197   // Parameters:198   //   env [IN] pointer to JNIenv.199   //   clazz [IN] Java class object.200   //   methods [OUT] array of method with the name, shorty, and fnPtr.201   //   method_count [IN] max number of elements in methods.202   // Returns:203   //   number of method it actually wrote to methods.204   uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,205                                uint32_t method_count);206 };

这三个回调函数的功能如下:
1)getMethodShorty:获得指定JNI方法的短描述符;
2)getNativeMethodCount:获得指定类内部定义的JNI函数的个数;
3)getNativeMethods:获得指定类内部的method_count个JNI方法。

接下来,我们继续来看看android::LoadNativeBridge函数做了些什么

//~/android-6.0.1_r62/system/core/libnativebridge/native_bridge.cc169 bool LoadNativeBridge(const char* nb_library_filename,170                       const NativeBridgeRuntimeCallbacks* runtime_cbs) {171   // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not172   // multi-threaded, so we do not need locking here.173     //定义为static constexpr const char* kNotSetupString = "kNotSetup";174   if (state != NativeBridgeState::kNotSetup) {175     // Setup has been called before. Ignore this call.176     if (nb_library_filename != nullptr) {  // Avoids some log-spam for dalvikvm.177       ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",178             GetNativeBridgeStateString(state));179     }180     // Note: counts as an error, even though the bridge may be functional.181     had_error = true;182     return false;183   }184 185   if (nb_library_filename == nullptr || *nb_library_filename == 0) {186     CloseNativeBridge(false);187     return false;188   } else {189     if (!NativeBridgeNameAcceptable(nb_library_filename)) {190       CloseNativeBridge(true);191     } else {192       // Try to open the library.193       void* handle = dlopen(nb_library_filename, RTLD_LAZY);194       if (handle != nullptr) {195         callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,196                                                                    kNativeBridgeInterfaceSymbol));197         if (callbacks != nullptr) {198           if (VersionCheck(callbacks)) {199             // Store the handle for later.200             native_bridge_handle = handle;201           } else {202             callbacks = nullptr;203             dlclose(handle);204             ALOGW("Unsupported native bridge interface.");205           }206         } else {207           dlclose(handle);208         }209       }210 211       // Two failure conditions: could not find library (dlopen failed), or could not find native212       // bridge interface (dlsym failed). Both are an error and close the native bridge.213       if (callbacks == nullptr) {214         CloseNativeBridge(true);215       } else {216         runtime_callbacks = runtime_cbs;217         state = NativeBridgeState::kOpened;218       }219     }220     return state == NativeBridgeState::kOpened;221   }222 }

首先是看一下是否已经初始化过NativeBridge了,如果已经初始化过了的话直接报错返回。再检查传入的文件名是否为空,NativeBridgeNameAcceptable函数来检查文件名且是否满足要求:只允许一些简单名字,需要是在/system/lib或者/vendor/lib中的文件,只允许一小部分的字符出现,包括[a-zA-Z0-9._-]以及,首字母可以是[a-zA-Z]。

如果文件名满足要求,接着调用dlopen打开文件名指定的那个动态链接库(所以文件名应该是以.so结尾的)。如果打开成功的话,再调用dlsym,在这个动态链接库中查找由kNativeBridgeInterfaceSymbol指定的那个符号的地址,并将其强制转换成指向NativeBridgeCallbacks结构体的指针。
kNativeBridgeInterfaceSymbol的定义如下:

//~/android-6.0.1_r62/system/core/libnativebridge/native_bridge.cc 40 // The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. 41 static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";

而NativeBridgeCallbacks的定义为:

 95 struct NativeBridgeCallbacks { 96   // Version number of the interface. 97   uint32_t version; 98  99   // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and100   // that the native bridge is initialized only once. Thus it is OK to call this interface for an101   // already initialized native bridge.102   //103   // Parameters:104   //   runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.105   // Returns:106   //   true iff initialization was successful.107   bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,108                      const char* instruction_set);109 110   // Load a shared library that is supported by the native bridge.111   //112   // Parameters:113   //   libpath [IN] path to the shared library114   //   flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h115   // Returns:116   //   The opaque handle of the shared library if sucessful, otherwise NULL117   void* (*loadLibrary)(const char* libpath, int flag);118 119   // Get a native bridge trampoline for specified native method. The trampoline has same120   // sigature as the native method.121   //122   // Parameters:123   //   handle [IN] the handle returned from loadLibrary124   //   shorty [IN] short descriptor of native method125   //   len [IN] length of shorty126   // Returns:127   //   address of trampoline if successful, otherwise NULL128   void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);129 130   // Check whether native library is valid and is for an ABI that is supported by native bridge.131   //132   // Parameters:133   //   libpath [IN] path to the shared library134   // Returns:135   //   TRUE if library is supported by native bridge, FALSE otherwise136   bool (*isSupported)(const char* libpath);137 138   // Provide environment values required by the app running with native bridge according to the139   // instruction set.140   //141   // Parameters:142   //    instruction_set [IN] the instruction set of the app143   // Returns:144   //    NULL if not supported by native bridge.145   //    Otherwise, return all environment values to be set after fork.146   const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);147 148   // Added callbacks in version 2.149 150   // Check whether the bridge is compatible with the given version. A bridge may decide not to be151   // forwards- or backwards-compatible, and libnativebridge will then stop using it.152   //153   // Parameters:154   //     bridge_version [IN] the version of libnativebridge.155   // Returns:156   //     true iff the native bridge supports the given version of libnativebridge.157   bool (*isCompatibleWith)(uint32_t bridge_version);158 159   // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime160   // will ensure that the signal handler is being called after the runtime's own handler, but before161   // all chained handlers. The native bridge should not try to install the handler by itself, as162   // that will potentially lead to cycles.163   //164   // Parameters:165   //     signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is166   //                 supported by the runtime.167   // Returns:168   //     NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the169   //     runtime.170   //     Otherwise, a pointer to the signal handler.171   NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);172 };

所以,所谓的NativeBridge应该是一个动态链接库,这个动态链接库中必须要包含一个符号名字为“NativeBridgeItf”的结构体,这个结构体的前4个字节表示这个NativeBridge的版本号。实际上VersionCheck函数就是看一下这个版本号是否和自己定义的版本号一致的:

//~/android-6.0.1_r62/system/core/libnativebridge/native_bridge.cc144 static bool VersionCheck(const NativeBridgeCallbacks* cb) {145   // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported146   // version.147   if (cb == nullptr || cb->version == 0) {148     return false;149   }150 151   // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.152   if (cb->version >= 2) {153     if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) {154       // TODO: Scan which version is supported, and fall back to handle it.155       return false;156     }157   }158 159   return true;160 }

所以版本号必须为1,否则不兼容。这个结构体后面接着的7个4字节,分别是7个指向函数的指针,这些函数都是在实现动态转指令集功能的动态库中提供的,它们的功能如下:
1)initialize:顾名思义,就是用来做初始化的,初始化的时候要传入前面提到的由ART虚拟机提供的几个函数,包含在android::NativeBridgeRuntimeCallbacks结构体中;
2)loadLibrary:加载一个指定路径的动态库;
3)getTrampoline:获得一个跳转函数,通过它再调用那个不同指令集的真正的函数,这样对于虚拟机来说,根本就不用管是否有NativeBridge的存在了,因为转指令集对其来说完全是透明的;
4)isSupported:查看一个指定路径的动态库是否可以用这个转指令库进行指令转换;
5)getAppEnv:获得这个动态转指令集动态库所支持的平台信息。
6)isCompatibleWith:检查该Bridge是否与给定的版本兼容,一个Bridge可能会决定不再向前或者向后兼容,这时libnativebridge将会停止使用它。
7)getSignalHandler:为特定signal获取一个native bridge的signal handler 的一个回调。

还有一个问题,实现动态转指令集功能的动态链接库文件名是在哪指定的呢?在Runtime::Init函数中,调用LoadNativeBridge的时候,传入的是runtime_options.ReleaseOrDefault(Opt::NativeBridge),通过进一步阅读代码可以发现,这个选项是在AndroidRuntime::startVm函数中,然后通过层层调用得到的:

//~/android-6.0.1_r62/art/runtime/parsed_options.cc中 63 std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognized) {    ...252       .Define("-XX:NativeBridge=_")253           .WithType<std::string>()254           .IntoKey(M::NativeBridge)...

以及

//~/android-6.0.1_r62/frameworks/base/core/jni/AndroidRuntime.cpp 569 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) 570 {    ... 908     // Native bridge library. "0" means that native bridge is disabled. 909     property_get("ro.dalvik.vm.native.bridge", propBuf, ""); 910     if (propBuf[0] == '\0') { 911         ALOGW("ro.dalvik.vm.native.bridge is not expected to be empty"); 912     } else if (strcmp(propBuf, "0") != 0) { 913         snprintf(nativeBridgeLibrary, sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX, 914                  "-XX:NativeBridge=%s", propBuf); 915         addOption(nativeBridgeLibrary); 916     }     ... 971     return 0; 972 }

通过阅读以上的代码,可以发现NativeBridge的动态库是在ART虚拟机启动的时候就已经被加载进来的,当然包括所有应用程序的父进程,即Zygote进程。也就是说只要你在那个系统属性中指定了,那么这个NativeBridge在系统一启动的时候就会被加载进来。

好了,我们接下来看,当要启动一个应用程序的时候,都是从Zygote进程fork出来的,具体的fork动作是在ForkAndSpecializeCommon这个JNI函数来实现的,在这个函数中也相应加入了对NativeBridge的支持代码(代码位于framworks\base\core\jni\com_android_internal_os_Zygote.cpp文件中):

//~/android-6.0.1_r62/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp中:442 // Utility routine to fork zygote and specialize the child process.443 static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,444                                      jint debug_flags, jobjectArray javaRlimits,445                                      jlong permittedCapabilities, jlong effectiveCapabilities,446                                      jint mount_external,447                                      jstring java_se_info, jstring java_se_name,448                                      bool is_system_server, jintArray fdsToClose,449                                      jstring instructionSet, jstring dataDir) {    ...456   pid_t pid = fork();457 458   if (pid == 0) {    ...472     bool use_native_bridge = !is_system_server && (instructionSet != NULL)473         && android::NativeBridgeAvailable();474     if (use_native_bridge) {475       ScopedUtfChars isa_string(env, instructionSet);476       use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());477     }478     if (use_native_bridge && dataDir == NULL) {479       // dataDir should never be null if we need to use a native bridge.480       // In general, dataDir will never be null for normal applications. It can only happen in481       // special cases (for isolated processes which are not associated with any app). These are482       // launched by the framework and should not be emulated anyway.483       use_native_bridge = false;484       ALOGW("Native bridge will not be used because dataDir == NULL.");485     }    ...516     if (use_native_bridge) {517       ScopedUtfChars isa_string(env, instructionSet);518       ScopedUtfChars data_dir(env, dataDir);519       android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());520     }    ...602   }603   return pid;604 }

所以,要正式在新应用程序进程中启用NativeBridge,必须要满足几个条件:
1)不是SystemServer进程(NativeBridge主要是用来解决JNI函数的兼容性问题的,SystemServer是fork自Zygote,但是它属于系统的一部分,肯定是根据所在平台而编译的,因此肯定不需要转指令集)。
2)NativeBridge已经准备好了(代码位于system\core\libnativebridge\native_bridge.cc文件中):

470 bool NativeBridgeAvailable() {471   return state == NativeBridgeState::kOpened472       || state == NativeBridgeState::kPreInitialized473       || state == NativeBridgeState::kInitialized;474 }

前面在android::LoadNativeBridge函数中,在成功加载了NativeBridge之后,会把状态设置成NativeBridgeState::kOpened,因此一般情况下NativeBridge是可用的,这个函数会返回true。

3)真的需要NativeBridge来转指令集(代码位于system\core\libnativebridge\native_bridge.cc文件中):

239 bool NeedsNativeBridge(const char* instruction_set) {240   if (instruction_set == nullptr) {241     ALOGE("Null instruction set in NeedsNativeBridge.");242     return false;243   } 244   return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA) + 1) != 0;245 } 

instruction_set是要运行的应用程序所支持的指令集,而kRuntimeISA是指当前Android系统所运行平台的指令集,它是在编译时就指定好了的(代码位于system\core\libnativebridge\native_bridge.cc文件中):

224 #if defined(__arm__)225 static const char* kRuntimeISA = "arm";226 #elif defined(__aarch64__)227 static const char* kRuntimeISA = "arm64";228 #elif defined(__mips__)229 static const char* kRuntimeISA = "mips";230 #elif defined(__i386__)231 static const char* kRuntimeISA = "x86";232 #elif defined(__x86_64__)233 static const char* kRuntimeISA = "x86_64";234 #else235 static const char* kRuntimeISA = "unknown";236 #endif

什么时候需要NativeBridge呢?当然是这两个指令集不相同的时候了。要在只支持Intel X86指令集的平台上跑只支持ARM指令集的程序,一般情况下是不可能的,NativeBridge就是为了把它变成可能才存在的。
4)有自己的数据目录,一般应用程序是有的,在/data/data/目录下。
如果决定要启用NativeBridge,则还需要调用android::PreInitializeNativeBridge对其做预初始化:

.......

最后,做一个简单的总结:

1)所谓的NativeBridge,其实是Android为了支持JNI函数运行时动态转指令集而加入的一个逻辑抽象层,可以将动态转指令集的库与ART虚拟机的运行逻辑进行解耦。
2)对于虚拟机来说(目前是ART,未来换成别的也可以),需要提供android::NativeBridgeRuntimeCallbacks结构体所定义的三个函数;
3)对于真正实现动态转指令集的库来说,要有一个android::NativeBridgeCallbacks全局结构体变量及提供其定义的5个函数,且这个结构体变量的名字叫做“NativeBridgeItf”;
4)可以通过更改系统属性“ro.dalvik.vm.native.bridge”来打开和关闭动态转指令集的功能,将其设置为0表示关闭,而将其设置成一个有效的动态库的路径表示打开。

原创粉丝点击