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

你可能感兴趣的:(Android系统开发)