bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpincomingcommandhandler.cpp
changeset 0 f63038272f30
child 20 2f88a7d66f50
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpincomingcommandhandler.cpp	Mon Jan 18 20:28:57 2010 +0200
@@ -0,0 +1,872 @@
+// 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 <remcon/avrcpspec.h>
+#include <remcon/remconbearerobserver.h>
+#include <remconcoreapi.h>
+
+#include "controlcommand.h"
+#include "avrcpcommandframer.h"
+#include "avrcpincomingcommandhandler.h"
+#include "avrcpinternalinterface.h"
+#include "avrcplog.h"
+#include "avrcprouter.h"
+#include "avrcptimer.h"
+#include "avrcputils.h"
+#include "controlbearer.h"
+#include "passthroughhelper.h"
+#include "mediainformation.h"
+#include "nowplaying.h"
+#include "playerinformation.h"
+#include "remconbattery.h"
+#include "remcongroupnavigation.h"
+
+//------------------------------------------------------------------------------------
+// Construction/Destruction
+//------------------------------------------------------------------------------------
+
+/** Factory function.
+
+@param aBearer	The CRemConBearerAvrcp this is to handle commands for.
+@param aObserver The observer of the bearer. Used to acquire converters.
+@param aRouter	A CRcpRouter to use for communication with remote devices.
+@param aTimer	CDeltaTimer to use for queuing timed events.
+@return A fully constructed CRcpIncomingCommandHandler.
+@leave System wide error codes.
+*/
+CRcpIncomingCommandHandler* CRcpIncomingCommandHandler::NewL(CRemConBearerAvrcp& aBearer,
+	MRemConBearerObserver& aObserver,
+	CRcpRouter& aRouter,
+	CDeltaTimer& aTimer,
+	CAvrcpPlayerInfoManager& aPlayerInfoManager,
+	TBTDevAddr& aDevice) 
+	{
+	LOG_STATIC_FUNC
+	CRcpIncomingCommandHandler* handler = new(ELeave)CRcpIncomingCommandHandler(aBearer, aObserver, aRouter, aTimer, aPlayerInfoManager, aDevice);
+	CleanupStack::PushL(handler);
+	handler->ConstructL();
+	CleanupStack::Pop(handler);
+	return handler;
+	}
+
+/** Constructor.
+
+@param aBearer	The CRemConBearerAvrcp this is to handle commands for.
+@param aObserver The observer of the bearer. Used to aquire converters.
+@param aRouter	A CRcpRouter to use for communication with remote devices.
+@param aTimer	CDeltaTimer to use for queuing timed events.
+@return A partially constructed CRcpIncomingCommandHandler.
+@leave System wide error codes.
+*/	
+CRcpIncomingCommandHandler::CRcpIncomingCommandHandler(CRemConBearerAvrcp& aBearer, 
+	MRemConBearerObserver& aObserver,
+	CRcpRouter& aRouter,
+	CDeltaTimer& aTimer,
+	CAvrcpPlayerInfoManager& aPlayerInfoManager,
+	TBTDevAddr& aDevice) 
+	: iCommandQueue(_FOFF(CControlCommand, iHandlingLink))
+	, iInternalCommandQueue(_FOFF(CControlCommand, iHandlingLink))
+	, iFragmenter(NULL)
+	, iBearer(aBearer)
+	, iObserver(aObserver)
+	, iRouter(aRouter)
+	, iTimer(aTimer)
+	, iAddressedMode(EFalse)
+	, iPlayerInfoManager(aPlayerInfoManager)
+	, iDevice(aDevice)
+	{
+	LOG_FUNC
+	}
+
+void CRcpIncomingCommandHandler::ConstructL()
+	{
+	LOG_FUNC
+	
+	iFragmenter = CAVRCPFragmenter::NewL();
+	iPlayerInfoManager.AddObserverL(*this);
+
+	RArray<TUint> players;
+	iPlayerInfoManager.PlayerListing(players);
+	CleanupClosePushL(players);
+	
+	if(players.Count())
+		{
+		// Start out with the first player as default.  TSP will need to update
+		// us if it wants to set a different one.  Note that this wont be used 
+		// unless we enter addressed mode anyway.
+		iClientId = iPlayerInfoManager.ClientL(0);
+		}
+	// if no player have yet been registered we'll set the default when the
+	// first one is
+	
+	CleanupStack::PopAndDestroy(&players);
+	
+	iPassthroughHelper = CPassthroughHelper::NewL(iRouter, iBearer, iTimer);
+	}
+
+/** Destructor.
+*/	
+CRcpIncomingCommandHandler::~CRcpIncomingCommandHandler()
+	{
+	LOG_FUNC
+
+	iPlayerInfoManager.RemoveObserver( *this );
+	delete iFragmenter;
+	delete iPassthroughHelper;
+
+	// All commands should have been handled by the time we reach here.
+	__ASSERT_ALWAYS(iCommandQueue.IsEmpty(), AvrcpUtils::Panic(EAvrcpIncomingCommandsNotHandled));
+	__ASSERT_ALWAYS(iInternalCommandQueue.IsEmpty(), AvrcpUtils::Panic(EAvrcpIncomingCommandsNotHandled));
+	}
+
+//---------------------------------------------------------------------
+// Called from the bearer
+//---------------------------------------------------------------------
+
+/** Tell the handler to gracefully shutdown.
+
+*/
+void CRcpIncomingCommandHandler::Disconnect()
+	{
+	LOG_FUNC
+	
+	iPassthroughHelper->Disconnect();
+
+	if(!iCommandQueue.IsEmpty())
+		{
+		// Cleanup remaining commands
+		while(!iCommandQueue.IsEmpty())
+			{
+			CControlCommand* command = iCommandQueue.First();
+			// Tell RemCon the command is finished with
+			iObserver.CommandExpired(command->RemConCommandId());
+			// And now remove the command from the bearer
+			iRouter.RemoveFromSendQueue(*command);
+			HandledCommand(*command);
+			}
+		}
+	if(!iInternalCommandQueue.IsEmpty())
+		{
+				
+		// Cleanup remaining commands
+		while(!iInternalCommandQueue.IsEmpty())
+			{
+			CControlCommand* command = iInternalCommandQueue.First();
+			// Tell RemCon the command is finished with
+			iObserver.CommandExpired(command->RemConCommandId());
+			// And now remove the command from the bearer
+			iRouter.RemoveFromSendQueue(*command);
+			HandledCommand(*command);
+			}
+		}
+	}
+
+//------------------------------------------------------------------------------------
+// Called by router
+//------------------------------------------------------------------------------------
+
+void CRcpIncomingCommandHandler::MaxPacketSize(TInt /*aMtu*/)
+	{
+	// ignore - we don't care if we use AVCTP fragmentation on the
+	// control channel
+	}
+
+/** Receive an incoming AVRCP command.
+
+The command is parsed from a CAVCFrame into a CControlCommand owned by the
+command handler.
+
+@param aFrame	The AVC frame contained within 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 CRcpIncomingCommandHandler::ReceiveCommandL(const TDesC8& aMessageInformation, 
+	SymbianAvctp::TTransactionLabel aTransactionLabel, 
+	const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+		
+	TUint id = iBearer.MrcciNewTransactionId();
+	
+	// If there's nothing beyond a header this is bobs.  Dump it now.
+	if(aMessageInformation.Length() <= KAVCFrameHeaderLength)
+		{
+		User::Leave(KErrCorrupt);
+		}
+
+	// Parse it out into an AV/C frame
+	CAVCFrame* frame = CAVCFrame::NewL(aMessageInformation, AVC::ECommand);
+	
+	CControlCommand* command = CControlCommand::NewL(frame, id, aTransactionLabel, aAddr, iAddressedMode ? iClientId : KNullClientId, &AvrcpPlayerInfoManager());
+	CleanupStack::PushL(command);
+
+	TInt result = command->ParseIncomingCommandL(iObserver, *iFragmenter);
+	CleanupStack::Pop(command);
+
+	command->IncrementUsers();
+	
+	switch(result)
+		{
+		case KErrAvrcpHandledInternallyInformRemCon:
+			{
+			HandleRemConCommand(*command);
+			iRouter.AddToSendQueue(*command);
+			command->DecrementUsers();
+			break;
+			}
+		case KErrNone:
+			{
+			if(! command->IsPassthrough())
+				{
+				// add to iCommandQueue for non-passthrough commands
+				iCommandQueue.AddLast(*command);
+				}
+			HandleRemConCommand(*command);
+			break;
+			}
+		case KErrAvrcpInternalCommand:
+			{
+			iInternalCommandQueue.AddLast(*command);
+			HandleInternalCommand(*command);
+			break;
+			}
+		case KErrAvrcpInvalidCType:
+			{
+			// We should ignore commands with invalid ctype (AV/C v4.0 8.3.1).
+			command->DecrementUsers();
+			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 CRcpIncomingCommandHandler::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.
+	}
+
+//------------------------------------------------------------------------------------
+// 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 KErrNone.
+*/
+TInt CRcpIncomingCommandHandler::SendRemConResponse(TUid /*aInterfaceUid*/, TUint aId, RBuf8& aData)
+	{
+	LOG_FUNC
+
+	// We always take ownership of the response data in SendResponse, so we 
+	// always return KErrNone.
+	SendResponse(iCommandQueue, aId, aData);
+	return KErrNone;
+	}
+
+void CRcpIncomingCommandHandler::SendReject(TUid aInterfaceUid, TUint aId)
+	{
+	LOG_FUNC
+
+	TDblQueIter<CControlCommand> iter(iCommandQueue);
+	CControlCommand* command = NULL;
+
+	while (iter)
+		{
+		command = iter++;
+		if(command->RemConCommandId() == aId)
+			{
+			TInt err = KErrGeneral;
+			if (aInterfaceUid.iUid == KRemConMediaInformationApiUid || aInterfaceUid.iUid == KRemConPlayerInformationUid)
+				{
+				err = KErrAvrcpMetadataInternalError;
+				}
+			
+			Respond(*command, err);
+			
+			HandledCommand(*command);
+			}		
+		}
+	}
+
+//------------------------------------------------------------------------------------
+// MPlayerChangeObserver functions
+//------------------------------------------------------------------------------------
+void CRcpIncomingCommandHandler::MpcoAvailablePlayersChanged()
+	{
+	LOG_FUNC 
+	
+	TDblQueIter<CControlCommand> internalIter(iInternalCommandQueue);
+	CControlCommand* command = NULL;
+	TInt err = KErrNone;
+	
+	while(internalIter)
+		{
+		command = internalIter++;
+		if((command->RemConInterfaceUid() == TUid::Uid(KUidAvrcpInternalInterface)) && (command->RemConOperationId() == EAvrcpInternalAvailablePlayersNotification))
+			{
+			// If this fails we cann't send the notification - just give up
+			err = HandleRegisterAvailablePlayersNotification(*command);
+			static_cast<void>(err = err); // ignore this error (i.e. give up).
+			}
+		}
+	//register internal notification
+	//still need to optimize for avoid re-registering notification
+	RArray<TUint> players;
+	err = iPlayerInfoManager.PlayerListing(players);
+	
+	if(err == KErrNone)
+		{
+		TBool currentClientAvailable = EFalse;
+
+		for(TInt i = 0 ; i < players.Count() ; i++)
+			{
+			TRemConClientId clientId = 0;
+			clientId = iPlayerInfoManager.Client(players[i]);
+			if(clientId == iClientId)
+				{
+				currentClientAvailable = ETrue;
+				}
+			}
+		
+		if((iClientId == KNullClientId || !currentClientAvailable) && players.Count())
+			{
+			// If this is the first target client we set our default client id
+			// to this
+			iClientId = iPlayerInfoManager.Client(players[0]);
+			}
+		}
+
+	players.Close();
+	}
+
+void CRcpIncomingCommandHandler::MpcoAddressedPlayerChangedLocally(TRemConClientId aClientId)
+	{
+	LOG_FUNC
+	
+	TRAP_IGNORE(AddressedPlayerChangedL(aClientId));
+	}
+
+void CRcpIncomingCommandHandler::MpcoUidCounterChanged(TRemConClientId aClientId)
+	{
+	LOG_FUNC
+	
+	CControlCommand* command;
+	if(aClientId == iClientId)
+		{
+		command = FindNotify(iInternalCommandQueue, TUid::Uid(KUidAvrcpInternalInterface),EAvrcpInternalUidChangedNotification);
+		if(command)
+			{
+			// if we fail to send an update it is effectively the same condition
+			// as when the notification arrives after the controller uses the
+			// old UID counter
+			TInt err = HandleUidChangedNotification(*command);
+			
+			if(err != KErrNone)
+				{
+				Respond(*command, KErrAvrcpAirInternalError);
+				HandledCommand(*command);
+				}
+			}
+		}
+	}
+
+//------------------------------------------------------------------------------------
+// Internal command handling functions
+//------------------------------------------------------------------------------------
+
+
+/** Sends a response to the remote device.
+
+Because of the 100ms timeout for responses we send a response to
+passthrough commands as soon as we receive them.  This means the real
+response from RemCon is currently ignored.  A real response is
+only sent if this is a vendor dependent command.
+
+@param aCommand The command to respond to.
+@param aErr The result of handling the command.  KErrNone if successful.
+			KErrNotSupported if this operation is not supported.
+*/		
+void CRcpIncomingCommandHandler::Respond(CControlCommand& aCommand, TInt aErr)
+	{
+	LOG_FUNC
+	aCommand.SetResponseType(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 CRcpIncomingCommandHandler::HandledCommand(CControlCommand& aCommand)
+	{
+	LOG_FUNC
+	aCommand.CancelTimer(iTimer);
+	aCommand.iHandlingLink.Deque();
+	aCommand.DecrementUsers();
+	}
+
+void CRcpIncomingCommandHandler::HandleInternalCommand(CControlCommand& 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 EAvrcpInternalSetAddressedPlayer:
+		{
+		err = HandleSetAddressedPlayer(id, commandData);
+		break;
+		}
+	case EAvrcpInternalAvailablePlayersNotification:
+		{
+		err = HandleRegisterAvailablePlayersNotification(aCommand);
+		break;
+		}
+	case EAvrcpInternalAddressedPlayerNotification:
+		{
+		err = HandleRegisterAddressedPlayerNotification(aCommand);
+		break;
+		}
+	case EAvrcpInternalUidChangedNotification:
+		{
+		err = HandleUidChangedNotification(aCommand);
+		break;
+		}
+		};
+
+	if(err != KErrNone)
+		{
+		Respond(aCommand, KErrAvrcpAirInternalError);
+		HandledCommand(aCommand);
+		}
+
+	commandData.Close();
+	}
+
+void CRcpIncomingCommandHandler::HandleRemConCommand(CControlCommand& aCommand)
+	{
+	LOG_FUNC
+	
+	if(aCommand.IsPassthrough())
+		{
+		// This deals with button press/release stuff,
+		// queues the command and responds
+		iPassthroughHelper->HandlePassthrough(aCommand);
+		}
+	else 
+		{
+		// can go directly to client (unlike passthrough which may need to map 2 commands to 1 click
+		if (aCommand.Frame().Type() == AVC::ENotify)
+			{
+			iBearer.MrccciNewNotifyCommand(aCommand, aCommand.ClientId());
+			}
+		else
+			{
+			iBearer.MrcciNewCommand(aCommand, aCommand.ClientId());
+			}
+		}
+	}
+
+TInt CRcpIncomingCommandHandler::HandleSetAddressedPlayer(TUint aId, RBuf8& aCommandData)
+	{
+	LOG_FUNC
+	
+	// Once we respond to this we've told the remote that we're using a particular player
+	iAddressedMode = ETrue;
+	
+	// SetAddressedPlayer involves not just responding to this command but
+	// also rejecting a bunch of notifies and completing the addressed player
+	// changed notify.  We try as hard as we can to ensure our state remains
+	// consistent with the view of the remote.
+	
+	// Allocate the response buffer for the SetAddressedPlayer command first
+	RBuf8 responseBuf;
+	TInt err = responseBuf.Create(KSetAddressedPlayerResponseSize);
+	if(err == KErrNone)
+		{
+		// Now we know we can at least try and send a response to the remote 
+		// do the other gubbins, which we can cope with failing
+		TRAPD(setResult, DoHandleSetAddressedPlayerL(aCommandData));
+	
+		switch(setResult)
+			{
+		case KErrNotFound:
+			{
+			RAvrcpIPCError errorResponse;
+			errorResponse.iError = KErrAvrcpAirInvalidPlayerId;
+	
+			// Can ignore this as we know we have allocated a big enough buffer
+			TRAP_IGNORE(errorResponse.WriteL(responseBuf));
+			break;
+			}
+		case KErrNone:
+		default:
+			// For any result other than KErrNotFound we managed to set the 
+			// addressed client.  Other errors indicate a failure in sending
+			// a changed response to the AddressedPlayerChanged notify.  Even
+			// if we failed sending a changed response we should have been
+			// able to reject the outstanding notify so we are in a consistent
+			// state
+			{
+			RAvrcpSetAddressedPlayerResponse response;
+			response.iStatus = AvrcpStatus::ESuccess;
+			
+			// Can ignore this as we know we have allocated a big enough buffer
+			TRAP_IGNORE(response.WriteL(responseBuf));
+			break;
+			}
+			};
+	
+		SendInternalResponse(aId, responseBuf);
+		iBearer.MrccciSetAddressedClient(iClientId);
+	
+		responseBuf.Close();
+		}
+
+	return err;
+	}
+
+void CRcpIncomingCommandHandler::DoHandleSetAddressedPlayerL(RBuf8& aCommandData)
+	{
+	LOG_FUNC
+
+	RAvrcpSetAddressedPlayerRequest request;
+	request.ReadL(aCommandData);
+	
+	TRemConClientId clientId = iPlayerInfoManager.ClientL(request.iPlayerId);
+	AddressedPlayerChangedL(clientId);
+	}
+
+void CRcpIncomingCommandHandler::AddressedPlayerChangedL(TRemConClientId aClientId)
+	{
+	LOG_FUNC
+
+	if(aClientId == iClientId)
+		{
+		return;
+		}
+
+	iClientId = aClientId;
+	
+	TDblQueIter<CControlCommand> iter(iCommandQueue);
+	CControlCommand* command = NULL;
+	
+	// Complete player specific notifications
+	while(iter)
+		{
+		command = iter++;
+		if(command->PlayerSpecificNotify())
+			{
+			iObserver.CommandExpired(command->RemConCommandId());
+
+			Respond(*command, KErrAvrcpAirAddressedPlayerChanged);
+			
+			HandledCommand(*command);
+			}
+		}
+
+	command = FindNotify(iInternalCommandQueue, TUid::Uid(KUidAvrcpInternalInterface),EAvrcpInternalAddressedPlayerNotification);
+	if(command)
+		{
+		User::LeaveIfError(HandleRegisterAddressedPlayerNotification(*command));
+		}
+	}
+
+TInt CRcpIncomingCommandHandler::HandleRegisterAvailablePlayersNotification(CControlCommand& aCommand)
+	{
+	LOG_FUNC
+
+	RBuf8 responseBuf;
+	TRAPD(err, DoHandleRegisterAvailablePlayersNotificationL(responseBuf, aCommand));
+	
+	if(err == KErrNone)
+		{
+		SendInternalResponse(aCommand.RemConCommandId(), responseBuf);
+		}
+
+	responseBuf.Close();
+	return err;
+	}
+
+void CRcpIncomingCommandHandler::DoHandleRegisterAvailablePlayersNotificationL(RBuf8& aResponseData, CControlCommand& aCommand)
+	{
+	LOG_FUNC
+
+	RAvrcpIPCError response;
+	response.iError = KErrNone;
+
+	if(DuplicateNotify(iInternalCommandQueue, aCommand))
+		{
+		response.iError = KErrAvrcpAirInvalidCommand;
+		}
+	
+	aResponseData.CreateL(KRegisterNotificationEmptyResponseSize);
+	CleanupClosePushL(aResponseData);
+	
+	response.WriteL(aResponseData);
+	CleanupStack::Pop();
+	}
+
+TInt CRcpIncomingCommandHandler::HandleRegisterAddressedPlayerNotification(CControlCommand& aCommand)
+	{
+	LOG_FUNC
+
+	// Once we respond to this we've told the remote that we're using a particular player
+	iAddressedMode = ETrue;
+	
+	RBuf8 responseBuf;
+	TRAPD(err, DoHandleRegisterAddressedPlayerNotificationL(responseBuf, aCommand));
+	
+	if(!err)
+		{
+		SendInternalResponse(aCommand.RemConCommandId(), responseBuf);
+		}
+
+	responseBuf.Close();
+	return err;
+	}
+
+void CRcpIncomingCommandHandler::DoHandleRegisterAddressedPlayerNotificationL(RBuf8& aResponseData, CControlCommand& aCommand)
+	{
+	LOG_FUNC
+
+	RAvrcpAddressedPlayerNotificationResponse response;
+	RAvrcpIPCError rejectResponse;
+	
+	if(DuplicateNotify(iInternalCommandQueue, aCommand))
+		{
+		aResponseData.CreateL(KRegisterNotificationEmptyResponseSize);
+		rejectResponse.iError = KErrAvrcpAirInvalidCommand;
+		CleanupClosePushL(aResponseData);
+		rejectResponse.WriteL(aResponseData);
+		}
+	else
+		{
+		// Tricky situation thinking.Reject if at this moment the client just be shut down
+		TRAPD(err, response.iPlayerId = iPlayerInfoManager.PlayerL(iClientId));
+		if(err != KErrNone)
+			{
+			aResponseData.CreateL(KRegisterNotificationEmptyResponseSize);
+			rejectResponse.iError = KErrAvrcpAirInvalidCommand;
+			CleanupClosePushL(aResponseData);
+			rejectResponse.WriteL(aResponseData);
+			}
+		else
+			{
+			// This line will never leave once the previous line pass 
+			response.iUidCounter = iPlayerInfoManager.UidCounterL(iClientId);
+			aResponseData.CreateL(KRegisterNotificationAddressedPlayerResponseSize);
+			CleanupClosePushL(aResponseData);
+			response.WriteL(aResponseData);
+			}
+		}
+	
+	CleanupStack::Pop();
+	}
+
+TInt CRcpIncomingCommandHandler::HandleUidChangedNotification(CControlCommand& aCommand)
+	{
+	LOG_FUNC
+
+	// Although we haven't strictly told the remote which player we're using this is 
+	// a 1.4 command, and implies use of a specific player so switch into addressed mode
+	iAddressedMode = ETrue;
+
+	RBuf8 responseBuf;
+	TUint16 uidCounter = 0;
+	TRAPD(err, uidCounter = iPlayerInfoManager.UidCounterL(iClientId));
+	
+	if(err == KErrNone && !DuplicateNotify(iInternalCommandQueue, aCommand))
+		{
+		TRAP(err, DoHandleUidChangedNotificationL(responseBuf, uidCounter));
+		}
+
+	if(err == KErrNone)
+		{
+		SendInternalResponse(aCommand.RemConCommandId(), responseBuf);
+		}
+
+	responseBuf.Close();
+	return err;
+	}
+
+void CRcpIncomingCommandHandler::DoHandleUidChangedNotificationL(RBuf8& aResponseData, TUint16 aUidCounter)
+	{
+	LOG_FUNC
+
+	RAvrcpUidCounterNotificationResponse response;
+	response.iUidCounter = aUidCounter;
+	aResponseData.CreateL(KRegisterNotificationUidChangedResponseSize);
+	CleanupClosePushL(aResponseData);
+	response.WriteL(aResponseData);
+	CleanupStack::Pop();
+	}
+
+void CRcpIncomingCommandHandler::SendInternalResponse(TUint aId, RBuf8& aData)
+	{
+	LOG_FUNC 
+
+	SendResponse(iInternalCommandQueue, aId, aData);
+	}
+
+/**
+This function always takes responsibility for the response.
+*/
+void CRcpIncomingCommandHandler::SendResponse(TDblQue<CControlCommand>& aCommandQueue, TUint aId, RBuf8& aData)
+	{
+	LOG_FUNC
+	
+	TDblQueIter<CControlCommand> iter(aCommandQueue);
+	CControlCommand* command = NULL;
+
+	while (iter)
+		{
+		command = iter++;
+		if(command->RemConCommandId() == aId)
+			{
+			TInt err = command->ProcessOutgoingResponse(iObserver, aData, *iFragmenter);
+			
+			if(command->Frame().Type() == AVC::ENotify)
+				{
+				// If the interim response was successful then create a new command 
+				// to contain the final response when it arrives.  The same command
+				// cannot be re-used as we may not have finished sending this before
+				// we get the final response.  We won't have both the commands on 
+				// the handling queue at one time though, so there is no ambiguity
+				// about which is which.  We finish handling the interim response here.
+				if(err == KErrNone)
+					{
+					// Try creating the CControlCommand which will be used for the
+					// final response to the Notify.  If it fails then we will just
+					// reject the notify straight away.
+					//
+					// To start with we set its AV/C frame to be an Interim response, 
+					// since this will match the AV/C frame of the original CControlCommand
+					// once we send the interim response later in this function.
+					CControlCommand* finalResponse = NULL;
+					TRAP(err, finalResponse = command->InterimResponseL());
+				
+					if(err == KErrNone)
+						{
+						finalResponse->IncrementUsers();
+						aCommandQueue.AddLast(*finalResponse);
+						}
+					}
+				
+				if(err != KErrNone && command->NormalCommand())
+					{
+					// If we had an unsuucessful interim response, we need to remove the command from remcon
+					iObserver.CommandExpired(aId);
+					}
+				}
+
+			Respond(*command, err);
+			HandledCommand(*command);
+			break;  // Exit while (iter) loop
+			}
+		}
+	
+	// Either we have created a response which took ownership of aData, or
+	// we didn't match a command, so this was a response to something we've
+	// already removed from our queue.  We're telling RemCon that we dealt ok
+	// with this so we have resonsibility for tidying up the data.
+	aData.Close();
+	}
+
+TBool CRcpIncomingCommandHandler::DuplicateNotify(TDblQue<CControlCommand>& aCommandQueue, const CControlCommand& aCommand) const
+	{
+	TUid interfaceUid = aCommand.RemConInterfaceUid();
+	TUint operationId = aCommand.RemConOperationId();
+	
+	CControlCommand* command = NULL;
+	TDblQueIter<CControlCommand> iter(aCommandQueue);
+	TInt count = 0;
+	TBool duplicate = EFalse;
+	
+	while(iter)
+		{
+		command = iter++;
+		if((interfaceUid == command->RemConInterfaceUid())&&(operationId == command->RemConOperationId()))
+			{
+			count++;
+			// this should be a reject if we've already got a notification outstanding
+			if(count > 1)
+				{
+				duplicate = ETrue;
+				break;
+				}
+			}
+		}
+	
+	return duplicate;
+	}
+
+CControlCommand* CRcpIncomingCommandHandler::FindNotify(TDblQue<CControlCommand>& aCommandQueue, TUid aInterfaceUid, TUint aOperationId)
+	{
+	CControlCommand* command = NULL;
+	TDblQueIter<CControlCommand> iter(aCommandQueue);
+	TBool found = EFalse;
+	
+	while(iter)
+		{
+		command = iter++;
+
+		if((command->RemConInterfaceUid() == aInterfaceUid)&&(command->RemConOperationId() == aOperationId))
+			{
+			found = ETrue;
+			break;
+			}
+		}
+	
+	return found ? command : NULL;
+	}