码迷,mamicode.com
首页 > 移动开发 > 详细

Android 6.0 IMS流程(二)——接口扩展(从RIL到APP)

时间:2016-07-15 11:11:14      阅读:1738      评论:0      收藏:0      [点我收藏+]

标签:

平时工作中,经常有这样的需求,就是从RIL层一直到APP,添加一个新接口给APP调用,方便系统APP或者第三方APP通过这个接口获取或者改变modem中某些的值、状态等。
基于高通平台,有三种方式去实现这种接口:

  1. 扩展ImsSenderRxr,依赖ImsService来实现。
  2. 扩展RILJ,依赖Phone进程来实现。
  3. 结合1和2,依赖Phone进程来实现。

其中,第一种方式最简单,第二种次之,第三种最复杂;但是每种方式各自有各自的限制条件以及适用范围。


http://blog.csdn.net/linyongan


本文讲解如何扩展ImsSenderRxr,依赖ImsService来实现
限制条件:APP必须导入ims-common这个Library。
适用范围:高通平台IMS相关。

1、相关知识讲解

在讲解如何实现这个接口之前,我们先来分析下问什么可以依赖ImsService来扩展ImsSenderRxr。

1.1 ImsService与ImsSenderRxr的关系

技术分享
《 Android 6.0 IMS流程(一)——IMS开机初始化 》的1.2小节中,我们曾经讲解过ImsService的启动过程,在ImsService.java的onCreate()方法中做了两件非常重要的事情:
1. 创建ImsServiceSub对象,进而创建ImsSenderRxr对象。
2. 将ImsService添加成系统服务,这样子APP可以通过ImsManager去调用ImsService中mBinder里已实现的方法。

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d (LOG_TAG, "ImsService created!");
        //创建ImsServiceSub的数组
        mServiceSub = new ImsServiceSub[getNumSubscriptions()];
        for (int i = 0; i < getNumSubscriptions(); i++) {
            //1、创建ImsServiceSub对象
            mServiceSub[i] = new ImsServiceSub(i + 1, this);
        }
        //2、将ImsService添加成系统服务
        ServiceManager.addService("ims", mBinder);
        ......
    }

在ImsServiceSub的构造方法中:

    public ImsServiceSub(int sub, Context context) {
        //创建ImsSenderRxr对象,也就是说通过ImsServiceSub对象可以调用ImsSenderRxr里的方法。
        mCi = new ImsSenderRxr(mContext);
        //Initialize the UT interface associated with the sub.
        //ImsUtImpl在第三种方式中会用到,也是在ImsServiceSub这里被创建。
        ImsUtImpl.createUtInterface(mCi, mContext);
    }

所以,ImsSenderRxr是在ImsService初始化过程中被创建的,所以ImsService可以调用到ImsSenderRxr中的方法,进而可以跟modem交互。


1.2 ImsManager与ImsService的关系

ImsManager与ImsService处于不同的进程中,要跨进程访问,还是要通过AIDL。
ImsService本来就是个Service

public class ImsService extends Service {

在ImsService的mBinder实现了IImsService接口中的方法

    /*
     * Implement the methods of the IImsService interface in this stub
     */
    private final IImsService.Stub mBinder = new IImsService.Stub() {
    }

并且在ImsService初始化的时候把mBinder对象添加到ServiceManager中

ServiceManager.addService("ims", mBinder);

而ImsManager在初始化的时候通过ServiceManager得到ImsService的Binder对象,进而可以得到IImsService对象

    private ImsManager(Context context, int phoneId) {
        mContext = context;
        mPhoneId = phoneId;
        createImsService(true);
    }

    private void createImsService(boolean checkService) {
        if (checkService) {
            IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
            if (binder == null) {
                return;
            }
        }
        //通过ServiceManager得到ImsService的Binder对象
        IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
        if (b != null) {
            try {
                b.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
            }
        }
        //进而得到IImsService对象
        mImsService = IImsService.Stub.asInterface(b);
    }

所以我们可以直接通过mImsService去调用ImsService中mBinder已实现的方法。


2、具体实现

技术分享

2.1 定义接口

IImsService.aidl(frameworks\base\telephony\java\com\android\ims\internal)
在IImsService.aidl的尾部添加:

    void getValueFromModem(in Message result);
    void setValueToModem(int value ,in Message result);

2.2 调用接口中的方法

ImsManager.java (frameworks\opt\net\ims\src\java\com\android\ims)
在ImsManager的尾部添加:

    public void getValueFromModem(Message result) throws ImsException {
        //检查mImsService是否为空,如果再次获取mImsService依旧为空则抛出异常
        checkAndThrowExceptionIfServiceUnavailable();
        try {
            //通过mImsService调用IMSSerice中的方法
            mImsService.getValueFromModem(result);
        } catch (RemoteException e) {
            throw new ImsException("getValueFromModem() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
    }

    public void setValueToModem(int value , Message result) throws ImsException {
        //同上
        checkAndThrowExceptionIfServiceUnavailable();
        try {
            mImsService.setValueToModem(value,result);
        } catch (RemoteException e) {
            throw new ImsException("setValueToModem() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
    }

ImsManager中的这两个方法是提供给APP使用的,通过下面这行代码,APP就可以获取到ImsManager对象。

    //传递context和PhoneId即可得到ImsManager对象
    ImsManager imsManager = ImsManager.getInstance(context,
                   SubscriptionManager.getDefaultVoicePhoneId());

2.3 实现接口中的方法

ImsServiceBase.java (frameworks\opt\net\ims\src\java\com\android\ims)
在ImsServiceBase的内部类ImsServiceBinder的尾部添加:

        @Override
        public void getValueFromModem(Message result) {
            onGetValueFromModem(result);
        }

        @Override
        public void setValueToModem(int value , Message result) {
            onSetValueToModem(value,result);
        }

在ImsServiceBase的尾部添加:

    protected void onGetValueFromModem(Message result) {
        // no-op
    }

    protected void onSetValueToModem(int value , Message result) {
        // no-op
    }

ImsServiceBase对接口中的方法做了初步的实现,但是在这里没有做任何具体实现,方法都是空的。

2.4 真正实现接口中的方法

ImsService.java (vendor\qcom\proprietary\telephony-apps\ims\src\org\codeaurora\ims)
在ImsService的mBinder的尾部添加:

        public void getValueFromModem(Message result) {
            mServiceSub[0].getValueFromModem(result);
        }

        public void setValueToModem(int value , Message result) {
            mServiceSub[0].setValueToModem(value,result);
        }

ImsServiceSub.java (vendor\qcom\proprietary\telephony-apps\ims\src\org\codeaurora\ims)
在ImsServiceSub的尾部添加:

    public void getValueFromModem(Message result) {
        mCi.getValueFromModem(result);
    }

    public void setValueToModem(int value , Message result){
        mCi.setValueToModem(value,result);
    }

2.5 定义请求的消息和数据的结构

imsIF.proto(vendor\qcom\proprietary\telephony-apps\ims\src)
在imsIF.proto中enum MsgId的尾部添加:

    //不一定从101开始
    REQUEST_GET_VALUE_FROM_MODEM = 101;
    REQUEST_SET_VALUE_TO_MODEM = 102;

在imsIF.proto的尾部添加:

//按自己的需要定义
enum EnableFunction {
    FALSE = 0;
    TRUE = 1;
}
//按自己的需要定义
message FunctionConfig {
    optional EnableFunction enableFunction = 1;
}

2.6 实现发送请求的方法和对modem返回数据处理的方法

ImsSenderRxr.java (vendor\qcom\proprietary\telephony-apps\ims\src\org\codeaurora\ims)
在ImsSenderRxr的尾部添加:

    public void getValueFromModem(Message result) {
        logv("getValueFromModem");
        encodeMsg(ImsQmiIF.REQUEST_GET_VALUE_FROM_MODEM, result, null);
    }

    public void setValueToModem(int value , Message result) {
        logv("setValueToModem " + "value= " + value);
        //对APP传递下来的value进行处理
        ImsQmiIF.FunctionConfig function = new ImsQmiIF.FunctionConfig();
        function.setEnableFunction(value);
        //将对象转成byte数组
        byte[] functionb = function.toByteArray();
        //在encodeMsg里会进一步把数据封装到IFRequest中,
        //跟RILJ一样,通过send()方法把数据与请求发送给Qcril。
        encodeMsg(ImsQmiIF.REQUEST_SET_VALUE_TO_MODEM, result, functionb);
    }

在ImsSenderRxr的msgIdToString()方法的尾部添加:

    case ImsQmiIF.REQUEST_GET_VALUE_FROM_MODEM:
        return "REQUEST_GET_VALUE_FROM_MODEM";
    case ImsQmiIF.REQUEST_SET_VALUE_TO_MODEM:
        return "REQUEST_SET_VALUE_TO_MODEM";

modem返回消息给ImsSenderRxr,就会执行ImsSenderRxr的processSolicited()方法
在ImsSenderRxr的processSolicited()方法中添加:

     //根据不同的请求调用相应的处理方法
     case ImsQmiIF.REQUEST_GET_VALUE_FROM_MODEM:
         ret = responseGetValueFromModem(message);
         break;
     case ImsQmiIF.REQUEST_SET_VALUE_TO_MODEM:
         ret = responseSetValueToModem(message);
         break;    

在ImsSenderRxr的尾部添加:

    protected Object responseGetValueFromModem(byte[] valueInfo) {
        //最终返回的是int类型的数组,这个数组会存储到AsyncResult.result中
        //后面第3小节会讲解如何得到这个数组并且对里面的数据再处理
        int[] response = null;

        if (valueInfo != null) {
            log("response GetValueFromModem");
            //对modem返回来的数据进行处理,将valueInfo中的数据存储到response中
        } else {
            response = new int[0];
        }
        return response;
    }

    protected Object responseSetValueToModem(byte[] valueInfo) {
        //同上
        int[] response = null;

        if (valueInfo != null) {
            log("response SetValueToModem");
        } else {
            response = new int[0];
        }
        return response;
    }

3、扩展的问题

在第2小节中所讲解的实现方式,需要一开始在APP通过obtainMessage()方法得到一个Message,这个Message会一直传递到ImsSenderRxr;等到modem处理完get/set请求之后,ImsSenderRxr会调用Message.sendToTarget()方法,通过回调来通知APP。
但是这种实现方式是有限制条件的:APP必须加入到Phone进程中。

<manifest
   ......
   android:sharedUserId="android.uid.phone">
   <application
   android:process="com.android.phone">
    </application>
</manifest>

所以现在还需引出一个问题,如何让APP不需要加入Phone进程中又可以正常地调用接口?
假如APP不加入到Phone进程中的话,Message是无法传递到ImsService,也就无法传递到ImsSenderRxr中。
所以我目前想到的解决方案就是:

3.1 不需要APP传递Message下来

ImsManager.java (frameworks\opt\net\ims\src\java\com\android\ims)

    public void getValueFromModem() throws ImsException {
        checkAndThrowExceptionIfServiceUnavailable();
        try {
            mImsService.getValueFromModem();
        } catch (RemoteException e) {
            throw new ImsException("getValueFromModem() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
    }

    public void setValueToModem(int value) throws ImsException {
        checkAndThrowExceptionIfServiceUnavailable();
        try {
            mImsService.setValueToModem(value);
        } catch (RemoteException e) {
            throw new ImsException("setValueToModem() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
        }
    }

从ImsManager一直到ImsServiceSub所有方法都去掉Message这个参数。

3.2 在ImsServiceSub中通过obtainMessage()方法得到一个Message

    private final int EVENT_GET_VALUE_FROM_MODEM = 101;
    private final int EVENT_SET_VALUE_TO_MODEM = 102;

    public void getValueFromModem(Message result) {
        mCi.getValueFromModem(mHandler.obtainMessage(EVENT_GET_VALUE_FROM_MODEM));
    }

    public void setValueToModem(int value , Message result){
        mCi.setValueToModem(value , mHandler.obtainMessage(EVENT_SET_VALUE_TO_MODEM));
    }

然后通过回调,在ImsServiceSub中就可以得到modem返回来的结果,此时再通过广播通知APP。

        @Override
        public void handleMessage(Message msg) {
            Log.d (LOG_TAG, "Message received: what = " + msg.what);
            AsyncResult ar;

            switch (msg.what) {
                case EVENT_GET_VALUE_FROM_MODEM:
                    if (ar != null && ar.exception == null && ar.result != null) {
                        //successs
                        //把int数组取出来,再做相应的处理
                        int[] resultArray = (int[]) ar.result;
                    }else{
                        //fail
                    }
                    //最后通过广播把结果传递给APP
                    break;
                case EVENT_SET_VALUE_TO_MODEM:
                    //同上
                    break;
            }
       }

4、总结

本文主要讲解了依赖IMS,如何最快地添加一条通路(从RIL到APP),但是这个接口目前还是不能使用的,因为Qcril和QMI还有很多代码需要写。如果为了测试从RIL到APP这条通道是否能正常工作,可以借用现有的方法来测试:
如在ImsSenderRxr中,

public void setValueToModem(int value , Message result) {
    //可以直接调现有方法来测试
    setUiTTYMode(value,result);
}

Android 6.0 IMS流程(二)——接口扩展(从RIL到APP)

标签:

原文地址:http://blog.csdn.net/linyongan/article/details/51868260

(0)
(2)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!