不少公司在开发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,
};

具体的函数功能就不多说了,反正就是发送命令初始化和关闭。当上层打开这个service的时候会调用goldtel_rd_init()这个函数,因此在这个函数里面我们要实现一系列的初始化功能,包括打开设备电源,打开串口,创建接收进程等等。具体代码:

static int goldtel_rd_init(RDCallbacks* callbacks)
{RdState*  s = _rd_state;//lijianzhangwrite_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;
}

然后是rd_state_init()

static void
rd_state_init( RdState*  state, RDCallbacks* callbacks )
{.................一系列初始化。。。。。。。。。。。。。。。。。。。state->thread = callbacks->create_thread_cb( "rd_state_thread", rd_state_thread, state );//这个函数里最主要的功能就是创建接收线程。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

下面来看接收线程里的东西:
这里面用到了很多的epoll通信机制,其实逻辑很简单一看就明白,下面把代码贴出来,大家给个参考指正

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 loopfor (;;) {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);}}}}}

下面重要的函数就是nmea_reader_addc( NmeaReader*  r, int  c ) 这里作为涉密内容不做过多的说明了,明白就是判定命令并与上层通信就可以了。

进程创建完成了,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,
};

3.JNI层

动态链接库生成完了,下面就到了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;}

然后是,收到串口数据,传送的上层的方法,这里就涉及到了刚才提到的回调函数,在这里实现但是在HAL层执行,函数含简单就是调用函数将数据发送上去,看代码

 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__);}

其他的就不多说了下面来看framework层

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 {        //启动这个serviceSlog.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);}

增加这些代码后就系统就可以启动服务了,
最后在\frameworks\base\core\java\android\os\增加IRdMessageManager.aidl, RdMessageManager.java这两个文件,是提供给上层使用的短信服务。

到了这里整个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/GpsLocationProviderreturn jniRegisterNativeMethods(env, "com/android/server/rdmessage/RDMessageDispatch", sMethods, NELEM(sMethods));//注册这个数据到所调用的java类中
}

android的JNI就是用这种方式实现的,我百度了一下,发现还是有别的引用方法,但是对于dalvik这个精简的虚拟机来讲不知道支不支持,期待做过的高手们给予一定的指导。
这个网页是别人写的资料,供大家参考一下http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

(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()的参数
mCallbacksObj:是dalvik虚拟机传下来的java类的一个对象,实现的方法是

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");
剩余的:剩下的所有函数就是java函数的参数
用这种方法cpp层就可以调用java函数了总的来讲游泳的函数就几个
env->NewGlobalRef();
env->GetMethodID();
env->CallVoidMethod();
(3)  C++向java传送指针数据

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 reportTXXXjbyte* 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;i<length;i++)LOGE("nmea %2X",nmea[i]);env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);  //释放刚才的char*return length;}

这个函数是C++的一个函数,注册到java中执行。这里涉及到数据交换的函数是dalvik虚拟机提供的
jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0); //讲java数组转化成char*指针
env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); //释放这个指针
由于java中并不支持C++中的指针,因此才需要一个函数来进行数据转换。
nmeaArray:是java类中的数据
nmea:是C++中的char*指针
JNI_ABORT:是复制的类型。
同样的java向C++传送数据也用着两个函数,只是memcpy拷贝数据的方向不同而已。

android在framework层增加自己的service---仿照GPS相关推荐

  1. android在framework层增加自己的service仿照GPS

    不少公司在开发android产品的时候,都需要在android中增加自己的service,尤其是定制的工业用途的设备,我们公司的项目中就涉及到要增加一个service,是一个北斗通信service,具 ...

  2. android 代码控制音量,Android的framework层音量控制原理分析--hot(key)处理

    Android.media.AudioManager中包含了对android.media.AudioService的跨进程AIDL调用封装. 正常处理过程: 1.调整音量是通过AudioManager ...

  3. Android开发如何定制framework层服务

    刚刚跨完年,新年第一篇文章,那么今天将对Android开发framework中间层的服务定制使用作个总结.首先我们先导入Android平台源码framework层的代码到开发工具eclipse中,代码 ...

  4. 了解Framework层对一名Android工程师的工作有什么帮助吗?

    了解Framework层对一名Android工程师的工作有什么帮助吗? 最近有一个朋友向我问了一个这样的问题: 作为一个应用开发工程师,在网上有看到过大家都说了解系统源码,例如四大组件启动流程及IPC ...

  5. android power 按键,Android Framework层Power键关机流程(一,Power长按键操作处理)

    一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...

  6. Android Framework中的Application Framework层介绍

    Android的四层架构相比大家都很清楚,老生常谈的说一下分别为: Linux2.6内核层,核心库层,应用框架层,应用层.我今天重点介绍一下应用框架层Framework. Framework层为我们开 ...

  7. android l camera no panorama,Android Camera从App层到framework层到HAL层的初始化过程

    Android camera 从上到下能够分为四个部分: Application层. framework层. HAL(hardware abstract layer)层. Kernel层 通常面向开发 ...

  8. android重复拉起app首页_Android进程管理:Framework层概念

    前情回顾 RickAi,公众号:安卓尖端技术研究Android进程管理:从Kernel到LowMemoryKiller 上一篇文章从Native角度讲解了Android进程管理的相关概念,本文将继续从 ...

  9. Android源码分析 - Framework层的Binder(客户端篇)

    开篇 本篇以aosp分支android-11.0.0_r25作为基础解析 我们在之前的文章中,从驱动层面分析了Binder是怎样工作的,但Binder驱动只涉及传输部分,待传输对象是怎么产生的呢,这就 ...

最新文章

  1. Python入门:常用模块—logging模块
  2. [译]ASP.NET Core 2.0 机密配置项
  3. postgresql+postgis安装
  4. oracle增量脚本(记录)创建触发器监控对一张表的增删改
  5. JavaScript中的.trim()无法在IE中运行
  6. 【超详细】Java实现学生信息管理系统
  7. android 自定义唤醒词,星星1号语音升级 中兴语音助手实现可自定义唤醒词
  8. HBase预分区设计
  9. 论文解读:Structural Optimization Makes Graph Classification Simpler and Better
  10. 人工智能 | ShowMeAI资讯日报 #2022.06.07
  11. 待支付取件费用是什么意思_菜鸟裹裹待支付怎么取消
  12. 桌面虚拟化中RDS、VDI、IDV、VOI主流的云桌面技术比较(转载)
  13. HCIP第九天笔记(OSPF的路由回馈、路由策略、以及配置指南)
  14. 纪念我的第一个程序员节
  15. 组合数学——特征方程与线性递推方程
  16. 如何利用微信生态为教育行业提高招生率?
  17. 进制转换器的c代码实现
  18. 选择恐惧症应对方法——以终为始来做选择
  19. 简单的echarts安装教程
  20. 个人站长做什么网站比较实际

热门文章

  1. 如何免费建立一个网站?
  2. 微视点:网传社交游戏公司“五分钟”面临倒闭,引发各方热议
  3. 深度学习(3):基于人脸的性别和年龄判断
  4. ubuntu cron 定时任务
  5. 小米5 MIUI 10系统完全Root教程 (Root思想通用所有机型)
  6. word文件打开就是只读模式,怎么取消?
  7. 美女画廊(点击上面的图片下面进行显示)
  8. 指向性麦克风----8字形麦克风设计(四)
  9. html鼠标悬停超链接(头像、文字)显示提示信息
  10. (Modern Family S01E01) Part 9 PhilClair 射Luke、Haley和Dylan躺床上看电影