bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcprouter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 20:28:57 +0200
changeset 0 f63038272f30
permissions -rw-r--r--
Revision: 201001 Kit: 201003

// 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:
//



/**
 @file
 @internalComponent
 @released
*/

#include <bluetoothav.h>

#include "avrcpcommand.h"
#include "avrcplog.h"
#include "avrcprouter.h"
#include "avrcputils.h"
#include "commandhandlerinterface.h"

using namespace SymbianAvctp;

/** Constructor.

@param aRemotes The list of remote devices known to AVRCP.
@param aBearer The bearer.
@return A partially constructed CRcpRouter.
*/
CRcpRouter::CRcpRouter(MAvrcpBearer& aBearer)
	: iChannel(NULL)
	, iBearer(aBearer)
	, iSendQueue(_FOFF(CAvrcpCommand, iSendLink))
	, iState(EAvrcpRouterCanSend)
	{
	LOG_FUNC
	}

/** Destructor.
*/	
CRcpRouter::~CRcpRouter()
	{
	LOG_FUNC
	// The lifetime of the Router is the same as that of the bearer plugin.
	// This means that there is no point worrying about the stuff on our
	// send queue as we won't be around to pass the results up to RemCon.
	while (!iSendQueue.IsEmpty())
		{
		CAvrcpCommand *command = iSendQueue.First();
		command->iSendLink.Deque();
		command->DecrementUsers();
		}
	}
	
//------------------------------------------------------------------
// Data functions called from command handlers
//------------------------------------------------------------------	

/** Adds command to send queue.

This message will be sent as soon as the iAvcp becomes
free for sending, unless the command is removed before
then.

@param aCommand The command to send.
@see CRcpRouter::RemoveFromSendQueue
*/
void CRcpRouter::AddToSendQueue(CAvrcpCommand& aCommand)
	{
	LOG_FUNC
	
	__ASSERT_ALWAYS(!aCommand.iSendLink.IsQueued(), AVRCP_PANIC(ECommandAlreadyQueuedForSending));

	iSendQueue.AddLast(aCommand);
	aCommand.IncrementUsers();

	if(iState == EAvrcpRouterCanSend)
		{
		Send();
		}	
	}

/** Remove command from send queue.

@param aCommand The command that is not to be sent.
*/	
void CRcpRouter::RemoveFromSendQueue(CAvrcpCommand& aCommand)
	{
	LOG_FUNC
	
	if(iSendQueue.IsFirst(&aCommand))
		{
		// If this is at the front of the queue it's currently being
		// sent.  Cancel that and start off the next thing.
		iChannel->MacCancelSend();
		aCommand.iSendLink.Deque();
		iState = EAvrcpRouterCanSend;
		if(!iSendQueue.IsEmpty())
			{
			Send();
			}
		}
	else
		{
		// Still waiting to be sent so we can just sneak it out of
		// the queue.
		aCommand.iSendLink.Deque();
		}
	}
	
//------------------------------------------------------------------
// Control functions called from bearer
//------------------------------------------------------------------

/** Try to bring up an explicit connection to a remote.

@param aAddr The address of the remote.
@return System wide error.  KErrNone if this request will be
		attempted, and generate a ConnectConfirm.
*/
TInt CRcpRouter::ConnectRequest(const TBTDevAddr& aAddr)
	{
	LOG_FUNC
	return iChannel->MacAttachRequest(aAddr);
	}

/** Try to bring down an explicit connection to a remote.

@param aAddr The address of the remote.
@return System wide error.  KErrNone if the disconnect will be
		attempted, and generate a DisconnectConfirm.
*/	
TInt CRcpRouter::DisconnectRequest(const TBTDevAddr& aAddr)
	{
	LOG_FUNC
	return iChannel->MacDetachRequest(aAddr);
	}
	
//------------------------------------------------------------------
// MAvctpEventNotify functions called from RAvctp
//------------------------------------------------------------------	

/** AVCTP Connection Indicate.

This is called when a remote device has connected 
to us. NB we don't return the configuration status as 
in Appendix A - AVCTP Upper Interface of [R2]. If aAccept 
is not changed the connection will be refused however 
this may not result in the actual disconnection of the 
device if another RAvctp client did accept the connection.

@param aBTDevice the address of the device connected to
@param aAccept this parameter is provided so that the client 
	of RAvctp can indicate whether they want to accept the 
	connection.  The meaning of a refusal is that you don't 
	care whether or not the connection is there or not.	
@see RAvctp			   
*/
void CRcpRouter::MaenAttachIndicate(const TBTDevAddr& aBTDevice, TInt aMtu, TBool& aAccept)
	{
	LOG_FUNC
	aAccept = ETrue;
	
	// if we have already received data from this device we will have 
	// informed RemCon about it already, so don't pass up the connection
	// now
	if(!iBearer.IncomingHandler(aBTDevice))
		{
		iBearer.ConnectIndicate(aBTDevice);
		}
	
	MIncomingCommandHandler* handler = iBearer.IncomingHandler(aBTDevice);
	if(handler)
		{
		handler->MaxPacketSize(aMtu);
		aAccept = ETrue;
		}
	else
		{
		aAccept = EFalse;
		}
	}

