diff -r 22de2e391156 -r 20ac952a623c remotecontrol/avrcp/remconbeareravrcp/src/avrcpbrowsingcommandhandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotecontrol/avrcp/remconbeareravrcp/src/avrcpbrowsingcommandhandler.cpp Wed Oct 13 16:20:29 2010 +0300 @@ -0,0 +1,489 @@ +// 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: +// avrcpincomingcommandhandler.cpp +// + + + +/** + @file + @internalComponent + @released +*/ +#include +#include +#include + +#include "browsecommand.h" +#include "avrcpbrowsingcommandhandler.h" +#include "avrcpinternalinterface.h" +#include "avrcplog.h" +#include "avrcprouter.h" +#include "avrcputils.h" +#include "browsingframe.h" +#include "bulkbearer.h" +#include "mediabrowse.h" +#include "remconcommandinterface.h" + +//------------------------------------------------------------------------------------ +// Construction/Destruction +//------------------------------------------------------------------------------------ + +/** Factory function. + +@param aCommandInterface The interface for providing commands that have been handled. +@param aRouter A CRcpRouter to use for communication with remote devices. +@param aPlayerInfoManager The central manager for player information. +@param aAddr The Bluetooth device address for the remote device handled by this handler. +@return A fully constructed CRcpBrowsingCommandHandler. +@leave System wide error codes. +*/ +CRcpBrowsingCommandHandler* CRcpBrowsingCommandHandler::NewL(MRemConBulkCommandInterface& aCommandInterface, + CBulkRouter& aRouter, + CAvrcpPlayerInfoManager& aPlayerInfoManager, + const TBTDevAddr& aAddr) + { + LOG_STATIC_FUNC + CRcpBrowsingCommandHandler* handler = new(ELeave) CRcpBrowsingCommandHandler(aCommandInterface, aRouter, aPlayerInfoManager, aAddr); + return handler; + } + +/** +@param aCommandInterface The interface for providing commands that have been handled. +@param aRouter A CRcpRouter to use for communication with remote devices. +@param aPlayerInfoManager The central manager for player information. +@param aAddr The Bluetooth device address for the remote device handled by this handler. +@return A partially constructed CRcpBrowsingCommandHandler. +*/ +CRcpBrowsingCommandHandler::CRcpBrowsingCommandHandler(MRemConBulkCommandInterface& aCommandInterface, + CBulkRouter& aRouter, + CAvrcpPlayerInfoManager& aPlayerInfoManager, + const TBTDevAddr& aAddr) + : iCommandQueue(_FOFF(CBrowseCommand, iHandlingLink)) + , iInternalCommandQueue(_FOFF(CBrowseCommand, iHandlingLink)) + , iCommandInterface(aCommandInterface) + , iRouter(aRouter) + , iMtu(335) // initialise to min for browse channel + , iPlayerInfoManager(aPlayerInfoManager) + , iAddr(aAddr) + { + LOG_FUNC + } + +CRcpBrowsingCommandHandler::~CRcpBrowsingCommandHandler() + { + LOG_FUNC + + while (!iCommandQueue.IsEmpty()) + { + CBrowseCommand *command = iCommandQueue.First(); + HandledCommand(*command); + } + + TRemConAddress remConAddr; + AvrcpUtils::BTToRemConAddr(iAddr, remConAddr); + iCommandInterface.MrcbciRemoveAddressing(remConAddr); + } + +//--------------------------------------------------------------------- +// Called from the bearer +//--------------------------------------------------------------------- + +/** Tell the handler to gracefully shutdown. + +*/ +void CRcpBrowsingCommandHandler::Disconnect() + { + LOG_FUNC + + while (!iCommandQueue.IsEmpty()) + { + CBrowseCommand* command = iCommandQueue.First(); + iRouter.RemoveFromSendQueue(*command); + + HandledCommand(*command); + } + } + +//------------------------------------------------------------------------------------ +// Called by router +//------------------------------------------------------------------------------------ + +/** Receive an incoming AVRCP browse command. + +@param aMessageInformation The command data from the AVCTP message. +@param aTransactionLabel AVCTP transaction label for this command. +@param aAddr The bluetooth device from which this command originated. +@leave System Wide Error code +*/ +void CRcpBrowsingCommandHandler::ReceiveCommandL(const TDesC8& aMessageInformation, + SymbianAvctp::TTransactionLabel aTransactionLabel, + const TBTDevAddr& aAddr) + { + LOG_FUNC + + // If there's nothing beyond a header this is bobs. Dump it now. + AvrcpBrowsing::BrowsingFrame::VerifyFrameL(aMessageInformation); + + TUint id = iCommandInterface.MrcciNewTransactionId(); + CBrowseCommand* command = CBrowseCommand::NewL(aMessageInformation, id, aTransactionLabel, aAddr, &iPlayerInfoManager); + CleanupStack::PushL(command); + + TInt result = command->ProcessIncomingCommandL(iMtu); + CleanupStack::Pop(command); + + command->IncrementUsers(); + + switch(result) + { + case KErrAvrcpFurtherProcessingRequired: + { + // The only command that we need to check out before sending on is + // SetBrowsedPlayer. Although it's been parsed to verify that it's + // a syntactically valid command we need to ensure that the selected + // player is available before sending it on. + + __ASSERT_DEBUG(command->RemConInterfaceUid() == TUid::Uid(KRemConMediaBrowseApiUid) && command->RemConOperationId() == ESetBrowsedPlayerOperationId, AVRCP_PANIC(EFurtherProcessingRequiredForNonSetBrowsedPlayer)); + TBool valid = HandleSetBrowsedPlayer(*command); + + if(!valid) + { + Respond(*command, result); + command->DecrementUsers(); + break; + } + else + { + result = KErrNone; + } + // valid case fallsthrough to be handled as normal + } + case KErrAvrcpHandledInternallyInformRemCon: // this case falls through + case KErrNone: + { + iCommandQueue.AddLast(*command); + iCommandInterface.MrcciNewCommand(*command); + + if (result == KErrNone) + { + break; + } + // KErrAvrcpHandledInternallyInformRemCon fallsthrough here + } + case KErrAvrcpHandledInternallyRespondNow: + { + // If the command has already set payload, just sent the command + iRouter.AddToSendQueue(*command); + command->DecrementUsers(); + break; + } + case KErrAvrcpInternalCommand: + { + iInternalCommandQueue.AddLast(*command); + HandleInternalCommand(*command); + break; + } + default: + { + Respond(*command, result); + command->DecrementUsers(); + break; + } + }; + } + +/** Called from the router to indicate send completion. + +@param aCommand The command that has been sent. +@param aSendResult KErrNone if the command was sent successfully. System wide + error code otherwise. +*/ +void CRcpBrowsingCommandHandler::MessageSent(CAvrcpCommand& /*aCommand*/, TInt /*aSendResult*/) + { + LOG_FUNC + // We try and send the response, but if we fail there's not a lot we can do about + // it. Just let the remote assume the response. + } + +void CRcpBrowsingCommandHandler::MaxPacketSize(TInt aMtu) + { + iMtu = aMtu-AvrcpBrowsing::KHeaderLength; + } + +//------------------------------------------------------------------------------------ +// Called by bearer +//------------------------------------------------------------------------------------ + +/** Send a response. + +@param aInterfaceUid The RemCon interface this response is from. +@param aId The RemCon transaction label of the command to respond to. +@param aData The command response data. +@return KErrNotFound if the command was not found on the queue. + System wide error codes. +*/ +TInt CRcpBrowsingCommandHandler::SendRemConResponse(TUid /*aInterfaceUid*/, TUint aId, RBuf8& aData) + { + LOG_FUNC + + return SendResponse(iCommandQueue, aId, aData); + } + +//------------------------------------------------------------------------------------ +// Internal command handling functions +//------------------------------------------------------------------------------------ + + +/** Sends a response to the remote device. + +@param aCommand The command to respond to. +@param aErr The result of handling the command. +*/ +void CRcpBrowsingCommandHandler::Respond(CBrowseCommand& aCommand, TInt aErr) + { + LOG_FUNC + aCommand.SetResult(aErr); + iRouter.AddToSendQueue(aCommand); + } + +/** To be called on completion of command handling. + +This aggregates the handler's tidying up of a finished +command. + +@param aCommand The command to tidy up. +*/ +void CRcpBrowsingCommandHandler::HandledCommand(CBrowseCommand& aCommand) + { + LOG_FUNC + + aCommand.iHandlingLink.Deque(); + aCommand.DecrementUsers(); + } + +void CRcpBrowsingCommandHandler::HandleInternalCommand(CBrowseCommand& aCommand) + { + LOG_FUNC + + TUid interfaceUid; + TUint id; + TUint operationId; + RBuf8 commandData; + TBTDevAddr addr; + + aCommand.GetCommandInfo(interfaceUid, id, operationId, commandData, addr); + + __ASSERT_DEBUG(interfaceUid == TUid::Uid(KUidAvrcpInternalInterface), AvrcpUtils::Panic(EAvrcpInternalHandlingRequestedOnWrongInterface)); + + TInt err = KErrNone; + switch(operationId) + { + case EAvrcpInternalGetFolderItems: + { + err = HandleGetFolderItems(id, commandData); + break; + } + }; + + if(err) + { + HandledCommand(aCommand); + } + + commandData.Close(); + } + +TInt CRcpBrowsingCommandHandler::HandleGetFolderItems(TUint aId, RBuf8& aCommandData) + { + LOG_FUNC + + RBuf8 responseBuf; + TRAPD(err, DoHandleGetFolderItemsL(aCommandData, responseBuf)); + + if(!err) + { + err = SendInternalResponse(aId, responseBuf); + } + + return err; + } + +void CRcpBrowsingCommandHandler::DoHandleGetFolderItemsL(RBuf8& aCommandData, RBuf8& aResponseData) + { + LOG_FUNC + + __ASSERT_DEBUG( &iPlayerInfoManager != NULL, AvrcpUtils::Panic(EAvrcpNotFullyConstructed)); + RAvrcpGetFolderItemsRequest request; + CleanupClosePushL(request); + request.ReadL(aCommandData); + + // Use 4 bytes even though player ids are 2 bytes becuase of + // restrictions on RArray preventing use of non-word aligned types + RArray players; + TInt err = iPlayerInfoManager.PlayerListing(request.iStartItem, request.iEndItem, players); + CleanupStack::PopAndDestroy(&request); + + RRemConMediaErrorResponse errResponse; + if(err != KErrNone) + { + CleanupClosePushL(players); + errResponse.iPduId = AvrcpBrowsing::EGetFolderItems; + errResponse.iStatus = (err == KErrArgument) ? AvrcpStatus::ERangeOutOfBounds : AvrcpStatus::EInternalError; + aResponseData.CreateL(KBrowseResponseBaseLength); + CleanupClosePushL(aResponseData); + errResponse.WriteL(aResponseData); + CleanupStack::Pop(&aResponseData); + CleanupStack::PopAndDestroy(&players); + return; + } + + RAvrcpGetFolderItemsResponse response; + CleanupClosePushL(response); + CleanupClosePushL(players); + for(TInt i = 0; i < players.Count(); i++) + { + RMediaPlayerItem item; + CleanupClosePushL(item); + iPlayerInfoManager.MediaPlayerItemL(players[i], item); + response.iItems.AppendL(item); + CleanupStack::Pop(&item); + } + + response.iPduId = AvrcpBrowsing::EGetFolderItems; + response.iStatus = AvrcpStatus::ESuccess; + response.iUidCounter = KMediaPlayerListUidCounter; + response.iNumberItems = players.Count(); + CleanupStack::PopAndDestroy(&players); + + //check this fits within MTU, Leave if the response size is bigger than max size + CleanupClosePushL(aResponseData); + if(response.Size() > iMtu) + { + + errResponse.iPduId = AvrcpBrowsing::EGetFolderItems; + errResponse.iStatus = AvrcpStatus::EInternalError; + aResponseData.CreateL(KBrowseResponseBaseLength); + errResponse.WriteL(aResponseData); + } + else + { + aResponseData.CreateL(response.Size()); + response.WriteL(aResponseData); + } + CleanupStack::Pop(&aResponseData); + CleanupStack::PopAndDestroy(&response); + } + +TInt CRcpBrowsingCommandHandler::SendInternalResponse(TUint aId, RBuf8& aData) + { + LOG_FUNC + + return SendResponse(iInternalCommandQueue, aId, aData); + } + +TInt CRcpBrowsingCommandHandler::SendResponse(TDblQue& aCommandQueue, TUint aId, RBuf8& aData) + { + LOG_FUNC + + TInt err = KErrNotFound; + + TDblQueIter iter(aCommandQueue); + CBrowseCommand* command = NULL; + + while (iter) + { + command = iter++; + if(command->RemConCommandId() == aId) + { + err = KErrNone; + command->ProcessOutgoingResponse(aData); + + Respond(*command, err); + aData.Close(); + HandledCommand(*command); + + break; + } + } + + return err; + } + +void CRcpBrowsingCommandHandler::SendReject(TUid /*aInterfaceUid*/, TUint aTransactionId) + { + LOG_FUNC; + + TDblQueIter iter(iCommandQueue); + CBrowseCommand* command = NULL; + + while (iter) + { + command = iter++; + if(command->RemConCommandId() == aTransactionId) + { + Respond(*command, KErrAvrcpAirInternalError); + HandledCommand(*command); + } + } + } + +const TBTDevAddr& CRcpBrowsingCommandHandler::BtAddr() const + { + return iAddr; + } + +TBool CRcpBrowsingCommandHandler::HandleSetBrowsedPlayer(CBrowseCommand& aCommand) + { + TInt err = KErrNone; + RRemConSetBrowsedPlayerRequest request; + + TRAP(err, request.ReadL(aCommand.CommandData())); + __ASSERT_DEBUG(err == KErrNone, AvrcpUtils::Panic(ESetBrowsePlayerRequestCorruptedLocally)); + + // Check if selected player exists + TUint16 playerId = request.iPlayerId; + TRemConClientId clientId; + TRAP(err, clientId = iPlayerInfoManager.ClientL(playerId)); + + if(err == KErrNone) + { + // Selected player exists, check with RemCon if we can use it + TRemConAddress remConAddr; + AvrcpUtils::BTToRemConAddr(iAddr, remConAddr); + + TInt err = iCommandInterface.MrcbciSetAddressedClient(remConAddr, clientId); + } + + if(err != KErrNone) + { + // Either the player was incorrect or is already in use, form a RemCon + // format response, then ask the command to process it for sending out + // on the air. + RBuf8 buf; + TInt bufErr = buf.Create(KMediaBrowseOutBufMaxLength); + + if(bufErr == KErrNone) + { + RRemConMediaErrorResponse response; + response.iPduId = 0x70; + response.iStatus = RAvrcpIPC::SymbianErrToStatus(KErrAvrcpAirInvalidPlayerId); + TRAP(bufErr, response.WriteL(buf)); + aCommand.ProcessOutgoingResponse(buf); + buf.Close(); + } + } + // else we will continue processing this command as normal + + return err == KErrNone ? ETrue : EFalse; + }