1.续上一节内容
承接上一节内容。
- 流程分析
上一节主要是跳出android系统的设计思路,按照最简单的linux分层思想来做的一个实践,而没有使用android系统自带的服务。
先简单说一说android自带的服务流程。
系统启动,zygote运行起来,调用到SystemServer服务,运行run()方法。会加载库android_servers,也就是调用到onload.cpp中的JNI_ONLoad程序,其中会注册很多的服务,如下所示:
注册方法
加载库android_servers文件,其实就是libandroid_servers,查看一下文件:frameworks/base/services/Android.mk,可以找到如下一行内容:
libandroid_servers库文件
所以,其实会加载frameworks/base/services/目录下的内容。
以示例VibratorService为例,执行到frameworks\base\services\core\jni\com_android_server_VibratorService.cpp文件内容,也就是能调用到JNI。
接着看SystemServer内容,最后还会执行startOtherServices()函数,添加了一项服务,还是以Vibrator为例子,如下图所示:
Vibrator服务添加例子
这个函数的执行,会进入VibratorService.java程序。这里面有jni需要执行的底层函数。
基于此,就能简单的认为,编写目前的LED程序,需要下面的步骤:
1.编写一个JNI程序,命名和Vibrator相似,如:com_android_server_LedService.cpp
2.基于1,在onload.cpp中添加刚注册的函数内容。
3.编写LedService.java文件,添加JNI上一层的函数调用关系。
4.修改SystemServer.java,将3的服务添加进去。
5.编写ILedService.java,该文件给Android APP提供接口,解脱APP开发人员的工作。
2.编写代码
现在开始从上往下添加代码:
1.编写ILedService.java接口文件(aidl文件)
参考IVibratorService.aidl文件,将文件先命名为:ILedService.java,然后放入 android-5.0.2\frameworks\base\core\java\android\os下。
aidl文件内容如下所示:
package android.os;
/** {@hide} */
interface ILedService
{
int ledctl(int which,int state);
}
接着先将环境变量设置一遍:
$ sudo -i
$ . setenv
$ lunch
$ cd frameworks/base
$ vi Android.mk
...
core/java/android/os/ILedService.aidl
...
$ mmm .
...
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/emma_out/lib/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/noproguard.classes.jar
target Dex: framework
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.dex
target Jar: framework (out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar)
Install: out/target/product/tiny4412/system/framework/framework.jar
make: Leaving directory `/home/wityuan/Desktop/android-5.0.2'
#### make completed successfully (03:12 (mm:ss)) ####
...
$ cd ../../
$ cd out
$ find -name "ILedService.java"
./target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java
$
之后使用该接口,可以参考SystemVibrator.java文件。
2.编写LedService.java文件。
文件路径为:frameworks/base/services/core/java/com/android/server/
该文件主要用来调用本地方法。
package com.android.server;
import android.os.ILedService;
public class LedService extends ILedService.Stub{
private static final String TAG = "LedService";
public int ledctl(int which, int state) throws android.os.RemoteException
{
return native_ledctl(which,state);
}
public LedService()
{
native_ledopen();
}
public static native int native_ledctl(int which, int state);
public static native int native_ledopen( );
public static native void native_ledclose( );
}
3.修改SystemServer.java文件:
文件路径为:android-5.0.2/android-5.0.2/frameworks/base/services/java/com/android/server/SystemServer.java
...
Slog.i(TAG, "Led Service");
ServiceManager.addService("Led", new LedService());
...
4.添加com_android_server_LedService.cpp文件
加入anroid系统源码的:frameworks/base/services/core/jni/位置。
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware_legacy/vibrator.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
namespace android
{
static jint fd;
jint native_ledopen(JNIEnv *env , jobject thiz)
{
fd = open("/dev/leds",O_RDWR);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_open...%d \r\n",fd);
if(fd<0)
return -1;
return 0;
}
void native_ledclose(JNIEnv *env , jobject thiz)
{
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_close...");
close(fd);
}
jint native_ledctl(JNIEnv *env , jobject thiz ,jint number, jint state)
{
int ret = ioctl(fd,state,number);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);
return ret;
}
static JNINativeMethod methods[] = {
{ "native_ledctl", "(II)I", (void*) native_ledctl },
{ "native_ledopen", "()I", (void*) native_ledopen },
{ "native_ledclose", "()V", (void*) native_ledclose }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
methods, NELEM(methods));
}
};
修改frameworks\base\services\core\jni\Android.mk文件:
$ vi frameworks/base/services/core/jni/Android.mk
...
$(LOCAL_REL_DIR)/com_android_server_LedService.cpp \
...
5.修改onload.cpp文件内容
文件路径为: android-5.0.2/android-5.0.2/frameworks/base/services/core/jni/Onload.cpp
...
int register_android_server_LedService(JNIEnv *env);
...
...
register_android_server_LedService(env);
...
完成以上步骤之后,就可以执行编译操作了。
$ mmm frameworks/base/services/
$ make snod
$ ./gen-img.sh
$ cp system.img /mnt/hgfs/Desktop
把以上需要拷贝的文件,集中到下面:
$ cp /mnt/hgfs/Desktop/LedService.java frameworks/base/services/core/java/com/android/server/
$ cp /mnt/hgfs/Desktop/SystemServer.java frameworks/base/services/java/com/android/server/
$ cp /mnt/hgfs/Desktop/com_android_server_LedService.cpp frameworks/base/services/core/jni/
$ cp /mnt/hgfs/Desktop/onload.cpp frameworks/base/services/core/jni/
2.编写App
将一些framework程序拷贝到android内核中之后,需要在app中引用,这个时候就需要对Android studio做一些设置了。
1.拷贝classes.jar包
$ cp out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar /mnt/hgfs/Desktop/
添加到Android studio中。
file--->project structure--->+import .JAR/.AAR Package--->....
2.解决出现的错误
在build.gradle(Module:app)中添加如下内容:
defaultConfig{
...
...
multiDexEnabled true
}
3.出现错误以及解决办法
1.出现binder error等错误信息。
如下图所示:
binder
出现这种情况的时候,是进入不了启动界面的,也就不能测试App了。
经过排查,是以上的代码:
Slog.i(TAG, "Led Service");
ServiceManager.addService("Led", new LedService());
添加的有问题导致。
最终排查到onload.cpp文件没有修改,也就是说,jni文件没有起效。
4.更进一步
在上面的程序中,其实使用仍然是jni控制的驱动程序,并没有完全的体现出非常好的分层效果。不过有一个需要说明的是,它将APP与framework之间的分层体现出来了。这样写APP的开发人员并不需要了解驱动,而只需要知道要调用哪些接口程序,接口程序的功能即可。
但是,还有一个问题仍然没有解决,HAL一般是厂商写成so文件的,JNI解析so文件即可。这样就能做成厂商封闭其不想开源的部分。
要达到这个效果,就需要hal层提供so文件中的函数接口。
可以查看hw_get_module接口函数。而具体的使用方法,可以参考com_android_server_lights_LightService.cpp文件。对于编写底层的hal程序,可以参考vibrator.c文件。
现在第一步可以编写led_hal.c文件,是针对linux接口的最底层的开发的上层应用接口。
代码如下:
#include <hardware/hardware.h>
#include <cutils/log.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <hardware/led_hal.h>
#include <utils/Log.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
static int led_close(struct hw_device_t* dev);
static int led_open(struct led_device_t* dev);
static int led_ctrl(struct led_device_t* dev,int number,int state);
struct led_device_t led_dev={
.common = {
.close = led_close,
},
.led_open = led_open,
.led_ctrl = led_ctrl,
};
static int fd;
static int led_close(struct hw_device_t* dev)
{
close(fd);
return 0;
}
static int led_ctrl(struct led_device_t* dev,int number,int state)
{
int ret = ioctl(fd,state,number);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);
return ret;
}
static int led_open(struct led_device_t* dev)
{
fd = open("/dev/leds",O_RDWR);
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_open...%d \r\n",fd);
if(fd<0)
return -1;
return 0;
}
int led_device_open(const struct hw_module_t* module, const char* id,
struct hw_device_t** device)
{
*device = (hw_device_t *)&led_dev;
return 0;
}
static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = "led",
.id = "led",
.name = "Default led HAL",
.author = "wit_yuan",
.methods = &led_module_methods,
};
也定义一个led_hal.h文件,内容如下:
#ifndef _HARDWARE_led_H
#define _HARDWARE_led_H
#include <hardware/hardware.h>
__BEGIN_DECLS
struct led_device_t {
struct hw_device_t common;
int (*led_open)(struct led_device_t* dev);
//void (*led_close)(struct led_device_t* dev);
int (*led_ctrl)(struct led_device_t* dev,int number,int state);
};
__END_DECLS
#endif // _HARDWARE_VIBRATOR_H
接着编写Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := led.default
# HAL module implementation stored in
# hw/<VIBRATOR_HARDWARE_MODULE_ID>.default.so
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng
include $(BUILD_SHARED_LIBRARY)
将这三个文件传到位置:
hardware/libhardware/include/hardware/led_hal.h
hardware/libhardware/modules/led/led_hal.c
hardware/libhardware/modules/led/Android.mk
执行命令如下:
$ mkdir hardware/libhardware/modules/led
$ cp /mnt/hgfs/Desktop/led_hal.c hardware/libhardware/modules/led/led_hal.c
$ cp /mnt/hgfs/Desktop/led_hal.h hardware/libhardware/include/hardware/led_hal.h
$ cp /mnt/hgfs/Desktop/Android.mk hardware/libhardware/modules/led/Android.mk
$ cp /mnt/hgfs/Desktop/com_android_server_LedService.cpp frameworks/base/services/core/jni/
重新编写com_android_server_LedService.cpp文件:
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
namespace android
{
led_device_t *led_device;
static jint fd;
jint native_ledopen(JNIEnv *env , jobject thiz)
{
jint err;
hw_module_t *module;
hw_device_t *device;
err = hw_get_module("led",(const struct hw_module_t * * )&module);
if(err == 0){
err = module->methods->open(module,NULL,&device);
if(err == 0){
led_device = (led_device_t *)device;
return led_device->led_open(led_device);
}
else{
return -1;
}
}
return -1;
}
void native_ledclose(JNIEnv *env , jobject thiz)
{
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_close...");
}
jint native_ledctl(JNIEnv *env , jobject thiz ,jint number, jint state)
{
__android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);
return led_device->led_ctrl(led_device,number,state);
}
static JNINativeMethod methods[] = {
{ "native_ledctl", "(II)I", (void*) native_ledctl },
{ "native_ledopen", "()I", (void*) native_ledopen },
{ "native_ledclose", "()V", (void*) native_ledclose }
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
methods, NELEM(methods));
}
};
整体编译程序:
$ mmm frameworks/base/services
$ mmm hardware/libhardware/modules/led
$ make snod
$ ./gen-img.sh
$ cp system.img /mnt/hgfs/Desktop/
编译完成之后,将文件system.img烧写到系统中,然后运行程序。
网友评论