jni在libandroid_runtime.so 引出的诡异问题

背景:
在libandroid_runtime.so添加了一个jni 方法, 在底层消息过来时, 此jni方法会回调java 的callback 方法,将数据吐给java.
问题:
 jni回调到java, 但没有执行java的相关代码, 然后线程跳到JN继续执行, log中也没有线程的相关异常, 各个线程也正常运行着, 但相关代码就是没有执行
代码:

code for jni

static void android_os_nng_startListener(JNIEnv* env, jclass clazz){
    while(true){
        mQueue.waitForItems();
        std::vector<Message*> vec = mQueue.flush();
        ALOGE(code, "mQueue flush  ");
        for(Message* msg : vec){
            switch(msg->type){
                case NNG_MSG_DATA:
                    {
                        ALOGE(code, "mQueue flush  DATA");
                        int len = nng_msg_len(msg->msg);
                        jbyteArray jarray = env->NewByteArray(len);

                        ALOGE(code, "mQueue flush  DATA len: %d" , len);
                        env->SetByteArrayRegion(jarray, 0, len, static_cast<jbyte*>(nng_msg_body(msg->msg)));
                        env->CallVoidMethod(gNngOffsets.obj, gNngOffsets.callback,  jarray);
                        ALOGE(code, "mQueue flush  DATA len end: %d" , len);
                        nng_msg_free(msg->msg);
                        free(msg);
                    }
                    break;
                case NNG_MSG_STATUS:
                        ALOGE(code, "mQueue flush  status");
                    env->CallVoidMethod(gNngOffsets.obj, gNngOffsets.reportStatus,  msg->nng_pipe_ev, msg->port);
                    break;
            }
        }
    }
}

code for java

  public final void onCallback(byte[] bytes){
        Log.d(TAG,  "onCallback");
        String topic = new String(bytes, 0, 11);
        int topicID = Integer.valueOf(topic);
        MsgCallBack obj;
        lock.lock();
        try{
            obj = arr.get(topicID);
        }finally{
             lock.unlock();
        }
        Log.d(TAG,  "onCallback: " + obj.toString());
        obj.onCallback(obj.cast(bytes));
    }
最后现象为:
4231  4296 E jni_nng : mQueue flush  
4231  4296 E jni_nng : mQueue flush  DATA
4231  4296 E jni_nng : mQueue flush  DATA len: 95
4231  4296 D Nng     : onCallback

4231  4296 E jni_nng : mQueue flush  
4231  4296 E jni_nng : mQueue flush  DATA
4231  4296 E jni_nng : mQueue flush  DATA len: 95
4231  4296 D Nng     : onCallback

可以看到, jni回调到java, 打印了一行log, 然后并没有往下走, 而是回到jni 等待下一个消息到来.

分析:

为什么java 后续没有执行? 是onCallback方法内有异常, 然后异常没有在log里面体现?

    措施: 在onCallback 中一行行添加log
    结果: 还是只打印在onCallback 入口log

是bytes有问题?
    措施: 在onCallback中添加log,打印 bytes的信息
    结果: 还是只打印onCallback入口处log
        难道是没有替换成功?
            措施: 排查替换jar包, 在次替换, 修改入口类log....
            结果: 确认替换成功了, 就是只能打印入口出log, 或无log输出.
    

是jni构建的bytes有问题,在java中无法正确的访问?
    措施: 用类似的代码, 写一个demo app, 在板子上试试
    结果: 经过验证没有问题, 可以正常运行.

jdb 调试一下试试:

    措施: jdb attach 到4231 进程, 在 onCallback 方法内打上断点
    结果: 程序运行到onCallback处, 直接和调试工具断开, 并且程序crash.

没有头绪, 进行整体一个try catch 试试:

    修改:
        public final void onCallback(byte[] bytes){
            try{
                ......
            }catch(Exception e){
                e.printStackTrace();
            }
        }
结果:

的确存在异常,相关log 也能打印出来了:

