Android N RIL AT command synchronization & asynchronous extension process

Keywords: Java Android socket

Combining two blog posts:
1. The original address: http://blog.csdn.net/guoleimail/article/details/41649537
2. Optimize the original address: http://blog.csdn.net/yuxiangyunei/article/details/42809543

Reference blog:
1,http://blog.csdn.net/linyongan/article/details/52189217
2,http://blog.csdn.net/linyongan/article/details/52151651

Note: The above two blog codes have been run, this article is based on experience and 7.0 code flow expansion, specific verification is still in progress.
Next, we will combine two articles to explain in detail.

Interpretation of synchronization and asynchrony:
Synchronized way, after calling, wait for MODEM to return the results, and return the results to third-party applications;
In an asynchronous way, after invocation, there is no need to wait for the result of MODEM to return. When the response of MODEM is completed, it is sent out by broadcasting for the application to receive.

1. Extension of Framework Layer

1.1 Telephone Extension under Base Module

1.1.1 Extension of ITelephony.aidl

Add:

    /** 
     * 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);   

But the implementation is: public class PhoneInterface Manager extends ITelephony. Stub, which will be skipped here later.

1.1.2 Extension of TelephonyManager. Java

Add:

/** 
 * 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() calls the interface in PhoneInterfaceManager.java, which is used to make calls between processes.
Call method:

/**
 * @hide
 */
 private ITelephony getITelephony() {
     return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
 }

1.1.3 Extension of RILConstants. Java

int RIL_REQUEST_SEND_AT = 138; (Android M is now 137)
int RIL_UNSOL_RESPONSE_TUNNEL_AT = 1046; (Android M is now 1045)

Regardless of how much, the values of these two messages must be added sequentially thereafter.

Extension of telephony under 1.2 opt module

Android 7.0 Change Description:
1. GSM Phone and CDMA Phone are merged into GsmCdmaPhone.
2. Phone. Java has replaced PhoneBase. Java (with minor modifications to the internal method), and Phone. Java has become the central hub of all relationships.
3. The interface becomes Phone International Interface, because PhoneProxy no longer exists, only Phone.java is left to implement the Phone International Interface interface, so a large number of register/unregister interfaces in Phone International Interface have been deleted. These register/unregister methods can be left in Phone.java, and the Phone International Interface interface has become more streamlined.

1.2.1 Extension of Phone International Interface. Java

Add:

//Add AT tunnel  
void sendAtToModem(String at_string, Message result);  

1.2.2 Extension of Phone. Java

Add a new message:

protected static final int EVENT_UNSOL_AT_TUNNEL = 43; (Android M currently reaches 42)

Add:

@Override  
public void sendAtToModem(String at_string, Message result) {  
    Rlog.e(LOG_TAG, "sendAtToModem Error! This class is a abstract class.");
}

Description of Phone class:
public abstract class Phone extends Handler implements PhoneInternalInterface
Because Phone.java is an abstract class, it cannot be implemented, so when someone calls the corresponding interface in Phone.java, we all consider it illegal.

Extension of 1.2.3 GsmCdmaPhone.java

In initOnce(), add:

mCi.registerForAtTunnel(this, EVENT_UNSOL_AT_TUNNEL, null);  

But in Android N, mCi.unregisterForAvailable() could not be found and dispose() disappeared, so we ignored it for the time being. (Welcome to add)
After that, add message processing in 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; 

Add:

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 Extension of RILJ Layer

1.3.1 Extension of 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 Extension of Simulated Commands. Java

In order to ensure that the compilation passes:

@Override  
public void sendAtToModem(String at_string, Message result){  
}

1.3.3 Extension of BaseCommands. Java

Head file:

import android.os.Message; 

Add a Registrant variable to register messages that are reported actively:

protected Registrant mAtTunnelRegistrant;  

