Android JNI分析

Android JNI分析

JNI – Java Native Interface
NDK – Native Develope Kit

什么是JNI

JNI(Java Native Interface,Java本地接口),用于打通Java层与Native(C/C++)层。这不是Android系统所独有的,而是Java所有。众所周知,Java语言是跨平台的语言,而这跨平台的背后都是依靠Java虚拟机,虚拟机采用C/C++编写,适配各个系统,通过JNI为上层Java提供各种服务,保证跨平台性。
可以这样理解,是通过HACK JAVA虚拟机(在Andorid平台上是Andorid虚拟机),来达到JAVA调用C或者C++的方法。说HACK有点严重,算是留的后门,但要进行这些操作,得按照“后门”设计的一套规则办。
 

先说说Zygote

1,涉及的文件
  1. frameworks/base/core/jni/AndroidRuntime.cpp
  2. frameworks/base/cmds/app_process/app_main.cpp
  3. frameworks/base/core/jni/android_util_Log.cpp
  4. libnativehelper/include/nativehelper/jni.h
  5. frameworks/base/core/jni/core_jni_helpers.h
2,Zygote的意思是受精卵,是指代Android Java进程的受精卵,是Android JAVA世界的始祖。Zygote本身是一个Native程序,是由init进程启动;
  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
  2. class main
  3. socket zygote stream 660 root system
  4. onrestart write /sys/android_power/request_state wake
  5. onrestart write /sys/power/state on
  6. onrestart restart media
  7. onrestart restart netd
他的执行程序是app_process,该程序的源文件为:frameworks/base/cmds/app_process/app_main.cpp
  1. ...
  2. //参数处理
  3. while (i < argc) {
  4. const char* arg = argv[i++];
  5. if (strcmp(arg, "--zygote") == 0) {
  6. zygote = true;
  7. niceName = ZYGOTE_NICE_NAME;
  8. } else if (strcmp(arg, "--start-system-server") == 0) {
  9. startSystemServer = true;
  10. } else if (strcmp(arg, "--application") == 0) {
  11. application = true;
  12. } else if (strncmp(arg, "--nice-name=", 12) == 0) {
  13. niceName.setTo(arg + 12);
  14. } else if (strncmp(arg, "--", 2) != 0) {
  15. className.setTo(arg);
  16. break;
  17. } else {
  18. --i;
  19. break;
  20. }
  21. }
  22. ...
zygote = true;
startSystemServer=true;
执行runtime.start
  1. ...
  2. if (zygote) {
  3. runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
  4. }
  5. ...
因为runtime是AppRuntime的实例,而AppRuntime为AndroidRuntime的子类,即start是执行AndroiRuntime.cpp的方法
3,进入AndroidRuntime.cpp
start函数主要做了三个事情
第一步,启动虚拟机startVm();
第二步,注册Android的JAVA函数(JNI定义的);
第三步,启动ZygoteInit.java的main函数,通过Socket持续接收上层的应用需求,并不返回。
3.1,startVm():
  1. int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
  2. {
  3. ...
  4. //处理一些prop,来获得一些参数
  5. ...
  6. if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
  7. }
通过JNI_CreateJavaVM()来启动VM,虚拟机另开一章节,这里不展开。
3.2,注册-startReg()
即注册所有Native的方法,参数即为startVm的JNIEnv** pEnv参数
主要是调用regitster_jni_procs来注册gRegJNI定义的Native函数。
  1. if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
  2. env->PopLocalFrame(NULL);
  3. return -1;
  4. }
来看看gRegJNI都有啥
  1. static const RegJNIRec gRegJNI[] = {
  2. REG_JNI(register_com_android_internal_os_RuntimeInit),
  3. REG_JNI(register_android_os_SystemClock),
  4. REG_JNI(register_android_util_EventLog),
  5. ...
  6. }