/** AVCTP Connection Confirm.

This is a response to RAvctp::ConnectRequest and passes on the 
result of the Connection attempt. NB we don't return the 
configuration status as in Appendix A - AVCTP Upper 
Interface of [R2]. If the aConnectResult is KErrNone 
then iAvctp is now connected.

@param aBTDevice the address of the device connected to
@param aConnectResult connection result - one of the 
					  system-wide error codes. 
@see RAvctp					  
*/	
void CRcpRouter::MaenAttachConfirm(const TBTDevAddr& aBTDevice, TInt aMtu, TInt aConnectResult)
	{
	LOG_FUNC
	iBearer.ConnectConfirm(aBTDevice, aConnectResult);

	MIncomingCommandHandler* handler = iBearer.IncomingHandler(aBTDevice);
	if(handler)
		{
		handler->MaxPacketSize(aMtu);
		}
	}

/** AVCTP Disconnection Indication.

Indicates that a remote device has disconnected from us.
It is only called if the device had been explicitly Connected to.
 
@param aBTDevice the address of the disconnecting device
@see RAvctp
*/	
void CRcpRouter::MaenDetachIndicate(const TBTDevAddr& aBTDevice)
	{
	LOG_FUNC
	iBearer.DisconnectIndicate(aBTDevice);
	}

/** AVCTP Disconnection Confirm.

@param aBTDevice the address of the disconnected device
@param aDisconnectResult will be one of the system-wide 
						 error codes. If KErrTimedOut is 
						 returned then the RAvctp will be 
						 disconnected.
@see RAvctp	
*/	
void CRcpRouter::MaenDetachConfirm(const TBTDevAddr& aBTDevice, TInt aDisconnectResult)
	{
	LOG_FUNC
	iBearer.DisconnectConfirm(aBTDevice, aDisconnectResult);
	}

/** AVCTP Message received indication.

This method is called when a message has been received from 
the given device on the RAvctp's PID. 

Note that because AVCTP is a connectionless protocol, it is 
perfectly possible to get a MaenMessageReceivedIndicate event 
from a device that you have not either explicitly connected to.
For instance even if you don't accept a MaenConnectIndicate 
you may still receive messages from that remote device.

@param aBTDevice address of the device sending us an AVCTP message
@param aTransactionLabel message transaction label
@param aType type of message 
@param aIpidBitSet this will be set to true only if a message has been received indicating 
				   that the profile corresponding to the originally sent message is not valid.
				   If RAvctp was used to send the message then this response will have come from
				   the remote device aBTDevice.
@param aMessageInformation contains only the AVCTP Command / Response Message Information and not the whole packet.
                           Ownership transferred to client.
@see RAvctp
*/	
void CRcpRouter::MaenMessageReceivedIndicate(const TBTDevAddr& aBTDevice,
		SymbianAvctp::TTransactionLabel aTransactionLabel,
		SymbianAvctp::TMessageType aType,
		TBool aIpidBitSet,
		const TDesC8& aMessageInformation)
	{
	LOG_FUNC
	AVRCPLOG(aMessageInformation)
		
	// Find the right handler
	if(aType == ECommand)
		{
		MIncomingCommandHandler* handler = iBearer.IncomingHandler(aBTDevice);
		
		if(!handler)
			{
			iBearer.ConnectIndicate(aBTDevice);
			
			handler = iBearer.IncomingHandler(aBTDevice);
			}
		
		if(handler)
			{
			// If this leaves the handler is rejecting handling this command, just
			// ignore it.
			TRAP_IGNORE(handler->ReceiveCommandL(aMessageInformation, aTransactionLabel, aBTDevice));
			}
		}
	else
		{
		MOutgoingCommandHandler* handler = iBearer.OutgoingHandler(aBTDevice);
		if(handler)
			{
			handler->ReceiveResponse(aMessageInformation, aTransactionLabel, aIpidBitSet);
			}
		}
	}

/** AVCTP Message send complete.

This method is called when a RAvctp has attempted to send 
the message defined by aTransactionLabel and aBTDevice. 
@param aTransactionLabel The transaction label of the message 
						 that has been sent
@param aBTDevice the device to which the send has completed
@param aSendResult KErrNone if the send was successful or one 
				   of the system-wide error codes
@see RAvctp
*/	
void CRcpRouter::MaenMessageSendComplete(const TBTDevAddr& aBTDevice, 
		       		SymbianAvctp::TTransactionLabel aTransactionLabel,   
					TInt aSendResult)
	{
	LOG_FUNC
	__ASSERT_ALWAYS(!iSendQueue.IsEmpty(), AvrcpUtils::Panic(EAvrcpNoOutstandingSend));
	
	CAvrcpCommand* command = iSendQueue.First();
	
	__ASSERT_ALWAYS(command->TransactionLabel() == aTransactionLabel, AvrcpUtils::Panic(EAvrcpUnknownAvctpTransId));
	
	MAvrcpCommandHandler* handler = NULL;
	if(command->MessageType() == ECommand)
		{
		handler = iBearer.OutgoingHandler(aBTDevice);
		}
	else 
		{
		handler = iBearer.IncomingHandler(aBTDevice);
		}
	
	if(handler)
		{
		handler->MessageSent(*command, aSendResult);
		}
		
	// Deque before calling Decrement because Decrement handling may involve
	// deleting command
	command->iSendLink.Deque();
	command->DecrementUsers();

	// Now able to do another send. Toggle our state and check if there
	// are any commands waiting.
	iState = EAvrcpRouterCanSend;
	if(!iSendQueue.IsEmpty())
		{
		Send();
		}
	}