4796  4843 E jni_nng : mQueue flush
4796  4843 E jni_nng : mQueue flush  DATA
4796  4843 E jni_nng : mQueue flush  DATA len: 95
4796  4843 D Nng     : onCallback
4796  4843 D Nng     : onCallback: com.android.car.hal.HalIPCPowerClient$IPCPowerInfoCallback@5a2fcd9
4796  4843 W System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[])' on a null object reference
4796  4843 W System.err:     at android.os.MsgCallBack.cast(MsgCallBack.java:60)
4796  4843 W System.err:     at android.os.Nng.onCallback(Nng.java:261)
4796  4843 W System.err:     at android.os.Nng.startListener(Native Method)
4796  4843 W System.err:     at android.os.Nng.access$000(Nng.java:37)
4796  4843 W System.err:     at android.os.Nng$1.run(Nng.java:92)

可以看到 onCallback 中是存在一个null指针的, 修改此 null 指针, 问题修复, 一切正常.

遗留问题:
    1.进程发生 excetpion 的原理是什么?
    2.为什么trycatch之前此异常没有抛出, 依然正常往下运行了呢?
excetpion 原理:
  1. 写一个demo app, 抛出一个exception
        public final void  onCallback(byte[] bytes){
            throw new NullPointerException("test");
        }

2.查看对应的dex文件:

.method public final onCallback([B)V
        .registers 4
00000000  new-instance        v0, NullPointerException
00000004  const-string        v1, "test"
00000008  invoke-direct       NullPointerException-><init>(String)V, v0, v1
0000000E  throw               v0
.end method

可以看到是throw 关键字抛出的异常

throw关键字的实现为:

art/runtime/entrypoints/quick/quick_throw_entrypoints.cc
// Called by generated code to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ScopedQuickEntrypointChecks sqec(self);
  // We come from an explicit check in the generated code. This path is triggered
  // only if the object is indeed null.
  ThrowNullPointerExceptionFromDexPC(/* check_address= */ false, 0U);
  self->QuickDeliverException();
}

// Installed by a signal handler to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromSignal(uintptr_t addr, Thread* self)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ScopedQuickEntrypointChecks sqec(self);
  ThrowNullPointerExceptionFromDexPC(/* check_address= */ true, addr);
  self->QuickDeliverException();
}

由此可见throw 抛出的异常会,在QuickDeliverException 内处理

art/runtime/thread.cc
void Thread::QuickDeliverException() {
    //获取线程异常
    ObjPtr<mirror::Throwable> exception = GetException();
    //查找对应的trycatch 模块,并设置线程内的异常不可见.
    ClearException();
    QuickExceptionHandler exception_handler(this, false); //创建 exception handler
    exception_handler.FindCatch(exception);  // 查找 是否存在对应的try catch, (编译try catch会在字节码中,生成一个异常表格, 此表格会加载到art, 以此为根据查找)
    //根据设置的 HandlerQuickFramePc 跳转到对应的trycatch 模块
    exception_handler.DoLongJump();
}

遍历调用栈, 查找符合的catch模块

art/runtime/quick_exception_handler.cc
void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
  instrumentation::InstrumentationStackPopper popper(self_);
  MutableHandle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
  do {
        // Walk the stack to find catch handler.
        CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this,  /*skip_frames=*/already_popped);
        visitor.WalkStack(true); //遍历整个调用栈 查找 catchHandler 或者 遍历到栈底结束
        popped_to_top = popper.PopFramesTo(instrumentation_frames_to_pop, exception_ref);
  } while (!popped_to_top);
}

遍历函数调用栈
art/runtime/stack.cc

void StackVisitor::WalkStack(bool include_transitions) {
    for(***){
        bool should_continue = VisitFrame(); //遍历当前栈, 找到catchHandler 或者 当前Frame指向nattive, 返回false, 否则都返回true
        if (UNLIKELY(!should_continue)) {
          return;
        }
       QuickMethodFrameInfo frame_info = GetCurrentQuickFrameInfo();
        if (context_ != nullptr) {
          context_->FillCalleeSaves(reinterpret_cast<uint8_t*>(cur_quick_frame_), frame_info);
        }
        //计算下一个栈的pc
        size_t frame_size = frame_info.FrameSizeInBytes();
        size_t return_pc_offset = frame_size - sizeof(void*);
        uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset;
        uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
        cur_quick_frame_pc_ = return_pc;
    }
}