gRegJNI即为函数指针数组。而register_jni_procs就逐个执行这些函数指针的方法,参数还是为JNIEnv*类型
  1. static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
  2. {
  3. for (size_t i = 0; i < count; i++) {
  4. if (array[i].mProc(env) < 0) {
  5. ...
  6. }
我们来看其中一个的函数指针方法,register_android_util_Log函数
  1. /*
  2. * JNI registration.
  3. */
  4. static const JNINativeMethod gMethods[] = {
  5. /* name, signature, funcPtr */
  6. { "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
  7. { "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
  8. { "logger_entry_max_payload_native", "()I", (void*) android_util_Log_logger_entry_max_payload_native },
  9. };
  10. int register_android_util_Log(JNIEnv* env)
  11. {
  12. jclass clazz = FindClassOrDie(env, "android/util/Log");
  13. levels.verbose = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "VERBOSE", "I"));
  14. levels.debug = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "DEBUG", "I"));
  15. levels.info = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "INFO", "I"));
  16. levels.warn = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "WARN", "I"));
  17. levels.error = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ERROR", "I"));
  18. levels.assert = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ASSERT", "I"));
  19. return RegisterMethodsOrDie(env, "android/util/Log", gMethods, NELEM(gMethods));
  20. }
RegisterMethodsOrDie就实际调用了AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods)方法,这个就跟APK端动态注册JNI方法类似了。
其中JNINativeMethod类型的gMethods数据,分别定义了name(Java中使用),signature(包含函数与返回值),funPtr(Native要实现的函数)。signature即签名的写法有特殊的要求,下一小节说明。
接下来就进入第三步,进入JAVA世界。
4,进入Java世界的钥匙ZygoteInit.java-main()
在start()函数里面进行一些类名和方法名的处理之后,就调到
  1. ...
  2. env->CallStaticVoidMethod(startClass, startMeth, strArray);
  3. ...
