--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpbrowsingcommandhandler.cpp Mon Jan 18 20:28:57 2010 +0200
@@ -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 <e32base.h>
+#include <remcon/remconbearerbulkobserver.h>
+#include <remconaddress.h>
+
+#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<TUint> 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<CBrowseCommand>& aCommandQueue, TUint aId, RBuf8& aData)
+ {
+ LOG_FUNC
+
+ TInt err = KErrNotFound;
+
+ TDblQueIter<CBrowseCommand> 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<CBrowseCommand> 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;
+ }