/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
* This class handles asynchronous SDP queries
*
*/
// INCLUDE FILES
#include "basrvsdpquery.h"
#include "btaccTypes.h"
#include "debug.h"
enum TQueryRequestId
{
ECreateView = 70,
EGetResponse = 71
};
// ================= MEMBER FUNCTIONS =======================
CBasrvSdpQuery* CBasrvSdpQuery::NewL(MBasrvSdpQuerier& aQuerier)
{
CBasrvSdpQuery* self = new (ELeave) CBasrvSdpQuery(aQuerier);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
CBasrvSdpQuery::~CBasrvSdpQuery()
{
if (iSdpAgent)
{
iSdpAgent->Cancel();
}
delete iSdpAgent;
delete iSdpSearchPattern;
iServiceAttrs.Close();
delete iResponse;
iReg.Close();
delete iActive;
iRegServ.Close();
}
void CBasrvSdpQuery::QueryAccInfoL(const TBTDevAddr& aAddr, TBool aTolerateSdpError)
{
if (iSdpAgent)
{
User::Leave(KErrInUse);
}
iAddr = aAddr;
iServiceAttrs.Reset();
TBTSdpAttrInt SF; //TBTSupportedFeature SF;
SF.iAttrValue = 0;
SF.iUUID = KAudioSinkUUID;
SF.iAttrID = KSdpAttrIdSupportedFeatures;
iServiceAttrs.AppendL(SF); // Advanced Audio Distribution Profile - BT Stereo Audio
SF.iUUID = 0x111e; // HFP
SF.iAttrID = KSdpAttrIdSupportedFeatures;
iServiceAttrs.AppendL(SF); // BT Handsfree Profile - BT Mono Audio
SF.iUUID = 0x1108; // HSP
SF.iAttrID = 0x0302; // Atti id of remote volume control
iServiceAttrs.AppendL(SF); // BT Headset Profile - BT Mono Audio
SF.iUUID = KAVRemoteControlTargetUUID;
SF.iAttrID = KSdpAttrIdSupportedFeatures;
iServiceAttrs.AppendL(SF); // Audio Video Remote Control Profile
SF.iUUID = KAVRemoteControlTargetUUID;
SF.iAttrID = KSdpAttrIdBluetoothProfileDescriptorList;
iServiceAttrs.AppendL(SF); // Audio Video Remote Control Profile, to find out avrcp 1.4 support
/*****************************************************
* Series 60 Customer
* You may want to consider modifying the following
* code to better meet your needs.
*****************************************************/
// Perform remote SDP query to find out if information about the exact
// model of the accessory is available in the SDP registry. NOTE that
// this query if using a proprietary UUID which will only exist in some
// devices of a certain manufacturer. For other devices this query does
// not find a value.
//
TUUID serviceUuid = TUUID((0x00005555),(0x00001000),(0x80000002),(0xEE000001)); // model number
SF.iUUID = serviceUuid;
SF.iAttrID = 0x0300; // Attri id of device model number
iServiceAttrs.AppendL(SF);
iCursor = 0;
iTolerateSdpError = aTolerateSdpError;
iSdpAgent = CSdpAgent::NewL(*this, iAddr);
iSdpSearchPattern = CSdpSearchPattern::NewL();
DoNextServiceRecordRequestL();
}
void CBasrvSdpQuery::NextRecordRequestComplete(TInt aError,
TSdpServRecordHandle aHandle,
TInt aTotalRecordsCount)
{
TRAP_IGNORE(NextRecordRequestCompleteL(aError, aHandle, aTotalRecordsCount));
}
void CBasrvSdpQuery::AttributeRequestResult(
TSdpServRecordHandle aHandle,
TSdpAttributeID aAttrID,
CSdpAttrValue* aAttr)
{
TRAP_IGNORE(AttributeRequestResultL(aHandle, aAttrID, aAttr));
}
void CBasrvSdpQuery::AttributeRequestComplete(TSdpServRecordHandle aHandle, TInt aError)
{
TRAP_IGNORE(AttributeRequestCompleteL(aHandle, aError));
}
void CBasrvSdpQuery::RequestCompletedL(CBasrvActive& aActive)
{
switch (aActive.RequestId())
{
case ECreateView:
{
// if we're called with iResponse != NULL we're leaking memory.
__ASSERT_ALWAYS(!iResponse, User::Invariant());
iResponse = NULL;
if (aActive.iStatus > KErrNone)
{
TRAP_IGNORE( iResponse = CBTRegistryResponse::NewL(iReg) );
if (iResponse)
{
aActive.SetRequestId(EGetResponse);
iResponse->Start(aActive.iStatus);
aActive.GoActive();
}
}
else
{
QueryCompleteL(KErrNone);
}
break;
}
case EGetResponse:
{
QueryCompleteL(KErrNone);
break;
}
}
}
void CBasrvSdpQuery::CancelRequest(CBasrvActive& /*aActive*/)
{
}
void CBasrvSdpQuery::ConstructL()
{
}
CBasrvSdpQuery::CBasrvSdpQuery(MBasrvSdpQuerier& aQuerier)
: iQuerier(aQuerier)
{
}
void CBasrvSdpQuery::QueryCompleteL(TInt aErr)
{
iSdpAgent->Cancel();
delete iSdpAgent;
iSdpAgent = NULL;
delete iSdpSearchPattern;
iSdpSearchPattern = NULL;
TBTDeviceClass cod = TBTDeviceClass();
if ( iResponse && iResponse->Results().Count() > 0 )
{
cod = (iResponse->Results())[0]->DeviceClass();
}
delete iResponse;
iResponse = NULL;
if (!aErr)
{
TAccInfo info;
MakeAccInfo(info);
iQuerier.GetAccInfoCompletedL(aErr, &info, &cod);
}
else
{
iQuerier.GetAccInfoCompletedL(aErr, NULL, &cod);
}
}
void CBasrvSdpQuery::DoNextServiceRecordRequestL()
{
if (iCursor >= iServiceAttrs.Count())
{
TInt err = iRegServ.Connect();
if (!err)
err = iReg.Open(iRegServ);
if (err)
{
QueryCompleteL(KErrNone);
}
else
{
iActive = CBasrvActive::NewL(*this, CActive::EPriorityStandard, ECreateView);
TBTRegistrySearch pattern;
pattern.FindAddress(iAddr);
iReg.CreateView(pattern, iActive->iStatus);
iActive->GoActive();
}
}
else
{
iSdpSearchPattern->Reset();
iSdpSearchPattern->AddL(iServiceAttrs[iCursor].iUUID);
iSdpAgent->SetRecordFilterL(*iSdpSearchPattern);
iSdpAgent->NextRecordRequestL();
}
}
void CBasrvSdpQuery::NextRecordRequestCompleteL(TInt aError,
TSdpServRecordHandle aHandle,
TInt aTotalRecordsCount)
{
TRACE_INFO((_L("NextRecord err <%d>, Handle <0x%x>, Records Count <%d>"),
aError, aHandle, aTotalRecordsCount));
if(aError == KErrEof || (!aError && aTotalRecordsCount == 0))
{
iServiceAttrs.Remove(iCursor);
DoNextServiceRecordRequestL();
}
else if(aError)
{
if (!iTolerateSdpError)
{
QueryCompleteL(aError);
}
else
{
for (TInt i = iServiceAttrs.Count() - 1; i >= iCursor; i--)
{
iServiceAttrs.Remove(i);
}
DoNextServiceRecordRequestL();
}
}
else if(aTotalRecordsCount)
{
iSdpAgent->AttributeRequestL(aHandle, iServiceAttrs[iCursor].iAttrID);
}
}
void CBasrvSdpQuery::AttributeRequestResultL(
TSdpServRecordHandle aHandle,
TSdpAttributeID aAttrID,
CSdpAttrValue* aAttr)
{
TRACE_INFO((_L("AttributeRequestResult, attr type %d"), aAttr->Type()));
// Adds the attribute values synchronously to the result array.
TRAP_IGNORE( aAttr->AcceptVisitorL( *this ) );
TRACE_INFO((_L("handle <0x%x>, attr id <0x%04x>, value <%d>"), aHandle, aAttrID, iServiceAttrs[iCursor].iAttrValue));
(void) aHandle;
(void) aAttrID;
delete aAttr;
}
void CBasrvSdpQuery::AttributeRequestCompleteL(TSdpServRecordHandle aHandle, TInt aError)
{
TRACE_INFO((_L("AttributeRequestCompleteL err %d"), aError)); (void) aError;
iSdpAgent->Cancel();
TInt currentCursor = iCursor;
iCursor++;
if(iServiceAttrs[currentCursor].iUUID == KAVRemoteControlTargetUUID &&
iServiceAttrs[currentCursor].iAttrID == KSdpAttrIdSupportedFeatures)
{
// We have some other information (avrcp version) in the same record
// so lets get the information without quering the same record again.
iSdpAgent->AttributeRequestL(aHandle, iServiceAttrs[iCursor].iAttrID);
}
else
{
DoNextServiceRecordRequestL();
}
}
void CBasrvSdpQuery::VisitAttributeValueL( CSdpAttrValue &aValue, TSdpElementType aType )
{
switch ( aType )
{
case ETypeUint:
{
TRACE_INFO((_L("attr value %d"), aValue.Uint()));
iServiceAttrs[iCursor].iAttrValue = aValue.Uint();
break;
}
case ETypeInt:
{
TRACE_INFO((_L("attr value %d"), aValue.Int()));
iServiceAttrs[iCursor].iAttrValue = aValue.Int();
break;
}
case ETypeBoolean:
{
TRACE_INFO((_L("attr value %d"), aValue.Bool()));
iServiceAttrs[iCursor].iAttrValue = aValue.Bool();
break;
}
}
}
void CBasrvSdpQuery::StartListL( CSdpAttrValueList &aList )
{
(void) aList;
}
void CBasrvSdpQuery::EndListL()
{
}
void CBasrvSdpQuery::MakeAccInfo(TAccInfo& aInfo)
{
aInfo.SetBDAddress(iAddr);
if (iServiceAttrs.Count())
{
aInfo.SetCapturingSupport(EFalse); // default indication as of CR 403-6409 CMLA change
TUUID serviceUuid = TUUID((0x00005555),(0x00001000),(0x80000002),(0xEE000001)); // model number
TUint8 suppProfiles = 0x00;
for ( TUint ii = 0 ; ii < iServiceAttrs.Count() ; ++ii )
{
if ( iServiceAttrs[ii].iUUID == KAudioSinkUUID )
{
TRACE_INFO((_L("Accessory is A2DP Sink.")))
suppProfiles |= EStereo;
aInfo.SetSupportedFeature((TUint16)iServiceAttrs[ii].iAttrValue, TAccInfo::EStereoIndex);
}
else if ( iServiceAttrs[ii].iUUID == 0x111e ) // HFP
{
suppProfiles |= EHFP;
TRACE_INFO((_L("Accessory supports Handsfree profile.")))
aInfo.SetSupportedFeature((TUint16)iServiceAttrs[ii].iAttrValue, TAccInfo::EHFPIndex);
}
else if ( iServiceAttrs[ii].iUUID == 0x1108 ) // HSP
{
suppProfiles |= EHSP;
TRACE_INFO((_L("Accessory supports Headset profile.")))
if (iServiceAttrs[ii].iAttrValue)
{
TRACE_INFO((_L("HSP Remote volume control is supported.")))
// Server is going to feed 0x10 to supported features for headset profile.
// The SDP of HSP doesn't really use supported feature attribute.
// We save RVC support in supported feature field for internal future use only.
// This approach is consistent with Accessory framework API.
aInfo.SetSupportedFeature(KHspRemoteVolumeControlSupport, TAccInfo::EHSPIndex);
}
}
else if ( iServiceAttrs[ii].iUUID == KAVRemoteControlTargetUUID )
{
if(iServiceAttrs[ii].iAttrID == KSdpAttrIdSupportedFeatures)
{
suppProfiles |= ERemConTG; // 0x08 remote control profile
TRACE_INFO((_L("Accessory is AVRCP TG, features 0x%02X"), iServiceAttrs[ii].iAttrValue))
aInfo.SetSupportedFeature(iServiceAttrs[ii].iAttrValue, TAccInfo::ERemConTGIndex); // remote control = Position 3
}
else // KSdpAttrIdBluetoothProfileDescriptorList, Avrcp version
{
if(iServiceAttrs[ii].iAttrValue == 0x0104)
{
aInfo.iAvrcpVersion = TAccInfo::EAvrcpVersion14;
}
else
{
aInfo.iAvrcpVersion = TAccInfo::EAvrcpVersionNo14;
}
}
}
else if (iServiceAttrs[ii].iUUID == serviceUuid)
{
aInfo.SetModel(iServiceAttrs[ii].iAttrValue);
TRACE_INFO((_L("Accessory is proprietary. Model: %d"), iServiceAttrs[ii].iAttrValue ))
}
}
aInfo.SetSuppportedProfiles(suppProfiles);
}
}