标签:
平时工作中,经常有这样的需求,就是从RIL层一直到APP,添加一个新接口给APP调用,方便系统APP或者第三方APP通过这个接口获取或者改变modem中某些的值、状态等。
基于高通平台,有三种方式去实现这种接口:
其中,第一种方式最简单,第二种次之,第三种最复杂;但是每种方式各自有各自的限制条件以及适用范围。
本文讲解如何扩展ImsSenderRxr,依赖ImsService来实现
限制条件:APP必须导入ims-common这个Library。
适用范围:高通平台IMS相关。
在讲解如何实现这个接口之前,我们先来分析下问什么可以依赖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交互。
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已实现的方法。
IImsService.aidl(frameworks\base\telephony\java\com\android\ims\internal)
在IImsService.aidl的尾部添加:
void getValueFromModem(in Message result);
void setValueToModem(int value ,in Message result);
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());
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对接口中的方法做了初步的实现,但是在这里没有做任何具体实现,方法都是空的。
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);
}
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;
}
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;
}
在第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中。
所以我目前想到的解决方案就是:
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这个参数。
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;
}
}
本文主要讲解了依赖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