结合两篇博文:
1、原文地址:http://blog.csdn.net/guoleimail/article/details/41649537
2、原文优化地址:http://blog.csdn.net/yuxiangyunei/article/details/42809543
参考博文:
1、http://blog.csdn.net/linyongan/article/details/52189217
2、http://blog.csdn.net/linyongan/article/details/52151651
注意:以上两篇博文代码已运行过,本文是根据经验和7.0的代码流程扩展的,具体还在验证中。
下面,我们结合两篇文章来具体讲解。
同步和异步的解释:
同步的方式,调用之后,要等MODEM返回结果,将结果返回给第三方应用;
异步的方式,调用之后,不需要等待MODEM返回结果,当MODEM响应完成后,通过广播发送出去,让应用接收。
1、 Framework层的扩展
1.1 base模块下telephony的扩展
1.1.1 ITelephony.aidl的扩展
在尾部新增:
/**
* Execute AT command via unsync tunnel
* @param cmd AT command to execute
* execute successfully return true,
* AT command result will send by broadcast with action android.intent.action.AtCommand.result
*/
boolean AtCommandSendUnSync(String cmd);
/**
* Execute AT command via sync tunnel
* @param cmd AT command, time is timeout of process unit is ms
*/
String AtCommandSendSync(String cmd, int time);
但是实现是在:public class PhoneInterfaceManager extends ITelephony.Stub,后面会讲到,这里先跳过。
1.1.2 TelephonyManager.java的扩展
在尾部新增:
/**
* Send AT command via sync tunnel, it should return result until the command execute completely.
* @param cmd AT command
* @param time max time for executing at command, unit is ms.
* return is result of AT.
*/
public String AtCommandSendSync(String cmd, int time){
try {
return getITelephony().AtCommandSendSync(cmd, time);
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
return null;
}
}
/**
* Send AT command via unsync tunnel, it should return true or false when AT command has been send.
* @param cmd AT command
* return boolean
*/
public boolean AtCommandSendUnSync(String cmd){
try {
return getITelephony().AtCommandSendUnSync(cmd);
} catch (RemoteException ex) {
return false;
} catch (NullPointerException ex) {
return false;
}
}
其中的getITelephony()就是调用PhoneInterfaceManager.java中的接口,就是用过这个来达到进程之间的调用。
调用方法:
/**
* @hide
*/
private ITelephony getITelephony() {
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
1.1.3 RILConstants.java的扩展
int RIL_REQUEST_SEND_AT = 138; (Android M 目前已经到了137)
int RIL_UNSOL_RESPONSE_TUNNEL_AT = 1046; (Android M 目前已经到了1045)
不管到多少,这两个消息的值必须在其后按顺序递增添加。
1.2 opt模块下telephony的扩展
Android 7.0变化说明:
1、GSMPhone和CDMAPhone合并成了GsmCdmaPhone;
2、Phone.Java取代了以前的PhoneBase.java(内部的方法有小部分修改),Phone.java变成了所有关系的中心枢纽;
3、接口变成了PhoneInternalInterface,因为PhoneProxy已不存在了,实现了PhoneInternalInterface接口的只剩下Phone.java,所以删掉了PhoneInternalInterface中大量的register/unregister的接口,这些register/unregister的方法留在Phone.java中即可,PhoneInternalInterface接口变得更加精简;
1.2.1 PhoneInternalInterface.java的扩展
在尾部新增:
//Add AT tunnel
void sendAtToModem(String at_string, Message result);
1.2.2 Phone.java的扩展
新增一个消息:
protected static final int EVENT_UNSOL_AT_TUNNEL = 43; (Android M目前到达42)
在尾部新增:
@Override
public void sendAtToModem(String at_string, Message result) {
Rlog.e(LOG_TAG, "sendAtToModem Error! This class is a abstract class.");
}
Phone类的说明:
public abstract class Phone extends Handler implements PhoneInternalInterface
因Phone.java是一个抽象类,所以不能实现化,所以当有人调用Phone.java中对应的接口时,我们一律都认为是非法的。
1.2.3 GsmCdmaPhone.java的扩展
在initOnce()里面新增:
mCi.registerForAtTunnel(this, EVENT_UNSOL_AT_TUNNEL, null);
但在Android N里面mCi.unregisterForAvailable()找不到,dispose()也消失了,所以我们暂时不理它。(欢迎大家补充)
之后,在handleMessage()中添加消息处理:
case EVENT_UNSOL_AT_TUNNEL:
ar = (AsyncResult)msg.obj;
log("receive EVENT_UNSOL_AT_TUNNEL done");
if (ar.exception == null) {
String result = (String)ar.result;
log("result = " + result);
sendResultBroadcast(result);
}
break;
在尾部添加:
private void sendResultBroadcast(String result) {
Intent intent = new Intent("android.intent.action.AtCommand.result");
intent.putExtra("result", result);
mContext.sendBroadcast(intent);
}
@Override
public void sendAtToModem(String at_string, Message result) {
mCi.sendAtToModem(at_string, result);
}
1.3 RILJ层的扩展
1.3.1 CommandsInterface.java的扩展
//Add for AT tunnel to modem
void sendAtToModem(String at_string, Message result);
void registerForAtTunnel(Handler h, int what, Object obj);
void unregisterForAtTunnel(Handler h);
1.3.2 SimulatedCommands.java的扩展
为了保证编译通过:
@Override
public void sendAtToModem(String at_string, Message result){
}
1.3.3 BaseCommands.java的扩展
头文件:
import android.os.Message;
添加一个Registrant变量,用于注册主动上报的消息:
protected Registrant mAtTunnelRegistrant;
在最后添加重写的方法:
/**
* Sets the handler for AT sync tunnel
*
* @param h Handler for notification message.
* @param what User-defined message code.
* @param obj User object.
*/
@Override
public void registerForAtTunnel(Handler h, int what, Object obj) {
mAtTunnelRegistrant = new Registrant(h, what, obj);
}
@Override
public void unregisterForAtTunnel(Handler h) {
mAtTunnelRegistrant.clear();
}
@Override
public void sendAtToModem(String at_string, Message result) {
}
1.3.4 RIL.java的扩展
在processSolicited()中添加:
case RIL_REQUEST_SEND_AT: ret = responseString(p); break;
在processUnsolicited()中添加:
消息返回类型处理:
case RIL_UNSOL_RESPONSE_TUNNEL_AT: ret = responseString(p); break;
消息上报处理:
case RIL_UNSOL_RESPONSE_TUNNEL_AT: {
if (RILJ_LOGD) unsljLogRet(response, ret);
if (mAtTunnelRegistrant != null) {
mAtTunnelRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
}
在requestToString()中添加:
case RIL_REQUEST_SEND_AT: return "SEND_AT";
在responseToString()中添加:
case RIL_UNSOL_RESPONSE_TUNNEL_AT: return "RESPONSE_TUNNEL_AT";
在尾部添加:
@Override
public void sendAtToModem(String at_string, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEND_AT, result);
rr.mParcel.writeString(at_string);
if (RILJ_LOGD) riljLog(rr.serialString() +
"> sendAtToModem: " + requestToString(rr.mRequest)
+ " at_string: " + at_string);
send(rr);
}
2、 RILC层的扩展
2.1 ril.h的扩展
添加主动请求和主动上报的两个消息,需要与RILConstants.java中的定义变量一致:
#define RIL_REQUEST_SEND_AT 336
#define RIL_UNSOL_RESPONSE_TUNNEL_AT 1052
2.2 ril_unsol_commands.h的扩展
添加主动上报的类型处理,由于该上报为一个字符串,所以使用responseString即可:
{RIL_UNSOL_RESPONSE_TUNNEL_AT, responseString, WAKE_PARTIAL}
2.3 ril_commands.h的扩展
添加主动请求返回的类型处理,此处也是用responseString即可:
{RIL_REQUEST_SEND_AT, dispatchString, responseString}
2.4 reference-ril.c的扩展
2.4.1 onRequest()主动请求的扩展
在onRequest()中添加处理RILJ发送过来的AT命令(主动请求):
消息处理:
case RIL_REQUEST_SEND_AT:
requestSendAt(data, datalen, t);
break;
消息调用:
static void requestSendAt(void *data, size_t datalen, RIL_Token t)
{
int err;
char *cmd;
char *response;
ATResponse *p_response = NULL;
RLOGD("requestSendAt data = %s, datalen = %d", (char *)data, datalen);
assert (datalen != 1);
asprintf(&cmd, "%s", (char *)data+1);
response = (char *)data;
if(response[0] == '1'){
err = at_send_command(cmd, &p_response);
}else if(response[0] == '2'){
err = at_send_command_singleline(cmd, "", &p_response);
}else if(response[0] == '3'){
err = at_send_command_numeric(cmd, &p_response);
}else if(response[0] == '4'){
err = at_send_command_multiline(cmd, "", &p_response);
}else{
err = -1;
}
if (cmd != NULL) {
free(cmd);
cmd = NULL;
}
RLOGD("requestSendAt err = %d, p_response->success = %d", err, p_response->success);
if (p_response->p_intermediates == NULL) {
RLOGD("requestSendAt finalResponse = %s", p_response->finalResponse);
asprintf(&response, "%s\r\n", p_response->finalResponse);
} else {
RLOGD("requestSendAt finalResponse = %s, p_intermediates->line = %s",
p_response->finalResponse, p_response->p_intermediates->line);
asprintf(&response, "%s, %s\r\n", p_response->p_intermediates->line,
p_response->finalResponse);
}
if (err < 0 || p_response->success == 0)
/*Maybe the at command from user is invalid, we also send successful response to user,
the result should handle it itself*/
goto error;
RLOGD("requestSendAt success, response = %s, len = ", response, strlen(response));
RIL_onRequestComplete(t, RIL_E_SUCCESS, response, strlen(response));
free(response);
return;
error:
RLOGE("ERROR: requestSendAt failed, response = %d", response);
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
free(response);
}
注意:
你需要知道下发的命令要不要求返回数据,如果不要求一般用at_send_command即可,
要求就用at_send_command_singleline。这个不能出错,否则RILJ和RILC之间的socket会断开。
这是对底层上报的字符串进行了处理,判断AT不同情况的时作出的不同处理。
调用RIL_onRequestComplelte将结果返回给上层,返回的是一个字符串。
该字符串被RILJ层的消息封装,并发给PhoneInterfaceManager.java进行下一步的处理。
2.4.2 onUnsolicited()主动上报的扩展
在onUnsolicited()中添加主动上报消息的处理(if-else的最后添加):
else{
if(!s) {
line = strdup(s);
memset(atReadStorage, 0, 1024);
strncpy(atReadStorage, line, strlen(line));
p = atReadStorage + strlen(line);
if(!sms_pdu) {
strncpy(p, sms_pdu, strlen(sms_pdu));
}
RIL_onUnsolicitedResponse (
RIL_UNSOL_RESPONSE_TUNNEL_AT,
atReadStorage, strlen(atReadStorage));
free(line);
}
}
注意:这个要放在已有的if-else语句的最后,目的是把过滤剩下的消息全部当成RIL_UNSOL_RESPONSE_TUNNEL_AT上发,其中atReadStorage为静态内存块。
static char atReadStorage[1024];
此处理解比较浅,欢迎补充。
3、 PhoneInterfaceManager层的扩展
PhoneInterfaceManager的扩展主要有以下:
1) 实现TelephonyManager.java提供的两个接口。
2) 提供返回值的等待过程。发送完命令后,等RIL层的响应。
3) 将结果处理后,返回给第三方应用。
3.1 实现两个接口
/**
* Send AT command via unsync tunnel, it should return true or false
* when AT command has been send.
* @param cmd AT command
* return boolean
*/
public boolean AtCommandSendUnSync(String cmd){
Log.d(LOG_TAG, "AtCommandSendUnSync send at command" + cmd);
Phone phone = mPhone;
if (phone == null) return false;
final AtSendThread atSendThread = new AtSendThread("AtCommandSendUnSync", cmd, false);
atSendThread.start();
String result = atSendThread.sendAt(phone);
sendResultBroadcast(result);
if (result != null && result.length() > 1 && result.contains("OK")) {
return true;
} else {
return false;
}
}
/**
* Send AT command via sync tunnel, it should return result until the command execute completely.
* @param cmd AT command
* @param time max time for executing at command, unit is ms.
* return is result of AT.
*/
public String AtCommandSendSync(String cmd, int time){
Log.d(LOG_TAG, "AtCommandSendSync send at command" + cmd + " time = " + time);
Phone phone = mPhone;
if (phone == null) return null;
final AtSendThread atSendThread = new AtSendThread("AtCommandSendSync", cmd, true, time);
atSendThread.start();
return atSendThread.sendAt(phone);
}
3.2 发送与等待的过程
private static class AtSendThread extends Thread {
private String mCmd;
private long mMaxTimeExcute;
private String mAtResult;
private boolean mIsSync;
private Handler mAtHandler;
private boolean mSuccess = false;
private static final int SEND_AT_VIA_TUNNEL = 1;
AtSendThread(String name, String cmd, boolean isSync) {
super(name);
mCmd = cmd;
mAtResult = null;
mMaxTimeExcute = 5;
mIsSync = false;
}
AtSendThread(String name, String cmd, boolean isSync, int max) {
super(name);
mCmd = cmd;
mMaxTimeExcute = (long)(max/100);
mAtResult = null;
mIsSync = isSync;
}
public void run() {
Looper.prepare();
synchronized (AtSendThread.this) {
mAtHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
switch (msg.what) {
case SEND_AT_VIA_TUNNEL:
Log.d("AtSyncThread", "SEND_AT_VIA_TUNNEL");
synchronized (AtSendThread.this) {
if (ar.exception == null && ar.result != null) {
mAtResult = ar.result.toString();
}else{
mAtResult = "Error AT response";
}
mSuccess = true;
AtSendThread.this.notifyAll();
}
break;
}
}
};
AtSendThread.this.notifyAll();
}
Looper.loop();
}
synchronized String sendAt(Phone phone) {
while (mAtHandler == null) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
Message callback = Message.obtain(mAtHandler, SEND_AT_VIA_TUNNEL);
Log.e(LOG_TAG, "mCmd = " + mCmd);
phone.sendAtToModem(mCmd, callback);
while (!mSuccess) {
try {
Log.d("AtSendThread", "wait for done");
mMaxTimeExcute--;
wait(100);
if (mMaxTimeExcute == 0) {
mAtResult = "Error AT TIME OUT";
return mAtResult;
}
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
Log.d("AtSendThread", "successfull! result = " + mAtResult);
return mAtResult;
}
}
分解下该Thread, 分成两个构造函数,一个发送函数sendAt才是最终启作用的核心,就是这个函数能把异步的请求转给化为同步的结果返回给APK的。在sendAt函数中,使用了同步锁的机制,当发送完后,该线程处理wait()的状态,等待AT的结果。在循环多次,500MS还没有结果时,就认为超时退出。
3.3 结果返回给APP
private void sendResultBroadcast(String result) {
Intent intent = new Intent("android.intent.action.AtCommand.result");
intent.putExtra("result", result);
mApp.getPhone().getContext().sendBroadcast(intent);
}
4、 总结
请求:
APK -> TelephonyManager -> PhoneInterfaceManager -> GsmCdmaPhone -> RILJ -> rild -> reference-ril -> modem;
请求完成后,PhoneInterfaceManager中的线程处于阻塞等待状态。每过100ms检查下,是否有结果返回,直到500ms超时退出
返回:
modem -> reference-ril -> rild -> RILJ -> 通过消息注册方式直接回到PhoneInterfaceManager -> APK。