// 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;
}