分析当前栈的情况

art/runtime/quick_exception_handler.cc
  bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
    ArtMethod* method = GetMethod();
    exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
    if (method == nullptr) {
      //如果遍历到了栈底, 将HandlerQuickFramePc 设置为当前栈的pc
      exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
      exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
      exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
      uint32_t next_dex_pc;
      ArtMethod* next_art_method;
      bool has_next = GetNextMethodAndDexPc(&next_art_method, &next_dex_pc);
      // Report the method that did the down call as the handler.
      exception_handler_->SetHandlerDexPc(next_dex_pc);
      exception_handler_->SetHandlerMethod(next_art_method);
      return false;  // End stack walk.
    }
    return HandleTryItems(method);
  }
 private:
  bool HandleTryItems(ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    uint32_t dex_pc = dex::kDexNoIndex;
    if (!method->IsNative()) {
      dex_pc = GetDexPc();
    }
    if (dex_pc != dex::kDexNoIndex) {
      bool clear_exception = false;
      StackHandleScope<1> hs(GetThread());
      Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass()));
      //进行匹配查找,看是否有对应的 catch handler
      uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
      exception_handler_->SetClearException(clear_exception);
      if (found_dex_pc != dex::kDexNoIndex) {
        //如果存在对应的catch handler, 这设置到 HandlerQuickFramePc 中
        exception_handler_->SetHandlerMethod(method);
        exception_handler_->SetHandlerDexPc(found_dex_pc);
        exception_handler_->SetHandlerQuickFramePc(
            GetCurrentOatQuickMethodHeader()->ToNativeQuickPc(
                method, found_dex_pc, /* is_for_catch_handler= */ true));
        exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
        exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
        return false;
      } else if (UNLIKELY(GetThread()->HasDebuggerShadowFrames())) {
        // We are going to unwind this frame. Did we prepare a shadow frame for debugging?
        size_t frame_id = GetFrameId();
        ShadowFrame* frame = GetThread()->FindDebuggerShadowFrame(frame_id);
        if (frame != nullptr) {
          // We will not execute this shadow frame so we can safely deallocate it.
          GetThread()->RemoveDebuggerShadowFrameMapping(frame_id);
          ShadowFrame::DeleteDeoptimizedFrame(frame);
        }
      }
    }
    return true;  // Continue stack walk.
  }
非throw 产生的异常

先由art接收到中断的signal信号,然后将信号交由对应handler处理,之后就流程类似了

//art/runtime/arch/arm64/fault_handler_arm64.cc
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
  struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
  struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
  sc->sp -= sizeof(uintptr_t);
  *reinterpret_cast<uintptr_t*>(sc->sp) = sc->pc + 4;
  sc->regs[30] = reinterpret_cast<uintptr_t>(info->si_addr);
  sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
  return true;
}
ENTRY art_quick_throw_null_pointer_exception_from_signal

    .cfi_def_cfa_offset __SIZEOF_POINTER__
    .cfi_rel_offset lr, 0
    INCREASE_FRAME (FRAME_SIZE_SAVE_EVERYTHING - __SIZEOF_POINTER__)
    SAVE_REG x29, (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__)  // LR already saved.
    SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR
    mov x0, lr                        // pass the fault address stored in LR by the fault handler.
    mov x1, xSELF                     // pass Thread::Current.
    bl  artThrowNullPointerExceptionFromSignal  // (arg, Thread*).
    brk 0
END art_quick_throw_null_pointer_exception_from_signal
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ScopedQuickEntrypointChecks sqec(self);
  ThrowNullPointerExceptionFromDexPC(/* check_address= */ false, 0U);
  self->QuickDeliverException();
}
结合exception 的处理流程分析

