1. 程式人生 > >android 呼叫第三方so庫

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 };
最後終於呼叫成功。。。。