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

来源:互联网 发布:java流程图输入 编辑:程序博客网 时间:2024/05/21 10:54

    在最近的工作中,遇到一个需求,需要让第三方应用向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){    }

3.3.4 RIL.java的扩展

增加一个向RIL发送请求的接口,如下代码
    public void sendAtToModem(String at_string, Message result) {        RILRequest rr = RILRequest.obtain(RILConstants.RIL_REQUEST_SEND_AT, result);        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));        rr.mParcel.writeString(at_string);        if (RILJ_LOGD) riljLog("at_string = " + at_string);        send(rr);    }


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.
1 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 放逐之城铁采完怎么办 车钥匙反锁车内怎么办 眼镜用热水洗了怎么办 眼镜放平后眼镜腿不平怎么办 瞄准镜十字歪了怎么办 瞄准镜调到底了怎么办 墨镜镜片刮花了怎么办 usb小风扇不转怎么办 金属眼镜压歪了怎么办 眼镜被电焊打了怎么办 电焊闪的眼睛疼怎么办 烧了电焊眼睛疼怎么办 用了电焊眼睛痛怎么办 烧电焊脸上红痛怎么办 眼睛让电焊晃了怎么办 眼被电焊打了怎么办 眼镜弹簧腿坏了怎么办 眼镜框铰链坏了怎么办 金属眼镜框歪了怎么办 眼镜框螺丝断了怎么办 眼镜被压变形了怎么办 金属眼镜腿断了怎么办 眼镜弹簧腿断了怎么办 眼镜腿螺丝太紧怎么办 眼镜金属柄断了怎么办 金属眼镜腿折了怎么办 眼镜腿中间断了怎么办 塑料眼镜腿断了怎么办 眼镜上的螺丝拧不紧怎么办 眼镜的把坏了怎么办 把眼镜坐坏了怎么办 梦见眼镜腿掉了怎么办 眼镜的腿掉了怎么办 眼镜腿的螺丝掉了怎么办 爱大爱眼镜掉腿了怎么办 合金眼镜腿断了怎么办 手关节复位h疼痛怎么办 我叫mt红色卡牌怎么办 星盟冲突qq登录怎么办 汽车雷达下雨一直响怎么办 火山小视频封火力怎么办