当前位置: 代码迷 >> Android >> Android kitkat RIL 请求扩张及主动上报扩展
  详细解决方案

Android kitkat RIL 请求扩张及主动上报扩展

热度:411   发布时间:2016-04-28 03:35:39.0
Android kitkat RIL 请求扩展及主动上报扩展

    在最近的工作中,遇到一个需求,需要让第三方应用向MODEM层请求AT命令,且有两种响应方式,分别为同步方式和异步方式,同步的情况下,调用后要等MODEM返回结果后,将结果送给第三方应用, 异步的方式,采用等MODEM响应后,通过广播发送出去,让应用接收。鉴于目前大部分市面的MODEM都是通过AT来交互的,特有此需求。

1. 需求分析:

A. 由于需求要求的是第三方应用可以使用,那就是注定了,不是在PHONE的进程中,需要扩展TelephonyManager相关的接口,以及AIDL供其它调用。复杂。

B. 两种发送模式,归结到一起都是一样的,所以,在RIL层添加一条请求消息即可,扩展RIL层的请求消息。较复杂,

C. 主动上报的消息扩展。   比较简单,very easy.

D. 修改RIL C的请求函数,将请求发送给MODEM,并将结果送回RIL.JAVA

2. 可行性分析:

A. 第三方应用要使用,肯定扩展Telephony.aidl这个文件,这个应该问题不大,可以实现,估计4天。

B. 两种发送模式对于全是异步的RIL层来说,都是一样的,只是处理结果时不一样而已, 估计2天。

C. 该块是需求的重点,可行性问题不大,就是比较烦而已,  主要是调试环境复杂, 估计10天

D. 该处理是直接命令透传即可,不用太费时间,估计1天。

通过以上的个个分析,该需求应该是可以实现的,主要时间消耗在RIL层的扩展,其它都相对来说比较简单。

3. 需求实现

3.1. Framework/base的扩展

该块修改需要重新编译framework/base,将编出来的framework.jar. framework2.jar推到手机中即可。

    3.1.1. ITelephony.aidl扩展。

文件位置:telephony/java/com/android/internal/telephony/ITelephony.aidl。为了方便第三方调用该接口,需要扩展ITelephony.aidl文件中的接口,AIDL说白了,就是一个接口,便于不同进程之间的调用。而AIDL在编译时,会被编译成对应的java文件,最重要的是里面有一个stub, 这个才是进程通讯的核心,具体的内容不在这多说。但大家可以注意下PhoneInterfaceManager.java的声明类型。

public class PhoneInterfaceManager extends ITelephony.Stub

它真是实现第三方应用可以调用该接口的问题所在。

/** * 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);<pre name="code" class="java">        int RIL_UNSOL_RESPONSE_TUNNEL_AT = 1052;

/** * Execute AT command via sync tunnel * @param cmd AT command, time is timeout of process unit is ms */String AtCommandSendSync(String cmd, int time);

     3.1.2. telephony/java/com/android/internal/telephony/RILConstants.java

     扩展主动请求和主动上报的消息,注意要与ril.h中的定义的变量是一致,否则无法收到了对应的请求。

     

    int RIL_REQUEST_SEND_AT = 336;    int RIL_UNSOL_RESPONSE_TUNNEL_AT = 1052;

3.1.3. telephony/java/android/telephony/TelephonyManager.java扩展方法

