android 调用第三方so库

首先要知道这个第三方的so库是不是按jni标准写的,如果是那就简单了,直接写个native调用就行了。如果不是那就比较麻烦了,必须要把这个so库里面的函数封装一下在调用,下面进入正题。

假设这个库放在/system/vendor/lib下面,名字为libtest.so,我们要调用的方法是get_status,我分别列出在第三方的app和framewok怎样调用。

首先是在第三方app的调用,这里的核心就是使用dlopen函数,然后使用dlsym找到这个函数,最后在调用这个函数。这还是公司的一个前辈教我的,真心不懂c.....

//定义LOGE打印
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
//定义一个指针函数,我这里没有传参数,如果需要的话可以在这里加参数
typedef int (*CAC_FUNC)();
//定义一个so库的路径
#define LIB_CACULATE_PATH "/system/vendor/lib/libtest.so"
Java_com_flycom_jnitest_MainActivity_intFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    LOGD("cac_func: %s","Java_com_flycom_jnitest_MainActivity_intFromJNI");
    void *handle;
    char *error;
    int r;
    CAC_FUNC cac_func = NULL;
    handle=dlopen(LIB_CACULATE_PATH,RTLD_LAZY);

    LOGD("cac_func: %s","handle");
    if (!handle) {
        LOGD("cac_func2: %s","EXIT_FAILURE");
        exit(EXIT_FAILURE);
    }
    //清除之前存在的错误
    dlerror();
    //获取一个函数
    LOGD("cac_func2: %s","get_status");
    *(void **) (&cac_func) = dlsym(handle, "get_status");
   
    if ((error = dlerror()) != NULL)  {
        LOGD("cac_func: %s","error");
        exit(EXIT_FAILURE);
    }
   //调用这个函数
    r =  (*cac_func)();

    LOGD("cac_func: %d\n", r);
    //关闭动态链接库
    dlclose(handle);
    return r;

}

然后用System.loadLibrary加载自己的这个库,写个native调用就行了。

 static {
        System.loadLibrary("native-lib");
 }
 public native int intFromJNI();

下面来说说遇到的坑

在调用时出现了这个错误,网上有大神解决了,主要是因为没有权限。

E/linker: library "/system/vendor/lib/libkphproxy.so" ("/vendor/lib/libkphproxy.so") needed or dlopened by "/data/app/com.flycom.jnitest-mKVoy6YOwxW_DNa_RgRhMQ==/lib/arm/libnative-lib.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/com.flycom.jnitest-mKVoy6YOwxW_DNa_RgRhMQ==/lib/arm:/data/app/com.flycom.jnitest-mKVoy6YOwxW_DNa_RgRhMQ==/base.apk!/lib/armeabi-v7a", permitted_paths="/data:/mnt/expand:/data/data/com.flycom.jnitest"]

在framework/vendor/etc目录下的public.libraries.txt,从这个文件的名字就可以大概知道其中的意义了如果没有的话可以新建一个,把你调用的so库名字加进去,注意必须是全名,例如我的libtest.so。在system/etc文件夹下面也有这个文件,当然是针对system/lib的so库,如果你想访问system/lib的so库请在这里添加。

接下来说下在framework层的添加,例如我想加在com_android_server_SystemServer.cpp

jni的调用是一样的,

static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) {
    using ::android::frameworks::schedulerservice::V1_0::ISchedulingPolicyService;
    using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
    using ::android::frameworks::sensorservice::V1_0::ISensorManager;
    using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager;
    using ::android::hardware::configureRpcThreadpool;

    status_t err;

    configureRpcThreadpool(5, false /* callerWillJoin */);

    JavaVM *vm;
    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Cannot get Java VM");

    sp<ISensorManager> sensorService = new SensorManager(vm);
    err = sensorService->registerAsService();
    ALOGE_IF(err != OK, "Cannot register %s: %d", ISensorManager::descriptor, err);

    sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService();
    err = schedulingService->registerAsService();
    ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
}
//这里的名字随便取
static jint android_server_SystemServer_verifyStatus(JNIEnv*, jobject /* clazz */) {
	LOGD("cac_func2: %s","Java_com_flycom_jnitest_MainActivity_intFromJNI");
    
  LOGD("cac_func: %s","Java_com_flycom_jnitest_MainActivity_intFromJNI");
    void *handle;
    char *error;
    int r;
    CAC_FUNC cac_func = NULL;
    handle=dlopen(LIB_CACULATE_PATH,RTLD_LAZY);

    LOGD("cac_func: %s","handle");
    if (!handle) {
        LOGD("cac_func2: %s","EXIT_FAILURE");
        exit(EXIT_FAILURE);
    }
    //清除之前存在的错误
    dlerror();
    //获取一个函数
    LOGD("cac_func2: %s","get_status");
    *(void **) (&cac_func) = dlsym(handle, "get_status");
   
    if ((error = dlerror()) != NULL)  {
        LOGD("cac_func: %s","error");
        exit(EXIT_FAILURE);
    }
   //调用这个函数
    r =  (*cac_func)();

    LOGD("cac_func: %d\n", r);
    //关闭动态链接库
    dlclose(handle);
    return r;
}

这里遇到的坑就比较多了,注意函数一定要注册,否则找不到方法,第一个参数跟第三个参数好理解,关键的第二个参数"()I",这个括号里面是传入的参数,我的方法没有返回值,所以是空的,后面的“I”代表返回类型是int,这个可以在网上找到。

/*
 * JNI registration.
 */
static const JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService },
    { "startHidlServices", "()V", (void*) android_server_SystemServer_startHidlServices },
	{ "verifyStatus", "()I", (void*) android_server_SystemServer_verifyStatus },
};

在这里调用是不需要在public.libraries.txt给权限的。然后有一个更奇葩的问题,就是我在framewoke层调用同样的函数,返回结果竟然跟app的结果不一样,这个问题困扰了我一天。最后问了提供so库的商家,才发现是权限的问题发火

在app中调用已经加了权限,而在system_server中没加权限,在device/mediatek/sepolicy/bsp/non_plat/system_server.te文件中加入访问的权限

allow system_server tkcore_admin_device:chr_file  { ioctl read write open };
最后终于调用成功。。。。


猜你喜欢

转载自blog.csdn.net/qq_32072451/article/details/80945561