Android N RIL层AT命令的同步&异步扩展流程

结合两篇博文:
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 目前已经到了137int 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。

在DuiLib中,HandleMessage函数是用于处理窗口消息的关键函数。它位于CDuiControl类中,是一个虚函数,可以在派生类中进行重写,以实现自定义的消息处理逻辑。 HandleMessage函数的原型如下: ```cpp LRESULT CDuiControl::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); ``` 参数解释: - uMsg:表示收到的消息类型,比如WM_PAINT、WM_KEYDOWN等。 - wParam:表示消息的附加参数,具体含义根据消息类型而定。 - lParam:表示消息的附加参数,具体含义根据消息类型而定。 返回值: - LRESULT:表示消息处理的结果,通常是0或者一个非零值。 在派生类中重写HandleMessage函数时,可以根据uMsg的不同值来处理不同的消息。常见的消息类型包括但不限于: - WM_CREATE:窗口创建消息。 - WM_DESTROY:窗口销毁消息。 - WM_PAINT:绘制窗口消息。 - WM_SIZE:窗口大小改变消息。 - WM_KEYDOWN、WM_KEYUP:键盘按键消息。 - WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_RBUTTONDOWN:鼠标消息等等。 重写HandleMessage函数时,可以根据具体需求进行相应的处理逻辑。例如,在WM_KEYDOWN消息中,可以判断按下的键盘按键是哪个键,并执行相应的操作。在WM_PAINT消息中,可以绘制窗口的内容。 需要注意的是,在重写HandleMessage函数时,需要调用父类的HandleMessage函数来处理未被重写的消息,以保证窗口的正常处理流程。 总之,HandleMessage函数是DuiLib中用于处理窗口消息的重要函数,通过重写该函数可以实现自定义的消息处理逻辑。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值