本次的问题, 是jni call java发生的问题,
art在遍历调用栈时,遍历到jni 栈就结束了, HandlerQuickFramePc 就指回了jni方法,
所以就陷入了个循环
在这里插入图片描述

后续分析

那是不是,jni 都是如此?
写demo验证,

        new Thread(){
            @Override
            public void run() {
                test(MainActivity.this "test".getBytes());
            }
        }.start();
    public final void  onCallback(byte[] bytes){
        throw new NullPointerException("test");
    }
static jint test(JNIEnv *env, jobject thiz, jobject obj, jbyteArray data) {
    env= nullptr;
    int len = env->GetArrayLength(data);
    jbyte* arr = new jbyte[len+1];
    memset(arr, 0, len+1);
    jbyteArray jarray = env->NewByteArray(len);
    env->SetByteArrayRegion(jarray, 0, len , arr);
    env->CallVoidMethod(obj, gNngOffsets.callback,  jarray); //第一次   
    env->CallVoidMethod(obj, gNngOffsets.callback,  jarray); //第二次
    free(arr);
    return result;
}
问题现象:
第一次回调onCallback 并没有 发生问题
第二次回调onCallback发生问题 ,将第一次的NullPointerException 打印了出来,

堆栈为:

03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #00 pc 00000000003cff38  /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*)+208)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #01 pc 00000000004a2980  /system/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+348)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #02 pc 00000000002ffffc  /system/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1052)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #03 pc 000000000030040c  /system/lib64/libart.so (art::JavaVMExt::JniAbortV(char const*, char const*, std::__va_list)+116)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #04 pc 000000000010f0ec  /system/lib64/libart.so (art::ScopedCheck::AbortF(char const*, ...)+152)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #05 pc 000000000010ec30  /system/lib64/libart.so (art::ScopedCheck::CheckThread(_JNIEnv*)+516)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #06 pc 000000000010d0c4  /system/lib64/libart.so (art::ScopedCheck::Check(art::ScopedObjectAccess&, bool, char const*, art::JniValueType*)+648)
03-22 17:10:11.054  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #07 pc 00000000001125ec  /system/lib64/libart.so (art::CheckJNI::CheckCallArgs(art::ScopedObjectAccess&, art::ScopedCheck&, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, art::InvokeType, art::VarArgs const*)+132)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #08 pc 0000000000111468  /system/lib64/libart.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType)+668)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #09 pc 00000000000ffd34  /system/lib64/libart.so (art::CheckJNI::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+92)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #10 pc 00000000000158e0  /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib/arm64/libnative-lib.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...)+208)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #11 pc 00000000000156c0  /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib/arm64/libnative-lib.so (???)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   native: #12 pc 00000000000006c8  /data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/oat/arm64/base.odex (Java_com_example_nng_MainActivity_add__Lcom_example_nng_MainActivity_2II_3B+216)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   at com.example.nng.MainActivity.add(Native method)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534]   at com.example.nng.MainActivity$2.run(MainActivity.java:167)
03-22 17:10:11.055  8535  8592 F zygote64: java_vm_ext.cc:534] 
分析
第一次回调, jump 到了jni, 异常没有被抛出来,  存在于当前Thread
第二次回调, checkjni,  check 当前Thread, 发现异常,主动abort 当前进程
疑问:
那为什么 demo 会报问题, libandroid_runtime.so不会呢, objdump    libandroid_runtime.so库
00000000000f42f4 <_ZN7_JNIEnv14CallVoidMethodEP8_jobjectP10_jmethodIDz>:
   f42f4:   d10483ff    sub sp, sp, #0x120
   f42f8:   a9104ffc    stp x28, x19, [sp,#256]
   f42fc:   a9117bfd    stp x29, x30, [sp,#272]
   f4300:   910443fd    add x29, sp, #0x110
   f4304:   ad031fe6    stp q6, q7, [sp,#96]
   f4308:   ad0217e4    stp q4, q5, [sp,#64]
   f430c:   ad010fe2    stp q2, q3, [sp,#32]
   f4310:   ad0007e0    stp q0, q1, [sp]
   f4314:   a90a1fe6    stp x6, x7, [sp,#160]
   f4318:   a90917e4    stp x4, x5, [sp,#144]
   f431c:   f90047e3    str x3, [sp,#136]
   f4320:   d53bd053    mrs x19, tpidr_el0
   f4324:   f940166b    ldr x11, [x19,#40]
   f4328:   928004e8    mov x8, #0xffffffffffffffd8     // #-40
   f432c:   910003e9    mov x9, sp
   f4330:   910223ea    add x10, sp, #0x88
   f4334:   f2dff008    movk    x8, #0xff80, lsl #32
   f4338:   910043ac    add x12, x29, #0x10
   f433c:   91020129    add x9, x9, #0x80
   f4340:   9100a14a    add x10, x10, #0x28
   f4344:   f81e83ab    stur    x11, [x29,#-24]
   f4348:   a93da3a9    stp x9, x8, [x29,#-40]
   f434c:   a93cabac    stp x12, x10, [x29,#-56]
   f4350:   f9400008    ldr x8, [x0]
   f4354:   d100e3a9    sub x9, x29, #0x38
   f4358:   ad400121    ldp q1, q0, [x9]
   f435c:   d10183a3    sub x3, x29, #0x60
   f4360:   f940f908    ldr x8, [x8,#496]
   f4364:   ad3d03a1    stp q1, q0, [x29,#-96]
   f4368:   d63f0100    blr x8
   f436c:   f9401668    ldr x8, [x19,#40]
   f4370:   f85e83a9    ldur    x9, [x29,#-24]
   f4374:   eb09011f    cmp x8, x9
   f4378:   540000a1    b.ne    f438c <_ZN7_JNIEnv14CallVoidMethodEP8_jobjectP10_jmethodIDz+0x98>
   f437c:   a9517bfd    ldp x29, x30, [sp,#272]
   f4380:   a9504ffc    ldp x28, x19, [sp,#256]
   f4384:   910483ff    add sp, sp, #0x120
   f4388:   d65f03c0    ret
   f438c:   94049bad    bl  21b240 <__stack_chk_fail@plt>

看出是什么,但感觉不是走的checkjni流程走的是jni_internal 流程, 在art/runtime/jni/jni_env_ext.cc 中 添加log:

const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
  const JNINativeInterface* override = JNIEnvExt::table_override_;
  if (override != nullptr) {
    return override;
  }
  LOG(ERROR)<<"check jni is " << check_jni;
  return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
}

抓取log:

03-16 04:14:11.190  4492  4500 E CarPowerServic: check jni is 0
03-16 04:14:11.191  4492  4501 E CarPowerServic: check jni is 0
03-16 04:14:11.192  4492  4502 E CarPowerServic: check jni is 0
03-16 04:14:11.193  4492  4503 E CarPowerServic: check jni is 0
03-16 04:14:11.195  4492  4504 E CarPowerServic: check jni is 0
03-16 04:14:11.196  4492  4505 E CarPowerServic: check jni is 0
03-16 04:14:11.199  4492  4506 E CarPowerServic: check jni is 0
03-16 04:14:11.199  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.200  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.206  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.223  4492  4511 E CarPowerServic: check jni is 0
03-16 04:14:11.230  4492  4513 E CarPowerServic: check jni is 0
03-16 04:14:11.409  4492  4519 E CarPowerServic: check jni is 0
03-16 04:14:11.435  4492  4520 E CarPowerServic: check jni is 0
03-16 04:14:11.437  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.455  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.456  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.480  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.480  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.481  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.488  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.491  1535  1535 E com.android.ca: check jni is 0
03-16 04:14:11.697  4492  4492 E CarPowerServic: check jni is 0
03-16 04:14:11.704  4492  4492 E CarPowerServic: check jni is 0

果然发生问题的相关进程和线程check_jni 是关闭.

那为什么 check_jni 一个是打开的一个是关闭的呢?

梳理check_jni的设置流程:

第一次设置check_jni的位置
private final boolean startProcessLocked(......) {
    if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
        runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
        runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
        runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
    }
    ......
}
.....
/frameworks/base/core/java/android/os/Process.java
ProcessStartResult-->star(... runtimeFlags ...)
   ZygoteProcess--->start(... runtimeFlags ...)
       -->startViaZygote(...runtimeFlags....)
          ZygoteConnection--->processOneCommand
              Zygote--->forkAndSpecialize() -->nativeForkAndSpecialize
                   com_android_internal_os_Zygote-->com_android_internal_os_Zygote_nativeForkAndSpecialize() ---->  ForkAndSpecializeCommon()                   
                        Zygote-->callPostForkChildHooks() --->
                           ZygoteHooks -->postForkChild()---> nativePostForkChild()
dalvik_system_ZygoteHooks.cc
ZygoteHooks_nativePostForkChild(){
        EnableDebugFeatures(runtime_flags);
}
static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
   Runtime* const runtime = Runtime::Current();
   if ((runtime_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
         JavaVMExt* vm = runtime->GetJavaVM();
 		if (!vm->IsCheckJniEnabled()) {
 				vm->SetCheckJniEnabled(true);
 				Thread::Current()->GetJniEnv()->SetCheckJniEnabled(true);
 		} else {
 			LOG(INFO) << "Not late-enabling -Xcheck:jni (already on)";
 		}
runtime_flags &= ~DEBUG_ENABLE_CHECKJNI;
}
第二次设置check_jni的位置
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote){
	bool checkJni = false;
	property_get("dalvik.vm.checkjni", propBuf, "");
	if (strcmp(propBuf, "true") == 0) {
    	checkJni = true;
	} else if (strcmp(propBuf, "false") != 0) {
	    property_get("ro.kernel.android.checkjni", propBuf, "");
    	if (propBuf[0] == '1') {
     	   checkJni = true;
    	}
	}
	ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
	if (checkJni) {
    	addOption("-Xcheck:jni");
	}
}

从以上流程可以看出 和 check_jni有关系的是:

  • ro.kernel.android.checkjni
  • dalvik.vm.checkjni
  • ApplicationInfo.FLAG_DEBUGGABLE
    这三个元素,

adb 中查看property:

  • ro.kernel.android.checkjni
  • dalvik.vm.checkjni
    实际山都没有配置, 查看第三个元素:
我的app 携带的 flag
Packages:
  Package [com.android.car] (be22749):
    userId=1000
    sharedUser=SharedUserSetting{e2f33e5 android.uid.system/1000}
    pkg=Package{4070eba com.android.car}
    codePath=/system/priv-app/CarService
    resourcePath=/system/priv-app/CarService
    legacyNativeLibraryDir=/system/priv-app/CarService/lib
    primaryCpuAbi=null
    secondaryCpuAbi=null
    versionCode=29 minSdk=29 targetSdk=29
    versionName=10
    splits=[base]
    apkSigningVersion=3
    applicationInfo=ApplicationInfo{b5a216b com.android.car}
    flags=[ SYSTEM HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA ]
........
demo app 携带的flag
Packages:
  Package [com.example.nng] (69c4123):
    userId=10014
    pkg=Package{fea81a3 com.example.nng}
    codePath=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==
    resourcePath=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==
    legacyNativeLibraryDir=/data/app/com.example.nng-j5W1OjvWTc5rZIOCCL-xGg==/lib
    primaryCpuAbi=arm64-v8a
    secondaryCpuAbi=null
    versionCode=1 minSdk=16 targetSdk=29
    versionName=1.0
    splits=[base]
    apkSigningVersion=2
    applicationInfo=ApplicationInfo{11883f4 com.example.nng}
    flags=[ DEBUGGABLE HAS_CODE ALLOW_CLEAR_USER_DATA TEST_ONLY ALLOW_BACKUP ]

果然 demo app 是携带了DEBUGGABLE 的flag, 所以jni 能识别到异常

在简单验证一下, 将app改为debug 签名此flag不在存在

推荐博客:
https://blog.csdn.net/hl09083253cy/article/details/78590666

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值