/** AVCTP Close Complete.

This is the response to the CloseGracefully() that has been
called on a RAvctp object. It is the last event that will be 
called until the RAvctp object is Open()'d again.
@see RAvctp
*/
void CRcpRouter::MaenCloseComplete()
	{
	LOG_FUNC
	}

/** AVCTP error notification.

Note an errored device does not indicate that the device has 
been disconnected. If it has then a MaenDisconnectIndicate 
event will be used to indicate this.

@param aBTDevice the remote device associated with the error or TBTDevAddr(0) for a general error
@param aError system wide error
@see RAvctp
*/
void CRcpRouter::MaenErrorNotify(const TBTDevAddr& /*aBTDevice*/, TInt /*aError*/)
	{
	LOG_FUNC
	}

/**
Returns a null aObject if the extension is not implemented,
or a pointer to another interface if it is.

@param aInterface UID of the interface to return
@param aObject system wide error
@see RAvctp
*/	
void CRcpRouter::MaenExtensionInterfaceL(TUid /*aInterface*/, void*& aObject)
	{
	LOG_FUNC
	aObject = NULL;
	}

//------------------------------------------------------------------
// Utility functions
//------------------------------------------------------------------

/** Issue a send to AVCTP.

This sends the first message on the command queue.
*/
void CRcpRouter::Send()
	{
	LOG_FUNC
	__ASSERT_ALWAYS(!iSendQueue.IsEmpty(), AvrcpUtils::Panic(EAvrcpNoOutstandingSend));
	
	CAvrcpCommand* command = iSendQueue.First();

#ifdef _DEBUG	
	TInt err = 
#endif // _DEBUG
	iChannel->MacSendMessage(command->RemoteAddress(),
		command->TransactionLabel(),
		command->MessageType(),
		command->Data());
	
	__ASSERT_DEBUG(err == KErrNone, AvrcpUtils::Panic(EAvrcpSendingMessageFailed));
	
	iState = EAvrcpRouterSending;
	}

/** Factory funtion.

@param aAvctp An open RAvctp instance.
@param aBearer The bearer.
@return A fully constructed CRcpRouter.
@leave System wide error codes.
*/
CControlRouter* CControlRouter::NewL(RAvctp& aAvctp, MAvrcpBearer& aBearer)
	{
	LOG_STATIC_FUNC
	CControlRouter* router = new(ELeave) CControlRouter(aAvctp, aBearer);
	CleanupStack::PushL(router);
	router->ConstructL();
	CleanupStack::Pop(router);
	return router;
	}

CControlRouter::~CControlRouter()
	{
	LOG_FUNC
	iAvctp.Close(RAvctp::ENormal);
	}

CControlRouter::CControlRouter(RAvctp& aAvctp, MAvrcpBearer& aBearer)
	: CRcpRouter(aBearer)
	, iAvctp(aAvctp)
	{
	LOG_FUNC
	}

void CControlRouter::ConstructL()
	{
	LOG_FUNC
	LEAVEIFERRORL(iAvctp.Open(*this, KAvrcpPid, iChannel));
	}

/** Factory funtion.

@param aAvctp An open RAvctp instance.
@param aBearer The bearer.
@return A fully constructed CRcpRouter.
@leave System wide error codes.
*/
CBulkRouter* CBulkRouter::NewL(RAvctp& aAvctp, MAvrcpBearer& aBearer)
	{
	LOG_STATIC_FUNC
	CBulkRouter* router = new(ELeave) CBulkRouter(aAvctp, aBearer);
	CleanupStack::PushL(router);
	router->ConstructL();
	CleanupStack::Pop(router);
	return router;
	}

CBulkRouter::~CBulkRouter()
	{
	LOG_FUNC
	iAvctp.UninstallSecondaryChannel();
	}

CBulkRouter::CBulkRouter(RAvctp& aAvctp, MAvrcpBearer& aBearer)
	: CRcpRouter(aBearer)
	, iAvctp(aAvctp)
	{
	LOG_FUNC
	}

void CBulkRouter::ConstructL()
	{
	LOG_FUNC
	LEAVEIFERRORL(iAvctp.InstallSecondaryChannel(*this, iChannel));
	}