// Copyright (c) 2004-2009 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:
// Implementation of Generic Audio-Video Distribution Profile - GAVDP
// The CGavdp class acts as a "Body" implementation to the RGavdp "Handle"
//
//
/**
@publishedPartner
*/
#include "gavdpInternal.h"
CGavdp* CGavdp::NewL(MGavdpUser& aServiceUser, RSocketServ& aSocketServer, RGavdp& aHandle)
{
#ifndef BLUETOOTH_NO_AV
CGavdp* gavdp = new(ELeave) CGavdp(aServiceUser, aSocketServer, aHandle);
CleanupStack::PushL(gavdp);
gavdp->ConstructL();
CleanupStack::Pop(gavdp);
return gavdp;
#else
User::Leave(KErrNotSupported);
return NULL; // not reached
#endif
}
CGavdp::~CGavdp()
{
DeAllocSocketsAndHelpers();
iSocketServer.Close();
}
void CGavdp::DeAllocSocketsAndHelpers()
{
delete iRequesterHelper;
iRequesterHelper = NULL;
delete iIndicatorHelper;
iIndicatorHelper = NULL;
AvdtpRequester().Close();
AvdtpIndicator().Close();
}
void CGavdp::ConstructL()
{
User::LeaveIfError(iSocketServer.Connect());
ConstructSocketsAndHelpersL();
}
void CGavdp::Cancel()
{
if (iRequesterHelper)
{
delete iRequesterHelper;
iRequesterHelper = NULL;
iState = EIdle;
// it is the client's job to recover state, so they should send an Abort.
}
// leave indications running.
}
void CGavdp::ConstructSocketsAndHelpersL()
{
User::LeaveIfError(AvdtpRequester().Open(iSocketServer, KAVDTPProtocolName));
// mark as for signalling purposes
TAvdtpSockAddr addr;
addr.SetSession(ESignalling);
User::LeaveIfError(AvdtpRequester().Bind(addr));
// start reading for AVDTP indications
User::LeaveIfError(AvdtpIndicator().Open(iSocketServer, KAVDTPProtocolName));
User::LeaveIfError(AvdtpIndicator().SetOpt(ESetAsSecondarySAP,
KSolBtAVDTPInternal,
KNullDesC8));
User::LeaveIfError(AvdtpRequester().SetOpt(EBindToSecondarySAP,
KSolBtAVDTPInternal,
KNullDesC8));
iIndicatorHelper = CGavdpIndicator::NewL(*this, iServiceUser);
iIndicatorHelper->Begin();
}
CGavdp::CGavdp(MGavdpUser& aServiceUser, RSocketServ& aClientSocketServer, RGavdp& aHandle)
: iClientSocketServer(aClientSocketServer), iServiceUser(aServiceUser), iHandle(aHandle)
{
}
void CGavdp::Connect(const TBTDevAddr& aRemoteAddr)
{
__ASSERT_ALWAYS((iState == EIdle || iState == EListening), Panic(EGavdpBadState));
__ASSERT_ALWAYS(iNumSEPsRegistered, Panic(EGavdpSEPMustBeRegisteredBeforeConnect));
__ASSERT_ALWAYS(aRemoteAddr!=TBTDevAddr(0), Panic(EGavdpBadRemoteAddress));
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
AvdtpRequester().SetOpt(EStopAwaitPassiveSignallingChannel, KSolBtAVDTPSignalling, NULL);
TRAPD(err, iRequesterHelper = CGavdpConnector::NewL(*this, iServiceUser, aRemoteAddr));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iState = EConnecting;
iRequesterHelper->Begin();
}
}
void CGavdp::DiscoverRemoteSEPs()
{
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
TRAPD(err, iRequesterHelper = CGavdpDiscover::NewL(*this, iServiceUser));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
void CGavdp::GetRemoteSEPCapabilities(TSEID aSEID, const TAvdtpServiceCategories& aInterestingCategories)
{
__ASSERT_ALWAYS(!iRequesterHelper /*&& iServiceUser*/, Panic(EGavdpBadState));
CheckSEID(aSEID);
TRAPD(err, iRequesterHelper = CGavdpGetCaps::NewL(*this,
iServiceUser,
aSEID,
aInterestingCategories));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
TInt CGavdp::BeginConfiguringRemoteSEP(TSEID aRemoteSEID, TSEID aLocalSEID)
{
__ASSERT_ALWAYS(iState!=EConfiguringRemoteSEP, Panic(EGavdpBadState));
CheckSEID(aRemoteSEID);
CheckSEID(aLocalSEID);
TInternalSelectRemoteSEP select;
select.iLocalSEID = aLocalSEID;
select.iRemoteSEID = aRemoteSEID;
TPckg<TInternalSelectRemoteSEP> seidpckg(select);
TInt err = AvdtpRequester().SetOpt(EStartConfiguringRemote, KSolBtAVDTPSignalling, seidpckg);
if (err==KErrNone)
{
iState = EConfiguringRemoteSEP;
}
return err;
}
TInt CGavdp::BeginConfiguringLocalSEP(TSEID aSEID)
{
__ASSERT_ALWAYS(iState!=EConfiguringLocalSEP, Panic(EGavdpBadState));
TPckg<TSEID> seidpckg(aSEID);
TInt err = AvdtpRequester().SetOpt(EStartConfiguringLocal, KSolBtAVDTPSignalling, seidpckg);
if (err==KErrNone)
{
iState = EConfiguringLocalSEP;
}
return err;
}
TInt CGavdp::AddSEPCapability(const TAvdtpServiceCapability& aCapability)
{
__ASSERT_ALWAYS(iState==EConfiguringLocalSEP || iState==EConfiguringRemoteSEP,
Panic(EGavdpBadState));
// convert into protocol form - this is a handy way to get a descriptor for IPC purposes
// if AVDTP requires the reverse operation it is of course allowed to get back to the T class form
RBuf8 buf;
TInt err = aCapability.AsProtocol(buf);
if (err==KErrNone)
{
err = AvdtpRequester().SetOpt(EAddCapabilitySelection,KSolBtAVDTPSignalling, buf);
}
buf.Close();
return err;
}
void CGavdp::CommitSEPConfiguration()
{
__ASSERT_ALWAYS(iState == EConfiguringLocalSEP || iState == EConfiguringRemoteSEP, Panic(EGavdpBadState));
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
TRAPD(err, iRequesterHelper = CGavdpSelectSEP::NewL(*this, iServiceUser));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
void CGavdp::StartStream(TSEID aSEID)
{
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
TRAPD(err, iRequesterHelper = CGavdpStart::NewL(*this, iServiceUser, aSEID));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
void CGavdp::SuspendStream(TSEID aSEID)
{
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
TRAPD(err, iRequesterHelper = CGavdpSuspend::NewL(*this, iServiceUser, aSEID));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
void CGavdp::AbortStream(TSEID aSEID)
{
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
CheckSEID(aSEID);
TRAPD(err, iRequesterHelper = CGavdpAbort::NewL(*this, iServiceUser, aSEID));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
void CGavdp::SendSecurityControl(TSEID aSEID, const TDesC8& aSecurityData)
{
__ASSERT_ALWAYS(!iRequesterHelper, Panic(EGavdpBadState));
CheckSEID(aSEID);
TRAPD(err, iRequesterHelper = CGavdpSecurityControl::NewL(*this, iServiceUser, aSEID, aSecurityData));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
TInt CGavdp::RegisterSEP(TAvdtpSEPInfo& aInfo)
{
// note this is a GetOpt - but it also sets:
// in goes the info, without SEID
// out comes the same structure with SEID filled in
TPckg<TAvdtpSEPInfo> sepInfo(aInfo);
TInt err = AvdtpRequester().GetOpt(ERegisterSEP, KSolBtAVDTPSignalling, sepInfo);
if (err==KErrNone)
{
iNumSEPsRegistered++;
}
return err;
}
/**
Called when gavdp client has registed all seps and is ready
May be short lived as they may try to connect out quickly
*/
TInt CGavdp::Listen()
{
__ASSERT_ALWAYS(iNumSEPsRegistered, Panic(EGavdpSEPMustBeRegisteredBeforeListen));
TInt ret = KErrNone;
if (iState!=EConnected)
{
// start awaiting for signalling channel as as SEP has been successfully registered
ret = AvdtpRequester().SetOpt(EAwaitPassiveSignallingChannel, KSolBtAVDTPSignalling, NULL);
if (KErrNone==ret)
{
iState = EListening;
}
}
else
{
ret = KErrInUse;
}
return ret;
}
// private
void CGavdp::SendResponse( TInternalAvdtpAirIndication aIndication,
TInt aResult,
const TAny* aResponseData/*=NULL*/,
TInt aResponseDataLen/*=0*/)
{
TInt err = KErrNone;
switch (aIndication.iIndication)
{
case EAvdtpSetConfiguration:
case EAvdtpReconfigure:
{
TAvdtpInternalConfigurationResponse rsp;
rsp.iSEID = aIndication.iSEID;
rsp.iTransactionLabel = aIndication.iTransactionLabel;
rsp.iResult = aResult;
rsp.iIsReconfigure = aIndication.iIndication==EAvdtpReconfigure;
if (aResult!=KErrNone)
{
__ASSERT_DEBUG(aResponseData, Panic(EGavdpResponseDataExpected));
rsp.iCategory = *static_cast<const TAvdtpServiceCategory*>(aResponseData);
}
TPckg<TAvdtpInternalConfigurationResponse> pckg(rsp);
err = AvdtpRequester().SetOpt(ESendConfigurationResponse, KSolBtAVDTPSignalling, pckg);
break;
}
case EAvdtpSecurityControl:
{
// optional to send data in response
TAvdtpInternalSecurityControlResponse rsp;
rsp.iSEID = aIndication.iSEID;
rsp.iTransactionLabel = aIndication.iTransactionLabel;
rsp.iResult = aResult;
if (aResult==KErrNone)
{
rsp.iSecurityControlInfo.Copy(static_cast<const TUint8*>(aResponseData), aResponseDataLen);
}
TPckg<TAvdtpInternalSecurityControlResponse> pckg(rsp);
err = AvdtpRequester().SetOpt(ESendSecurityControlResponse, KSolBtAVDTPSignalling, pckg);
break;
}
case EAvdtpStart:
{
TAvdtpInternalStartResponse rsp;
rsp.iSEID = aIndication.iSEID;
rsp.iTransactionLabel = aIndication.iTransactionLabel;
rsp.iResult = aResult;
TPckg<TAvdtpInternalStartResponse> pckg(rsp);
err = AvdtpRequester().SetOpt(ESendStartResponse, KSolBtAVDTPSignalling, pckg);
break;
}
case EAvdtpSuspend:
{
TAvdtpInternalSuspendResponse rsp;
rsp.iSEID = aIndication.iSEID;
rsp.iTransactionLabel = aIndication.iTransactionLabel;
rsp.iResult = aResult;
TPckg<TAvdtpInternalSuspendResponse> pckg(rsp);
err = AvdtpRequester().SetOpt(ESendSuspendResponse, KSolBtAVDTPSignalling, pckg);
break;
}
}
if (err!=KErrNone)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
}
void CGavdp::ServiceComplete(TInt aResult)
{
__ASSERT_DEBUG(iRequesterHelper, Fault(EGavdpHelperCompletingBadly));
iRequesterHelper = NULL;
if (aResult==KErrNone)
{
switch (iState)
{
case EConfiguringRemoteSEP:
case EListening:
case EConnecting:
iState = EConnected;
break;
case EConfiguringLocalSEP:
{
iState = EIdle;
break;
}
}
}
else
{
iState = EIdle; // or perhaps errored?
}
}
void CGavdp::Error(TInt aError)
{
// helper tells user - here we do whatever we need to tidy ourselves
delete iRequesterHelper;
iRequesterHelper = NULL;
iState = EIdle;
// and tell the user
iServiceUser.GAVDP_Error(aError, KNullDesC8);
}
void CGavdp::FatalError()
{
//Unbind from the body before informing the user
iHandle.UnbindBody();
//Now tell the user we are DEAD
iServiceUser.GAVDP_Error(KErrDied, KNullDesC8);
//Cleanup: we've unbound from the body, We are no longer owned. Delete ourselves
delete this;
}
/*
Forms the binding between passively created signalling and sockets
*/
void CGavdp::BindSignallingL(const TBTDevAddr& aRemoteAddress)
{
// use the same helper as if we originated the signalling channel
if (iRequesterHelper)
{
// the client has just issued another command
// this is likely to be something that renders the connections invalid anyway
// eg Abort, Config
User::Leave(KErrInUse);
}
iRequesterHelper = CGavdpConnector::NewL(*this, iServiceUser, aRemoteAddress);
iRequesterHelper->Begin();
}
/*
Forms the binding between passively created transport sessions and sockets
*/
void CGavdp::BindBearersL(TSEID aSEID, TBool aRequireReporting, TBool aRequireRecovery)
{
// which bearers? spy on set config to see if rec&rep in use
// or from the indication?
//certainly media
CheckSEID(aSEID);
TAvdtpSockAddr addr;
AvdtpRequester().RemoteName(addr);
addr.SetSEID(aSEID);
if (iRequesterHelper)
{
// the client has just issued another command
// this is likely to be something that renders the connections invalid anyway
// eg Abort, Config
User::Leave(KErrInUse);
}
iRequesterHelper = CGavdpUPlaneConnector::NewL(*this,
iServiceUser,
addr,
aRequireReporting,
aRequireRecovery,
iClientSocketServer
);
iRequesterHelper->Begin();
}
TInt CGavdp::CreateBearerSockets(TSEID aSEID)
{
// form socket address and connect
// should be in state listening. if we are told to create bearers then of course stop listening
CheckSEID(aSEID);
TAvdtpSockAddr addr;
// get remote BDAddr for this session
AvdtpRequester().RemoteName(addr);
// now set SEID and Session type
addr.SetSEID(aSEID);
// ask avdtp if reporting and recovery are configured
TReportingAndRecovery rar;
rar.iSEID = aSEID;
TPckg<TReportingAndRecovery> rarBuf(rar);
TInt err = AvdtpRequester().GetOpt(EGetReportingAndRecoveryConfig, KSolBtAVDTPSignalling, rarBuf);
if(err == KErrNone)
{
// helper sets session types
TRAP(err, iRequesterHelper = CGavdpUPlaneConnector::NewL(*this,
iServiceUser,
addr,
rar.iReporting,
rar.iRecovery,
iClientSocketServer));
if (err)
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
else
{
iRequesterHelper->Begin();
}
}
else
{
iServiceUser.GAVDP_Error(err, KNullDesC8);
}
return err;
}
inline TBool CGavdp::CheckSEID(TSEID aSEID)
{
TBool ok = aSEID.IsValid();
__ASSERT_ALWAYS(ok, Panic(EGavdpUserSuppliedBadSEID));
return ok;
}
TInt CGavdp::Shutdown()
{
Cancel();
iState=EIdle;
return AvdtpRequester().SetOpt(EShutdown, KSolBtAVDTPSignalling, NULL);
}