在jni.h中,该方法定义如下:
  1. void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
  2. {
  3. ...
  4. functions->CallStaticVoidMethodV(this, clazz, methodID, args);
  5. ...
  6. }
  7. void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
  8. { functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
通过虚拟机,进入JAVA,ZygoteInit.java的main函数
4.1,处理参数
  1. ...
  2. startSystemServer = true;
  3. socketName = "zygote"
  4. ...
4.2,创建Zygote Socket
这个Unix内一个Socket通信,是用于进程间通信,Zygote作为Host端,来处理Client进行的fork JAVA进程的请求。
  1. private static void registerZygoteSocket(String socketName) {
  2. final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
  3. ...
  4. sServerSocket = new LocalServerSocket(fd);
  5. ...
  6. }
会在dev/socket/下面创建一个socket文件zygote
  1. /dev/socket # ls -al zygote
  2. srw-rw---- 1 root system 0 2015-01-01 00:00 zygote
4.3,preload()
通过preload()来加载Java的类和资源文件,其中类文件放在/system/etc/preloaded-classes目录
  1. static void preload() {
  2. Log.d(TAG, "begin preload");
  3. Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
  4. beginIcuCachePinning();
  5. Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  6. Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
  7. preloadClasses();
  8. Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  9. Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
  10. preloadResources();
  11. Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  12. Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
  13. preloadOpenGL();
  14. Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  15. preloadSharedLibraries();
  16. preloadTextResources();
  17. // Ask the WebViewFactory to do any initialization that must run in the zygote process,
  18. // for memory sharing purposes.
  19. WebViewFactory.prepareWebViewInZygote();
  20. endIcuCachePinning();
  21. warmUpJcaProviders();
  22. Log.d(TAG, "end preload");
  23. }
说明:preload_class文件由framework/base/tools/preload工具生成,它需要判断每个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。这方面的内容,读者可参考有关preload工具中的说明,这里就不再赘述。
4.4,umount(有点疑惑TODO)
Zygote.nativeUnmountStorageOnInit();
4.5,启动SystemService进程
  1. private static boolean startSystemServer(String abiList, String socketName){
  2. ...
  3. pid = Zygote.forkSystemServer(
  4. parsedArgs.uid, parsedArgs.gid,
  5. parsedArgs.gids,
  6. parsedArgs.debugFlags,
  7. null,
  8. parsedArgs.permittedCapabilities,
  9. parsedArgs.effectiveCapabilities);
  10. ...
  11. //处理了进程的返回
  12. /* For child process */
  13. if (pid == 0) {
  14. if (hasSecondZygote(abiList)) {
  15. waitForSecondaryZygote(socketName);
  16. }
  17. handleSystemServerProcess(parsedArgs);
  18. }
  19. return true;
SystemService进程就启动了JAVA世界在绝大部分服务了。
4.6,进入死循环runSelectLoop(abiList);
这样,Zygote就循环等待socket的请求,完成他自己的命令。

JNI函数签名格式以及数据类型

JNINativeMethod结构体中有一个字段为signature(签名),再介绍signature格式之前需要掌握各种数据类型在Java层、Native层以及签名所采用的签名格式。
1,基本数据类型

Signature格式 Java Native
B byte jbyte
C char jchar
D double jdouble
F float jfloat
I int jint
S short jshort
J long jlong
Z boolean jboolean
V void void
2 数组数据类型

数组简称则是在前面添加[

Signature格式 Java Native
[B byte[] jbyteArray
[C char[] jcharArray
[D double[] jdoubleArray
[F float[] jfloatArray
[I int[] jintArray
[S short[] jshortArray
[J long[] jlongArray
[Z boolean[] jbooleanArray
3 复杂数据类型

对象类型简称:L+classname +;

Signature格式 Java Native
Ljava/lang/String; String jstring
L+classname +; 所有对象 jobject
[L+classname +; Object[] jobjectArray
Ljava.lang.Class; Class jclass
Ljava.lang.Throwable; Throwable jthrowable
4 Signature

有了前面的铺垫,那么再来通过实例说说函数签名: (输入参数...)返回值参数,这里用到的便是前面介绍的Signature格式。

Java函数 对应的签名
void foo() ()V
float foo(int i) (I)F
long foo(int[] i) ([I)J
double foo(Class c) (Ljava/lang/Class;)D
boolean foo(int[] i,String s) ([ILjava/lang/String;)Z
String foo(int i) (I)Ljava/lang/String;

如何使用JNI调用

在APP开发中使用JNI编程的话,基本的套路就是:
1,封装so库为libxxx_jni.so库,把这些JNI函数注册进系统;
2,在JAVA端, System.loadLibrary(“xxx_jni”);并在该JAVA文件通过public static native 声明Native方法
3,在APP的Android.mk文件中,引用libxxx_jni.so
  1. LOCAL_JNI_SHARED_LIBRARIES := libxxx_jni
  2. LOCAL_REQUIRED_MODULES := libxxx_jni
具体的实现,请向我索取源码示例
在系统中
系统很多类都使用了JNI的途径调到Native函数去了,但并没有在JAVA中的每个类System.loadLibrary()来加载相应的jni库,那系统在哪loadLibrary的呢?
总的来说,系统的大部分JNI函数的注册是在frameworks/base/core/jni中进行的。
frameworks/base/core/jni/Android.mk声明,把这些生成libandroid_runtime
  1. LOCAL_MODULE:= libandroid_runtime
而libandroid_runtime 又被frameworks/base/services/core/jni/Android.mk引用
  1. ...
  2. LOCAL_SHARED_LIBRARIES += \
  3. libandroid_runtime \
  4. ...
而这个Android.mk被frameworks/base/services/Android.mk引用,最后生成libandroid_servers.so
  1. ...
  2. # include all the jni subdirs to collect their sources
  3. include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
  4. ...
  5. LOCAL_MODULE:= libandroid_servers
  6. include $(BUILD_SHARED_LIBRARY)
libandroid_servers.so在SystemServer.java 执行loadLibrary()了,即,这就是入口
  1. ...
  2. // Initialize native services.
  3. System.loadLibrary("android_servers");
  4. ...
综上,算是殊途同归哪。
参考:
1,zygote:innost《深入理解 Android 卷I》
Comments are closed.
TOP