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表示关闭,而将其设置成一个有效的动态库的路径表示打开。
- Android6.0中的NativeBridge
- Android6.0中的权限
- Android6.0中的Handler消息模型
- [RK3288][Android6.0] Display中的Composition机制
- Android6.0 SystemUI中的Notification流程
- [RK3288][Android6.0] Android中的Properties
- [RK3288][Android6.0] WiFi在Setting中的开启过程小结
- [RK3288][Android6.0] Audio中的HW Params设置流程
- [RK3288][Android6.0] Display中的2D Rendering机制
- [RK3288][Android6.0] ALSA中的DMA buffer分配
- [RK3288][Android6.0] Audio中的period_size使用规则
- [RK3288][Android6.0] Audio中的录音重采样小结
- [RK3288][Android6.0] Audio中的放音重采样小结
- [RK3288][Android6.0] Audio中的混音过程小结
- [RK3288][Android6.0] DeviceTree中的status属性使用规则
- [RK3288][Android6.0] 开发者选项中的Strict mode功能
- [RK3288][Android6.0] Skia中的编解码小结
- RK3288][Android6.0] ALSA dapm框架中的widget/path/route
- JAVA 微信公众号调用摄像头并上传图片至服务器
- ★ Eclipse Debug 界面应用详解——Eclipse Debug不为人知的秘密
- windows64位下安装TensorFlow(cpu版本)
- 【Arcgis for android】相关教程收集自网络
- 哲学家就餐问题Java版解决案
- Android6.0中的NativeBridge
- Spring+MyBatis问题集锦2
- 设计模式之解释器模式(Interpreter)
- try-catch语句中return和finally执行顺序详解
- 从人类偏好进行深度强化学习(二)
- ffmpeg 推送sdp到darwin
- iOS与JS交互(全)
- 【思维 && 构造】CodeForces
- SQL不走索引的情况