结合这几天的工作,谈下BREW通话状态监听的相关问题。
BREWAPIReference中**Use Case Scenarios**部分*ITelephone - Accessing an incoming call*部分给了两种监听"来电"的方法,由于第二种方法实现起来相对灵活,本文以第二种方法为例,旨在探讨如何监听电话通话的各个状态。
第一种方法通过设置MIF注册对“来电”事件的监听,该方法比较简洁,跟着代码写下去很容易实现对“来电”状态的监听,而第二种方法,直接跟着样例代码走,会有一些问题。由于通过MIF注册的方法监听,需要修改并重新编译MIF文件,并且需在IAPPLET_HandEvent函数中添加对EVT_Notify事件的判断,在不需要程序未运行监听通话状态的前提下,可以采用第二种监听方法。
第一种方法通过在MIF中注册 AEECLSID_PHONENOTIFIER(掩码AEET_NMASK_NEW_CALLDESC)实现对通话事件的监听,当有通话事件到来时,程序会收到EVT_Notify事件,可以在IAPPLET_HandEvent函数中处理,所有通过MIF注册事件的一个好处是程序不需要运行也可以收到相应的事件。第二种方法通过注册一个model listener来实现,个人觉得这种方法要灵活一些。
为阅读方便,这里贴上API上给出的两种访问“来电”的代码:
第一种方法:
{
AEENotify * pNotify = (AEENotify *) dwParam;
if (pNotify->dwMask & AEET_NMASK_NEW_CALLDESC)
{
AEETNotifyInfo * pTNotifyInfo = pNotify->pData;
if (pTNotifyInfo && (AEET_EVENT_CALL_INCOM ==pTNotifyInfo->event))
{
AEETCallEventData * pTCallEventData = (AEETCallEventData *)&(pTNotifyInfo->event_data);
ICALLMGR_GetCall(pMe->pICallMgr, pTCallEventData->cd, &pMe->pIncomingCall);
}
}
}
第二种方法:
{
Int nError;
IModel* pModel = NULL;
if (SUCCESS == ITELEPHONE_QueryInterface(pMe->pTelephone, AEEIID_MODEL, (void**) &pModel))
{
nError = IMODEL_AddListenerEx(pModel, &pMe->phoneListener, Applet_TelephoneListener, pMe);
IMODEL_Release(pModel);
if (SUCCESS != nError)
{
// failed to add listener
…
}
else
{
// listener added
…
}
}
}
void Applet_TelephoneListener (CApplet* pMe, ModelEvent* pEvent)
{
AEETCallEvent * pCallEvent = (AEETCallEvent*) pEvent;
if (pCallEvent && (AEET_EVENT_CALL_INCOM == pCallEvent->evCode))
{
AEETCallEventData * pTCallEventData = (AEETCallEventData *)&(pCallEvent->call);
ICALLMGR_GetCall(pMe->pICallMgr, pTCallEventData->cd, &pMe->pIncomingCall);
}
}
第二段代码中,IMODEL_AddListenerEx函数的第三个参数可能需要做PFNLISTENER类型转换,否则编译时会出现"implicit cast of pointer to non-equal pointer"的错误。
依照上面的示例代码很容易实现对“来电”状态的访问,而要进一步监听通话的其它状态,如:主动发起通话,通话过程中,通话结束等,还需要自己做一些扩展。由于第二种方法用起来灵活些,下面的部分我都以第二种方法为例。下面的部分是一开始我修改的对各种状态监听的代码:
if( pCallEvent )
{
switch(pCallEvent->evCode)
{
case AEET_EVENT_CALL_ORIG://发起通话
...
break;
case AEET_EVENT_CALL_ANSWER://来电应答
...
break
case AEET_EVENT_CALL_END://通话结束
...
break;
"""C//Accessing an incoming call
case AEET_EVENT_CALL_INCOM://来电
...
break;
"""
case AEET_EVENT_CALL_CONNECT:
...
break;
//other cases
........
default:
DBGPRINTF( "pCallEvent->evCode: 0x%04x", pCallEvent->evCode );
break;
}
}
在测试中发现,上面的代码不能真正实现自己对通话状态的监听。程序运行后,我没有发起一个通话,但是事件AEET_EVENT_CALL_ORIG依然响应了,接着是AEET_EVENT_CALL_CONNECT事件,然后AEET_EVENT_CALL_END,过不了一会儿又会重复这些事件。看起来是手机自己发起了一个通话事件,然后处于连接状态,一会儿通话结束,过会儿又会有再次出现这些事件。另外我只关注几个AEET_EVENT_CALL_事件,测试中日志上打印了大量的default事件:AEET_EVENT_SS_RSSI和AEET_EVENT_SS_HDR_RSSI,这是怎么回事呢?看来需要进一步得到手机当前更多的信息。
Accessing call information
Use Case Scenarios
ITelephone - Accessing call information
这部分样例程序提供了对Call Information的访问,这似乎给我带来了一些灵感,阅读代码,一种是通过 ITelephone接口的GetCallInfo方法得到:nError = ITELEPHONE_GetCallInfo(pITelephone, cd, &sTCallInfo, sizeof(sTCallInfo))需要知道参数cd;
另外一种是通过ICall接口的GetInfo方法,但得先得到ICall接口:ICALLMGR_GetCall(pMe->pICallMgr, pTCallEventData->cd, &pMe->pIncomingCall);也需要先知道参数cd。这两种方法其实是一样的,在只需要监听通话状态而不去控制通话状态的情况下,肯定使用第一种方法更加合适。查询cd,发现它是AEETCallEvent 的成员AEETCallEventData的AEECallDesc字段,既是说,通过上面的两种方法访问Call information需要先得到AEETCallEventData的cd成员,然后调用上面的方法得到AEETCallInfo,测试下发现果然可以。再来查看下
AEETCallEventData结构体,发现最后一个字段就是AEETCallInfo,也就是说在得到了AEETCallEventData的情况下,就可以直接得到AEETCallInfo了,这个AEETCallInfo和通过上面方法得到的AEETCallInfo是不是一样呢,测试下可以发现,它们完全一样。所以在得到AEETCallEvent 后,通过访问它的call成员可以得到AEETCallEventData结构,进一步访问该结构的call_info成员,就可以得到AEETCallInfo了。
为什么要得到AEETCallInfo呢?因为AEETCallInfo结构体中call_state和call_type成员非常重要。先说下call_type成员,它是AEETCallType枚举类型,在回调函数中打印该字段,可以发现,在没有语音通话时,该字段都是AEET_CALL_TYPE_PS_DATA,这应该是手机与基站保持联系的数据交换包类型,只有在语音通话时,该字段为AEET_CALL_TYPE_VOICE,所以可以据此判断当前是否为语音通话。进一步根据call_state字段内容判断当前处于何种状态,具体参考API。问题又回来了,看来只需要对原来的回调函数做下修改就可以实现对电话通话状态的监听。
void Applet_TelephoneListener (CApplet* pMe, ModelEvent* pEvent)
{
AEETCallEvent * pCallEvent = (AEETCallEvent*) pEvent
if( AEET_CALL_TYPE_VOICE == pTCallEventData->call_info.call_type )
{
switch( pTCallEventData->call_info.call_state )
{
case AEET_CALL_STATE_ORIG:
....//发起通话
break;
case case AEET_CALL_STATE_INCOM:
....//来电
break;
case AEET_CALL_STATE_CONV:
....//通话中
break;
case AEET_CALL_STATE_ENDED:
....//通话结束
break;
.......//其它状态
}
}
}
想要过滤掉无关事件,只处理AEET_EVENT_CALL_事件可以根据pCallEvent 的evCode过滤:
if( pEvent->evCode >= AEET_EVENT_CALL_ORIG && pEvent->evCode <= AEET_EVENT_CALL_EXT_BRST_INTL )。提下函数Applet_TelephoneListener (CApplet pMe, ModelEvent pEvent)的第二个参数,它是一个指向ModelEvent结构体的指针,查询下API:
Definition
typedef struct ModelEvent {
uint32 evCode;
IModel *pModel;
uint32 dwParam;
} ModelEvent;
Members
evCode: Event code that identifies the type of event.
pModel: Pointer to the model that sent the event.
dwParam: An event dependent field that will vary in meaning depending
on the event identified in the 'evCode' field.
在回调函数中,我们并没有使用ModelEvent的第三个成员dwParam,这个值是干什么用的呢?每次回调的时候把它打印出来,会发现它总是和AEETCallEventData的成员cd(AEECallDesc字段)相同,可以得出结论,这个值就是cd,所以在回调函数中,也可以通过这个值,用ITELEPHONE_GetCallInfo函数得到AEETCallInfo,这就是前面讲到的“Accessing call information”部分的内容了。
至此,完成了对通话状态的监听。
开始我只关注几个*AEET_EVENT_CALL_*事件,在真机上测试的时候,打印了大量的default事件:AEET_EVENT_SS_RSSI和AEET_EVENT_SS_HDR_RSSI,再回头看下*void Applet_TelephoneListener (CApplet* pMe, ModelEvent* pEvent)*函数,发现了问题所在,出在函数的第一行上
AEETCallEvent * pCallEvent = (AEETCallEvent*) pEvent;
想要过滤掉这些不用的事件,集中精力处理*AEET_EVENT_CALL_*事件可以根据pCallEvent 的evCode过滤:
if( pEvent->evCode >= AEET_EVENT_CALL_ORIG && pEvent->evCode <= AEET_EVENT_CALL_EXT_BRST_INTL )
函数ModelEvent的 dwParam字段是什么意思呢?
Use Case ScenariosITelephone - Accessing call information部分
nError = ITELEPHONE_GetCallInfo(pITelephone, cd, &sTCallInfo, sizeof(sTCallInfo));
到这里大致明白点了,之前对于事件的监听方法是有问题的,正在反应电话通话各个阶段的是AEETCallInfo,具体来说是它的两个成员call_state和call_type。先说下call_type成员,它是AEETCallType枚举类型,可以在回调函数中打印该字段,可以发现,不语言通话时,该字段都是AEET_CALL_TYPE_PS_DATA,这应该是手机与基站保存联系的数据交换包,只有在语音通话时,该字段为AEET_CALL_TYPE_VOICE,所以可以据此判断当前是否为语音通话。进一步根据call_state字段内容判断当前处于何种状态。
The following code snippet demonstrates obtaining access to ICall object when model listener is invoked:
void Applet_TelephoneListener (CApplet* pMe, ModelEvent* pEvent)
{
AEETCallEvent * pCallEvent = (AEETCallEvent*) pEvent;
"""C
if (pCallEvent && (AEET_EVENT_CALL_INCOM == pCallEvent->evCode))
"""
{
AEETCallEventData * pTCallEventData = (AEETCallEventData *)&(pCallEvent->call);
ICALLMGR_GetCall(pMe->pICallMgr, pTCallEventData->cd, &pMe->pIncomingCall);
}
}
至此完成对通话状态的监听。