Finally, add the rewrite method:

  /**  
   * 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 Extension of RIL. Java

In process Solicited (), add:

case RIL_REQUEST_SEND_AT: ret =  responseString(p); break;  

In processUnsolicited(), add:
Message return type processing:

case RIL_UNSOL_RESPONSE_TUNNEL_AT: ret =  responseString(p); break;  

Message reporting processing:

case RIL_UNSOL_RESPONSE_TUNNEL_AT: {  
                if (RILJ_LOGD) unsljLogRet(response, ret);  

                if (mAtTunnelRegistrant != null) {  
                    mAtTunnelRegistrant.notifyRegistrant(  
                                        new AsyncResult (null, ret, null));  
                }  
                break;  
            }  

Add in requestToString():

case RIL_REQUEST_SEND_AT: return "SEND_AT";

Add in responseToString():

case RIL_UNSOL_RESPONSE_TUNNEL_AT: return "RESPONSE_TUNNEL_AT";  

Add:

@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. Extension of RILC Layer

Extension of 2.1 ril.h

Adding two messages, active request and active report, needs to be consistent with the defined variables in RILConstants.java:

 #define RIL_REQUEST_SEND_AT 336  
 #define RIL_UNSOL_RESPONSE_TUNNEL_AT 1052  

Extension of 2.2 ril_unsol_commands.h

Adding type processing for active reporting, since the reporting is a string, you can use response String:

{RIL_UNSOL_RESPONSE_TUNNEL_AT, responseString, WAKE_PARTIAL} 

Extension of 2.3 ril_commands.h

Add the type processing returned by the active request, and use responseString here as well:

{RIL_REQUEST_SEND_AT, dispatchString, responseString} 

Extension of 2.4 reference-ril.c

2.4.1 Extension of onRequest () Active Request

In onRequest(), add the AT command (active request) that handles the RILJ sending:
Message processing:

case RIL_REQUEST_SEND_AT:  
        requestSendAt(data, datalen, t);  
        break; 

Message invocation:

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

Be careful:
You need to know whether the command issued requires data return or at_send_command.
at_send_command_singleline is required. This cannot go wrong, otherwise the socket between RILJ and RILC will be disconnected.

This is the processing of the underlying reported strings and different processing when judging different AT situations.
Calling RIL_onRequestComplelte returns the result to the upper level, returning a string.
The string is encapsulated by RILJ layer messages and sent to PhoneInterfaceManager.java for further processing.

2.4.2 on Unsolicited () Extension of Active Reporting

Add active reporting processing to onUnsolicited() (the last addition of 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);  
  }  
} 

Note: This is placed at the end of the existing if-else statement in order to send all the remaining filtered messages as RIL_UNSOL_RESPONSE_TUNNEL_AT, where atReadStorage is a static memory block.

static char atReadStorage[1024];  

This understanding is relatively shallow, welcome to add.

3. Extension of Phone Interface Manager Layer

PhoneInterface Manager extensions include the following:
1) Implement two interfaces provided by TelephonyManager.java.
2) Provide the waiting process for the return value. After sending the command, wait for the response from the RIL layer.
3) After processing the results, they are returned to third-party applications.

3.1 Implementing Two Interfaces

   /** 
    * 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 The process of sending and waiting

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

The Thread is decomposed and divided into two constructors. SendAt is the core of the function, which can transfer asynchronous requests to APK as synchronous results. In the sendAt function, the synchronous lock mechanism is used. After sending, the thread processes the state of wait() and waits for the result of AT. When the 500 MS has not yet produced a result, it is considered to be out of time after many cycles.

3.3 Result returned to 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, summary

Request:
APK -> TelephonyManager -> PhoneInterfaceManager -> GsmCdmaPhone -> RILJ -> rild -> reference-ril -> modem;
After the request is completed, the threads in PhoneInterface Manager are blocked and waiting. Whether there is a result returned after every 100 ms check until 500 ms time-out

Return:
Modem - > reference - RIL - > RILD - > RILJ - > returns directly to Phone Interface Manager - > APK through message registration.

Posted by raymedia on Thu, 27 Dec 2018 18:57:07 -0800