想在手机上做一些功能的扩展,本来是想从驱动层入手的,但驱动需要改内核。因为,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。