优秀的参考文献:
https://blog.csdn.net/aaa111/article/details/79361595
https://blog.csdn.net/guoleimail/article/details/41649537
https://blog.csdn.net/yuxiangyunei/article/details/42809543
android中如何添加一个AT命令 简介: android平台,自带的参考RIL层是使用AT命令来和modem进行通信的,因为它假定AP和CP可能来自不同的厂家,所以AP和CP之间采用的是松耦合的机制。而高通平台,AP和CP都由高通提供,所以两颗CPU之间的通信采用紧耦合机制,采用ONCRPC或QMI来进行通信。 AT命令和QMI各有优缺点,AT命令有国际统一的标准,实现简单,但是实现的功能少,效率较低。而QMI机制是高通制定的,其实现复杂,但是效率高。
下面介绍使用AT命令机制实现的RIL,如何添加一个AT命令。我们假定要加一个读取CDMA modem的IMSI值的AT命令。 一、在上层添加所要发的AT命令的方法 1. 在接口phone中添加所要发的AT命令的方法。
接口phone所在的位置是:frameworks/base/telephony/java/com/android/internal/telephony/Phone.java 在phone的接口中添加方法:getcdmaIMSI(); Phone是个接口,因此,添加完方法后,得在实现Phone接口的java类里面实现这个方法,
GSM实现Phone的类是GSMPhone.java,
CDMA实现phone的类是CDMAPhone.java
GSMPhone.java位于 frameworks/base/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
CDMAPhone.java位于 frameworks/base/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
public String getcdmaIMSI() { return mSST.getImsi(); }
2. Phone接口的实现方法调用CommandsInterface接口里面的方法,所以我们还需要在CommandsInterface里面把我们要增加的方法添加进去。
CommandsInterface接口位于frameworks/base/telephony/java/com/android/internal/telephony/commandsinterface.java
在commandsinterface接口中添加方法:void getIMSI(String aid, Message result);
同样的,CommandInterface也是个接口,我们需要在实现接口的类里实现这个方法。而实现这个接口的类有2个:
1. frameworks/base/telephony/java/com/android/internal/telephony /RIL.java
2. frameworks/base/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
其中第2个是在模拟器里面测试用的, 我们只需要在里面把方法添加进去,然后调用个resultSuccess或者umimplement都可以。 而第1个才是真正实现功能的类。 所以,我们得在RIL.java中,实现具体的方法。 tob_id_2536在RIL.java的方法里面,只需要定义好你所需要发送AT命令的一个标识MARK(下面还会提到), 再把RilRequest类里面的Parcel成员mp赋值, 然后发送出去即可。 赋给mp的值,即为我们需要发送到下层去处理的值, 例如传个数组下去, 一般都先把长度写进去, 其次再把成员依次写入。
public void getIMSI(String aid, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
rr.mp.writeInt(1);
rr.mp.writeString(aid);
if (RILJ_LOGD) riljLog(rr.serialString() + "> getIMSI: " + requestToString(rr.mRequest) + " aid: " + aid);
send(rr); }
二、在下层添加实现的函数 1. 在hardware/ril/include/telephony/ril.h h文件中添加AT命令标识,即上文提到的MARK。注意不要和别的宏发生冲突。 int RIL_REQUEST_GET_IMSI = 11; 注意: 这里的MARK必须定义在最后面, 不然会带来不必要的麻烦, 理由如下: 在ril.h中定义了每个关键字对应的值,同时在ril_command.h有张映射表,而且是按ril.h中的顺序映射的,大家可以看作是数组的下标。这里要一一对应,如果从中间插入,将会导致后面的字段映射不对。 除非把ril.h中关键字对应的值修改,但这样会浪费比较多的时间。
2. 在hardware/ril/libril/ril.cpp中添加消息映射字符串。 在该文件的const char *requestToString(int request)函数里面。 case RIL_REQUEST_GET_IMSI: return "GET_IMSI";
3. 在hardware/ril/libril/ril_commands.h文件的最后添加函数映射表。 形如{MARK, dispatch, response} 解释如下: 首先第1个参数即为我们之前所定义的标识,即MARK。 第2个参数是下层的从数据流中解出数据的函数,这里要和上层所传下来的类型对应,例如上层传下来的是int数组,这里也得是dispathInts, 否则数据会出错 第3个参数是该函数所要返回的值, 这里的和第2个参数的一样。 {RIL_REQUEST_GET_IMSI, dispatchStrings, responseString},
4. 在hardware/ril/reference-ril/reference-ril.c中添加处理AT命令函数
此文件中,函数static void onRequest (int request, void *data, size_t datalen, RIL_Token t)是RIL发送请求的入口函数。在此函数里面添加我们所要处理的AT命令函数。 一般对于只返回成功与否的AT命令,我们用at_send_command()。 而对于有返回值的命令,我们用at_send_command_singleline()。 case RIL_REQUEST_GET_IMSI: at_send_command_numeric("AT+CIMI", &p_response); 三、添加返回值 命令发送成功后,modem会返回命令的响应消息。
因此我们还需要对响应消息进行处理。 响应消息的处理是在RIL.java文件中的processUnsolicited()或者processSolicited()函数中。 processSolicited()处理的是 通过发送命令然后才会返回结果的消息。 而processUnsolicited()处理的是 主动上报类型的结果消息,即不需要你发送命令,modem侧会主动发的消息。比如:modem主动上报来电消息。 注意我们选用的返回函数,得和我们在ril_command.h里面所添加的函数映射表里的返回函数对应。 case RIL_REQUEST_GET_IMSI: ret = responseString(p); break; 到了这里,AT命令的过程就添加结束, 命令的返回值就在调用Phone里面的函数所传入的Message中。 返回的是一个AsyncResult, 就是Message的obj成员 。 这里我们可以处理异常,一般可通过如下代码处理(假设传入的Message为msg): AsyncResult ar = (AsyncResult)msg.obj if (ar.exception != null) 处理异常 处理结果,就对ar.result进行处理。
1、hardware\ril\reference-ril\Reference-ril.c 中 onRequest() 处理RIL_REQUEST_DIAL 时会 调用requestDial();
2、requestDial()中转换RIL_REQUEST_DIAL 请求为AT指令;
3、通过at_send_command()下发;
4、经过at_send_command_full,at_send_command_full_nolock ,writeline()写到设备中。
要满足这三个条件:
1,单开线程自动写at
2,单开线程读at
3,打开文件读配置,将读出来后的配置文件放到log文件里去。
这边记录下如何在ui下开一个线程的过程:用一个handler变量调用handler.post函数,然后在runnable里面重写run接口就可以,实际上,这样调用出来的线程跟ui主线程是一个线程,不会创建新的。所以这边如果要用线程创建的话,必须
wthread = new HandlerThread("thread");
wthread.start();
wHandler = new Handler(wthread.getLooper());
wHandler.post(runnable);
这时重写runnable的run函数才可以实现重新开启一个线程。
在这个线程里面可以读写串口,但是界面的刷新不能在这个子线程里面做。必须主线程在做一个handler,然后子线程调用Message 变量传进主线程的handler.sendMessage(msg);然后主线程用handlerMessage接收子线程传过来的消息,在主线程里面刷新界面。