一個android 的HAL示例中遇到的坑。

qqssbb123發表於2018-12-27

想在手機上做一些功能的擴充套件,本來是想從驅動層入手的,但驅動需要改核心。因為,1,手機自帶的核心是沒有原始碼的,沒有其中的symbal的支援,自編的核心模組是不能在其下用的。2,自編全部核心因為沒有電路的硬體資訊,基本上沒可能了。所以只能從HAL層去入手了。並且因為MTK的不開源性,從HAL層入手,對MTK手機更適當一些。

這樣就要先去了解一下HAL吧。

從示例入手最快。網上找到一個mokoid的示例。這個是個教學示例。https://code.google.com/archive/p/mokoid/ google上的示例下了問題很多,要編譯的.mk檔案都少。github上找了一個,https://github.com/kangear/mokoid-read-only 這個git clone下來,要編譯通的話,要改一些地方。

1,目錄結構改一下,{android-source}/mokoid/trunk/{https://github.com/kangear/mokoid-read-only所見目錄}.這是因為其下的檔案中對相互引用時。當然改原始碼也可以。

2,先要android 原始碼編譯過。android原始碼我下的是4.4.4 r2。因為真機是這個。這個需要oracle jkd.用openjdk通不過。oracle jkd需要的是1.6的版本,ubuntu的apt已不提供下載,自已找個地方下載bin包,解壓。在build目錄下,生成envjava.sh如下。

#oracle java 
export JAVA_HOME=/media/ququ/android/tools/jdk1.6.0_45 #解壓的JDK目錄。
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin

進入原始碼目錄要編譯前,

source ./build/envjava.sh

source ./build/envsetup.sh

3,進入mokoid/trunk

mmm ./

會有一些報錯。依提示不難解決。主要是因不編譯環境不同,有些目錄找不到,改一原始碼就可。可以單獨進入子目錄,用mmm ./去單獨編譯。

4,如上的都不是坑,很快就能過了。直的坑在下到真機去執行。編譯時有提示輸出的內容,這些就查要下載到手機中的。輸出的內容主要在android-source/out/target/product/generic/system/下的目錄去找。

5,android-source/out/target/product/generic/system/app 下,

adb install LedClient.apk 

adb uninstall com.mokoid.LedClient

install時會報錯,因為lib找不到。

6,adp push /out/target/product/generic/system/lib/libmokoid_runtime.so  /system/lib

adp push /out/target/product/generic/system/lib/hw/led.default.so  /system/lib/hw

adp push /out/target/product/generic/system/framework/mokoid.jar /system/framework

adp push /out/target/product/generic/system/framework/mokoid.odex /system/framework

這個放到對應的目錄,後看一下許可權,改成與目錄中其它內容一樣的許可權就可。有時下載下去的會是root只讀的。

7,如下做了adb install LedClient.apk還是會報錯。    

<uses-library android:name="com.mokoid.server" />
        <!--uses-library android:required="false" android:name="com.mokoid.server" /-->

改工程下的配置檔案能讓它安裝,但不解決問題。

adb push mokoid/trunk/frameworks/base/service/com.mokoid.server.xml  /system/etc/permissions 

注意一下許可權。這個改對了,讓面的不要改就可以安裝通過。

8,全部的內容已放到真機了,可以執行了。

am start com.mokoid.LedClient/com.mokoid.LedClient.LedClient

會閃退的。用adb logcat >1.txt 看一下log.可以發現,JNI_OnLoad LED這個資訊就出現Fatal signal 11 (SIGSEGV) at 0x655c9e88 (code=2), thread 2458 (okoid.LedClient)。

JNI_OnLoad LED這個資訊是com_mokoid_server_LedService.cpp中的。再進一步是在mokoid_init中的hw_get_module中出錯的。hw_get_module在原始碼目錄hardware/libhardware/hardware.c中定義。但改這裡是沒有做用的,因為它生成的lib民真機中的不一樣。但可以看一下去找問題。我定位到是載入led.default.so時出錯。具體原因這時因為不能改libhardware就不好定位了。

這時自已寫個個可執行的程式碼,做libhardware的工作去載入這個程式碼。


#define LOG_TAG "Mokoid"
//#include "utils/Log.h"

//#include <stdlib.h>
//#include <string.h>
//#include <unistd.h>
//#include <assert.h>

//#include <jni.h>
//#include <mokoid/led.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "../hardware/modules/include/mokoid/led.h"
#define LIB_CACULATE_PATH "/system/lib/hw/led.default.so"
//#define LIB_CACULATE_PATH "/system/lib/libhardware.so"
#define ALOGE printf

typedef int (*CAC_FUNC)(void *, int);

int main(int argc, char **argv)
{
    int status;
    void *handle;
    const char *error;
    struct hw_module_t *hmi;
    struct hw_module_t **pHmi;
    const char *id="led";
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
CAC_FUNC cac_func = NULL;

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    ALOGE("dlopen ..\n");
    handle = dlopen(LIB_CACULATE_PATH, RTLD_NOW);
    if (!handle) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", LIB_CACULATE_PATH, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }
  
    *(void **) (&cac_func) = dlsym(handle, "led_on");
    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }
    ALOGE("led_on: 50\n");
    (*cac_func)(NULL,50);

    /* Get the address of the struct hal_module_info. */
     ALOGE("dlsym ..\n");
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }
    ALOGE("hmi->id: %s\n",hmi->id);
    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }
ALOGE("handle :%d \n",handle);
ALOGE("hmi :%d\n",hmi);
ALOGE("hmi->dso :%d \n",hmi->dso);
ALOGE("hmi->name :%s \n",hmi->name);

    hmi->dso = handle;
ALOGE("handle end ..\n");
    /* success */
    status = 0;

done:
    ALOGE("status : %d \n",status);
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }
    ALOGE("pHmi :%d\n",pHmi);
    *pHmi = hmi;

    return status;
}

同目錄下


LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

# [optional, user, eng] 
# eng = required
# optinal = no install on target
LOCAL_MODULE_TAGS := eng

# This is the target being built.
LOCAL_MODULE:= jnitest

# Target install path.
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)

# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
    jnitest.cpp

# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libnativehelper \
    libcutils \
    libutils \
    libhardware \
    libdl

# No static libraries.
LOCAL_STATIC_LIBRARIES :=

# Also need the JNI headers.
LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE) \
    vendor/mokoid/hardware/modules/include/

# No specia compiler flags.
LOCAL_CFLAGS += -g -static -fPIC -ldl

# Don't prelink this library.  For more efficient code, you may want
# to add this library to the prelink map and set this to true.
#LOCAL_PRELINK_MODULE := false
#include $(BUILD_SHARED_LIBRARY)

include $(BUILD_EXECUTABLE)

做了這些工作一步步定位到,hmi->dso = handle;這句通不過。呵呵。這個可以程式碼中坑。要改的地方在。

mokoid/trunk/hardware/modules/led/led.c中。

//const struct led_module_t HAL_MODULE_INFO_SYM = {
struct led_module_t HAL_MODULE_INFO_SYM = {

很明顯這個是有意而為。因為有這些坑,用網上的程式碼,其實不比自已寫程式碼少花多少時間。當然要看坑的大小。

然改對後就沒什麼問題了,這個程式碼是可以在真機上執行的。因為它把對驅動的呼叫程式碼給刪了,只輸出log. 真機上是一定沒有相應的裝置的,所以只能刪去相關程式碼。這個沒錯。所以上這原始碼只要改對還有點用。並且我的主要目的是用直接呼叫HAL。

 

相關文章