不少公司在开发android产品的时候,都需要在android中增加自己的service,尤其是定制的工业用途的设备,我们公司的项目中就涉及到要增加一个service,是一个北斗通信service,具体的内容不便透露,涉及到保密。但是增加service的过程大概能描述一下,具体代码就按着不重要的来帖,大家见谅!! 其实增加自己的service不论是谁来,我想都会仿照现有service来做,在android现有service中,最简单明了的是vibrator service,其次的是location service也就是GPS,这两个service 虽然简单,但是service的架构都是相同,我们仿照的目标就是要这样,明了的架构,往里面填东西就是体力活了。下面我们从下往上一一来看每个步骤。
1.kernel层
我们的硬件连接到设备上一个串口,因此kernel层我们就不用什么改动。当然如果你们添加的硬件设备需要驱动的话,自己加进去就是了,这里不多说了。
2.HAL层
我们先找到了GPS的HAL层代码 \android\hardware\imx\libgps中一共两个文件,仔细分析来看,这两个文件主要功能是生成一个动态链接库,向下与硬件通信,向上为系统提供访问的接口函数,知道了这个,我们就明白了HAL层大概的功能,我们的工作就是选一个适合自己硬件的方法实现这个功能。
我们的硬件是一个串口通信的设备,无非就是可以由上层控制来发送命令和接受命令,因此我选的是串口通信很常见的方式: 每条发送命令都单独写一个函数,有上层来控制发送哪条。接收命令就启动一个接收线程,每当收到数据后,进行一系列的判定,把有效的数据传送到上层。
下面我们来看具体代码在android\hardware\imx\librd目录中:
首先是我们与硬件通信的几个函数:
static const RDInterface goldtelrdInterface = {
sizeof(RDInterface),
goldtel_rd_init,
goldtel_rd_close,
goldtel_rd_send_XTZJ,
goldtel_rd_send_ICJC,
goldtel_rd_send_SJSC,
goldtel_rd_send_DWSQ,
goldtel_rd_send_BBDQ,
goldtel_rd_send_YHZL_SJTX,
goldtel_rd_send_TXSQ,
};
static int goldtel_rd_init(RDCallbacks* callbacks)
{
RdState* s = _rd_state;
//lijianzhang
write_sysfs("/sys/devices/platform/bd_power/enable_rdss","1",1); //给设备上电
usleep(1500000);
if (!s->init)
rd_state_init(s, callbacks);//进行一系列的初始化 注意这里的一个参数callbacks,
//这是一些回调函数,是在jni层实现的,传到底层来运行
//android很多都是这么来实现,如果以后看别的代码看到
//类似方式就不要慌乱,去jni层里肯定能找到
if (s->fd < 0)
return -1;
rd_state_start(s); //开始工作
return 0;
}
static void
rd_state_init( RdState* state, RDCallbacks* callbacks )
{
.................
一系列初始化
。。。。。。。。。。。。。。。。。。。
state->thread = callbacks->create_thread_cb( "rd_state_thread", rd_state_thread, state );//这个函数里最主要的功能就是创建接收线程
。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。
}
下面来看接收线程里的东西:
static void
rd_state_thread( void* arg )
{
RdState* state = (RdState*) arg;
NmeaReader reader[1];
int epoll_fd = epoll_create(2);
int started = 0;
int rd_fd = state->fd;
int control_fd = state->control[1];
nmea_reader_init( reader );
// 注册epoll 文件
epoll_register( epoll_fd, control_fd );
epoll_register( epoll_fd, rd_fd );
LOGE("RD thread running");
// now loop
for (;;) {
struct epoll_event events[2];
int ne, nevents;
nevents = epoll_wait( epoll_fd, events, 2, -1 ); //等待epoll消息
if (nevents < 0) {
if (errno != EINTR)
E("epoll_wait() unexpected error: %s", strerror(errno));
continue;
}
D("rd thread received %d events", nevents);
for (ne = 0; ne < nevents; ne++) { //处理每条消息
if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
E("EPOLLERR or EPOLLHUP after epoll_wait() !?");
return;
}
if ((events[ne].events & EPOLLIN) != 0) {
int fd = events[ne].data.fd;
if (fd == control_fd) //如果这个消息是控制命令的话,这里的控制命令其实就两条,
//服务开始和服务结束,下面就是分别对着两条命令进行处理
{
char cmd = 255;
int ret;
D("rd control fd event");
do {
ret = read( fd, &cmd, 1 );
} while (ret < 0 && errno == EINTR);
if (cmd == CMD_QUIT) { //服务退出
if (started) {
started = 0;
nmea_reader_set_DWXX_callback(reader,NULL);
nmea_reader_set_BBXX_callback(reader,NULL);
nmea_reader_set_FKXX_callback(reader,NULL);
nmea_reader_set_ICXX_callback(reader,NULL);
nmea_reader_set_ZJXX_callback(reader,NULL);
nmea_reader_set_TXHZ_callback(reader,NULL);
nmea_reader_set_TXXX_callback(reader,NULL);
}
return;
}
else if (cmd == CMD_START) { //服务开始
if (!started) {
LOGE("rd_state_thread start!");
started = 1;
nmea_reader_set_DWXX_callback(reader,state->callbacks.dwxx_cb);
nmea_reader_set_BBXX_callback(reader,state->callbacks.bbxx_cb);
nmea_reader_set_FKXX_callback(reader,state->callbacks.fkxx_cb);
nmea_reader_set_ICXX_callback(reader,state->callbacks.icxx_cb);
nmea_reader_set_ZJXX_callback(reader,state->callbacks.zjxx_cb);
nmea_reader_set_TXHZ_callback(reader,state->callbacks.txhz_cb);
nmea_reader_set_TXXX_callback(reader,state->callbacks.txxx_cb);
}
}
}
else if (fd == rd_fd) //如果是串口通信的消息
{
// LOGE("start read data");
char buff[32];
for (;;) {
int nn, ret;
ret = read( fd, buff, sizeof(buff) ); //从串口中读出数据
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno != EWOULDBLOCK)
E("error while reading from gps daemon socket: %s:", strerror(errno));
break;
}
for (nn = 0; nn < ret; nn++)
{
//LOGE("start read data %02X",buff[nn]);
nmea_reader_addc( reader, buff[nn] ); //判定数据有效性并进行处理
}
}
}
else
{
E("epoll_wait() returned unkown fd %d ?", fd);
}
}
}
}
}
进程创建完成了,hal层基本功能就完成了,下一步就是将这些功能声称一个.so动态链接库,方法就是下面的代码:
static const RDInterface* get_rd_hardware_interface()
{
return &goldtelrdInterface;
}
static int open_rd(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
struct rd_device_t *dev = malloc(sizeof(struct rd_device_t));
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->get_rd_interface = get_rd_hardware_interface;
*device = (struct hw_device_t*)dev;
return 0;
}
static struct hw_module_methods_t rd_module_methods = {
.open = open_rd
};
const struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = RD_HARDWARE_MODULE_ID,
.name = "Real6410 rd Module",
.author = "The Android Open Source Project",
.methods = &rd_module_methods,
};
动态链接库生成完了,下面就到了framework层,我们找到了location service的代码在目录\android\frameworks\base\services\jni\中功能就是打开这个动态链接库然后,向java层提供函数接口,功能很简单我们直接来看我写的代码在android\\frameworks\base\services\jni\com_android_server_rdmessage_RDMessageDispatch.cpp中
首先是打开动态链接库
static const RDInterface* get_rd_interface() {
int err;
hw_module_t* module;
const RDInterface* interface = NULL;
err = hw_get_module(RD_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, RD_HARDWARE_MODULE_ID, &device);
if (err == 0) {
rd_device_t* rd_device = (rd_device_t *)device;
interface = rd_device->get_rd_interface(rd_device);
}
}
return interface;
}
最后的是想上层提供的函数接口
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_rdmessage_RDMessageDispatch_class_init_native},
{"native_is_supported","()Z",(void*)android_rdmessage_RDMessageDispatch_is_supported},
{"native_BDMessage_start", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_BDMessage_start},
{"native_close","()V",(void*)android_rdmessage_RDMessageDispatch_native_close},
{"native_sendXTZJ", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendXTZJ},
{"native_sendICJC", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendICJC},
{"native_sendSJSC", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendSJSC},
{"native_sendDWSQ", "(ZZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendDWSQ},
{"native_sendBBDQ", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendBBDQ},
{"native_sendYHZL_SJTX", "(II)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendYHZL_SJTX},
{"native_SendBDMessage", "(I[BIIZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_SendBDMessage},
{"read_TXXX_message", "([BI)I", (void*)android_rdmessage_RDMessageDispatch_read_TXXX_message},
{"read_DWXX_message","([B[B[B[B[B)V",(void*)android_rdmessage_RDMessageDispatch__read_DWXX_message},
};
中间就是实现这两者之间的转换,我们来说转换过程中比较重要的几点首先是发送命令,很简单,就是直接调用动态链接库中提供的发送函数,看代码
static jboolean android_rdmessage_RDMessageDispatch_native_sendICJC
(JNIEnv *env, jobject obj)
{
if (!sRdInterface)
return false;
if(sRdInterface->rd_send_ICJC()!=0)
return false;
return true;
}
static void ZJXX_callback(ZJXXInfo *zjxx)
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(mCallbacksObj,method_reportZJXX,zjxx->UserId,zjxx->ICStatus,
zjxx->YJStatus,zjxx->DCStatus,zjxx->RZStatus,zjxx->bs1Status,zjxx->bs2Status,
zjxx->bs3Status,zjxx->bs4Status,zjxx->bs5Status,zjxx->bs6Status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
4. framework层
我们看到location service的代码在android\frameworks\base\services\java\com\android\server\LocationManagerService.java
android\frameworks\base\services\java\com\android\server\location目录 android\\frameworks\base\location\java\android\location目录中
分析一下架构,就是向下接口JNI层的 代码,在此基础上封装service实现函数,最后在servicemanager中运行这个服务,还有增加这个服务的aidl文件,以便于所有的app都能访问到。所以我们所做的工作也是这些,其实比较简单,主要是为界面提供支持,比如更新状态栏北斗信号强度等等,这里就不再贴代码了,大家可以自己看gps是怎么做的仿照来就行。
实现了这些函数最后要在android\frameworks\base\services\java\com\android\server\SystemServer.java中增加
try { //启动这个service
Slog.i(TAG, "RdMessage Manager");
rdmessage = new RdMessageManagerService(context);
ServiceManager.addService(Context.RD_MESSAGE_SERVICE, rdmessage);
} catch (Throwable e) {
reportWtf("starting RdMessage Manager", e);
}
try {
if (rdmessageF != null) rdmessageF.systemReady(); //告诉系统服务启动完成
} catch (Throwable e) {
reportWtf("making RdMessage Service ready", e);
}
到了这里整个service添加就完成了,我们就可以通过app来访问这个service了。
5.总结
添加service的整个过程中,HAL层是与硬件通信的纯C语言的东西,有过Linux和C开发的人应该是没有难度,java层的东西是为app服务的,做app开发的人写起来应该不算困难,不过本人是做底层的,对这些并不是很熟悉,所以不敢多写,期待高手们补充。本人在写这个的过程中重点的精力放在了jni层,下面我们就来总结一下C++和java通信的方法,java是怎么调用C++函数的,C++又是怎么调用java函数的?
(1)JAVA调用C++
在jni中用到的就是
static JNINativeMethod sMethods[] = { //C++函数和java函数映射的结构体数据,三个选项分别是:java函数名,参数和函数返回值,C++函数指针
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_rdmessage_RDMessageDispatch_class_init_native},
{"native_is_supported","()Z",(void*)android_rdmessage_RDMessageDispatch_is_supported},
{"native_BDMessage_start", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_BDMessage_start},
{"native_close","()V",(void*)android_rdmessage_RDMessageDispatch_native_close},
{"native_sendXTZJ", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendXTZJ},
{"native_sendICJC", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendICJC},
{"native_sendSJSC", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendSJSC},
{"native_sendDWSQ", "(ZZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendDWSQ},
{"native_sendBBDQ", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendBBDQ},
{"native_sendYHZL_SJTX", "(II)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendYHZL_SJTX},
{"native_SendBDMessage", "(I[BIIZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_SendBDMessage},
{"read_TXXX_message", "([BI)I", (void*)android_rdmessage_RDMessageDispatch_read_TXXX_message},
{"read_DWXX_message","([B[B[B[B[B)V",(void*)android_rdmessage_RDMessageDispatch__read_DWXX_message},
};
int register_android_server_rdmessage_RDMessageDispatch(JNIEnv* env)
{ //com/android/server/location/GpsLocationProvider
return jniRegisterNativeMethods(env, "com/android/server/rdmessage/RDMessageDispatch", sMethods, NELEM(sMethods));//注册这个数据到所调用的java类中
}
(2)C++调用java
这里用的方式如下面这个函数
static void ZJXX_callback(ZJXXInfo *zjxx)
{
JNIEnv* env = AndroidRuntime::getJNIEnv(); //获取dalvik虚拟机 当前的AndroidRuntime
env->CallVoidMethod(mCallbacksObj,method_reportZJXX,zjxx->UserId,zjxx->ICStatus, //执行java函数
zjxx->YJStatus,zjxx->DCStatus,zjxx->RZStatus,zjxx->bs1Status,zjxx->bs2Status,
zjxx->bs3Status,zjxx->bs4Status,zjxx->bs5Status,zjxx->bs6Status);
checkAndClearExceptionFromCallback(env, __FUNCTION__); //清楚回调函数的返回参数
}
这里调用了dalvik虚拟机提供的CallVoidMethod()函数来执行,我们可以找到这个函数的具体代码在:\android\dalvik\libnativehelper\include\nativehelper\jni.h中
void CallVoidMethod(jobject obj, jmethodID methodID, ...)
{
va_list args;
va_start(args, methodID);
functions->CallVoidMethodV(this, obj, methodID, args);
va_end(args);
}
从这里我们可以知道CallVoidMethod()的参数
static jboolean android_rdmessage_RDMessageDispatch_native_BDMessage_start
(JNIEnv *env, jobject obj)
{
if (!mCallbacksObj)
mCallbacksObj = env->NewGlobalRef(obj);//创建类的对象
if(!sRdInterface)
{
sRdInterface = get_rd_interface();
}
if (!sRdInterface || sRdInterface->rd_init(&sRdCallbacks) != 0) {
LOGE("in GetRDInterface sRdInterface is null");
sRdInterface = NULL;
return false;
}
return true;
}
method_reportZJXX:是java类里面的具体实现函数的ID,获得方法: method_reportZJXX = env->GetMethodID(clazz, "reportZJXX", "(IIIIIIIIIII)V");
static jint android_rdmessage_RDMessageDispatch_read_TXXX_message
(JNIEnv *env, jobject obj, jbyteArray nmeaArray, jint buffer_size)
{
// this should only be called from within a call to reportTXXX
jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0);//讲java数组转换成char*
int length = sMessageStringLength;
if (length > buffer_size)
length = buffer_size;
memcpy(nmea, sMessageString, length); //数据复制
for(int i=0;iReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); //释放刚才的char*
return length;
}