Teel(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

   /**    * 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中的接口,就是用过这个来达到进程之间的调用。该机制是Android自带的,其根本的方式还是通过binder的方式,来达到。

3.2. Framework/opt/telephony扩展

该处修改最终最需要PUSH一个JAR即可,telephony-common.jar包到system/framework即可。

3.2.1 Phone接口的扩展

文件路径:src/java/com/android/internal/telephony/Phone.java
        由于ITelephony中最后都是调用的Phone接口,所以必须扩展Phone.java。
    //Add AT tunnel    void sendAtToModem(String at_string, Message result);
为了编译通过,类似的需要扩展PhoneBase.java, PhoneProxy.java。

3.2.2 PhoneBase.java扩展

文件路径:src/java/com/android/internal/telephony/PhoneBase.java
        因PhoneBase.java是一个抽象类,所以不能实现化,所以当有人调用PhoneBase.java中对应的接口时,我们一律都认为是非法的,所以返回一个错误的LOG。
    @Override    public void sendAtToModem(String at_string, Message result){        Rlog.e(LOG_TAG, "sendAtToModem Error! This function is only for GSMPhone.");    }
只允许该接口从PhoneBase的子类调用,如GSMPhone, CDMAPhone.SipPhone等。

3.2.3 PhoneProxy.java扩展

文件路径:src/java/com/android/internal/telephony/PhoneProxy.java,该处PhoneProxy最终也是调用GSMPhone中的接口。

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

3.2.4 GSMPhone的扩展

文件路径:src/java/com/android/internal/telephony/PhoneProxy.java
GSMPhone中需要做三件事情,分别列举如下:
A. 注册主动上报的事件,当有主动上报时,转到处理主动上报的代码。
B. 发送广播,当收到主动上报时,发送主动上报内容给APP.
C. 给PhoneInterfaceManager.java提供接口支持。
代码如下:
        mCi.registerForAtTunnel(this, EVENT_UNSOL_AT_TUNNEL, null);
构造GSMPhone的时候,监听该事件。当RIL.java有事情上报时,转到对应的处理代码。
             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(ACTION_AT_COMMAND_RESULT);        intent.putExtra(RESULT_KEY, result);        mContext.sendBroadcast(intent);    }
添加PhoneInterfaceManager.java接口支持代码:
    @Override    public void sendAtToModem(String at_string, Message result) {        mCi.sendAtToModem(at_string, result);    }

3.3 RIL代码添加

A. 添加CommandInterface.java一个发送请求接口,完成消息监控,及消息分发,并保证编译通过。
B. RIL添加一个处理MODEM主动上报的接口,并将内容发送给GSMPhone.java处理。
C. RIL添加一个处理发送请求的接口,将结果返回给PhoneInterfaceManager.java。

3.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);

3.3.2 BaseCommands.java扩展

在BaseCommands.java中添加监控事件的处理,并添加一个新的注册事件集mAtTunnelRegistrant, 代码如下:
    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) {    }
其主要作用,当有用户监控该事件后,就在注册事件集中添加该监控。而主动请求,由于发送时,已经明确了消息Handler,就知道消息发送给Message的注册Handler处理。

3.3.3 SipCommandInterface.java扩展

为了编译通过,将SipCommandInterface.java实现对应的CommandInterface接口。
    @Override    public void sendAtToModem(String at_string, Message result){    }

4 RIL C层的扩展

4.1 ril.h的扩展

文件路径:hardware/ril/include/telephony/ril.h
添加主动请求和主动上报的两个消息,需要与RILConstants.java中的定义变量一致。代码如下:
#define RIL_REQUEST_SEND_AT 336#define RIL_UNSOL_RESPONSE_TUNNEL_AT 1052

4.2 ril_unsol_commands.h扩展

文件路径:hardware/ril/libril/ril_unsol_commands.h。添加一个处理该主动上报类型,由于该上报的为一个字符串,所以使用responseString即可。代码如下:
    {RIL_UNSOL_RESPONSE_TUNNEL_AT, responseString, WAKE_PARTIAL}

4.3 ril_commands.h扩展

文件路径:hardware/ril/libril/ril_commands.h。用于指定主动请求返回的类型处理,此处也是用responseString即可。代码如下:
    {RIL_REQUEST_SEND_AT, dispatchString, responseString}

4.4 reference-ril.c扩展

文件路径: leadcore-ril/reference-ril.c,代码如下:
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);    err = at_send_command(cmd, &p_response);    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_SUCCESS, response, strlen(response));    free(response);}
这时对下层上报的字符串进行了处理,判断AT不同情况的时作出的不同处理。调用RIL_onRequestComplelte将结果返回给上层,返回的是一个字符串。该字符串被RIL.JAVA层的消息封装,并发给给PhoneInterfaceManager.java进行后一步的处理。

5. PhoneInterfaceManager.java扩展

PhoneInterfaceManager的扩展主要有以下:
A. 给TelephonyManager.java提供实现两个接口,对于下层的Phone来说,直接将该调用的参数(AT)传递给GSMPhone进行处理。
B. 提供返回值的等待过程。发送完命令后,等RIL层的响应。
C. 将结果处理后,返回给第三方应用。
给TelephonyManager提供接口。
    /**     * 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 = getPhone(0);        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 = getPhone(0);        if (phone == null) return null;        final AtSendThread atSendThread = new AtSendThread("AtCommandSendSync", cmd, true, time);        atSendThread.start();        return atSendThread.sendAt(phone);    }
从代码中可以看中,最重要的东西在起的线程中,将异步的请求转化为同步的返回结果。下面发下该线程的代码,该处是经过多次失败的偿试后得出的,只有这种方式可以最好的解决异步转同步的方法,另外里面设置有超时模式,一旦超时,将立刻返回,而不会被阻塞住。代码如下:
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();                                    }                                    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还没有结果时,就认为超时退出。
总结一下,该需求的方式,APK-> TelephonyManager-> PhoneInterfaceManager-> GSMPhone->RIL.JAVA->RILD->Reference-RIL-> MODEM, 请求完成后,PhoneInterfaceManager中的线程处于阻塞等待状态。每过100MS去检查下,是否有结果返回,直到500MS超时退出。而在回路时,MODEM -> Reference-Ril -> RILD ->
RIL.java -> 通过消息注册方式,直接回到PhoneInterfaceManager -> APK.
  相关解决方案