bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpMetadataTransfer.cpp
changeset 0 f63038272f30
child 1 6a1fe72036e3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpMetadataTransfer.cpp	Mon Jan 18 20:28:57 2010 +0200
@@ -0,0 +1,1078 @@
+// Copyright (c) 2006-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
+ @prototype
+*/
+
+#include <avcframe.h>
+#include <e32base.h>
+#include <remcon/remconbearerobserver.h>
+#include <remcon/remconconverterplugin.h>
+#include <remcon/messagetype.h>
+#include <remcon/avrcpspec.h>
+#include <remconbeareravrcp.h>
+
+#include <remconbatterytargetobserver.h>
+#include <absolutevolumeapi.h>
+#include <absolutevolumeutils.h>
+
+#include "avrcp.h"
+#include "controlcommand.h"
+#include "avrcpcommandframer.h"
+#include "avrcpinternalinterface.h"
+#include "avrcpipc.h"
+#include "avrcplog.h"
+#include "avrcputils.h"
+#include "avrcpincomingcommandhandler.h"
+#include "mediabrowse.h"
+#include "mediainformation.h"
+#include "nowplaying.h"
+#include "playerinformation.h"
+#include "remconbattery.h"
+#include "remcongroupnavigation.h"
+
+TInt CControlCommand::ParseMetadataTransferPassthroughCommand()
+	{
+	// company id and frame type should already be set before here
+	__ASSERT_DEBUG(iFrame->Type() == AVC::EControl,  AvrcpUtils::Panic(EAvrcpCTypeNotSet));
+	__ASSERT_DEBUG(iVendorId == KBluetoothSIGVendorId,  AvrcpUtils::Panic(EAvrcpCompanyIDNotSet));
+
+	TInt ret = KErrNotSupported;
+	TUint16 operation = MetadataTransferParser::GetPassThroughOperationId(iVendorPayloadData);
+	if (operation == ENextGroup)
+		{
+		iOperationId = ENextGroup;
+		iInterfaceUid = TUid::Uid(KRemConGroupNavigationApiUid);
+		ret = KErrNone;
+		}
+	else if (operation == EPreviousGroup)
+		{
+		iOperationId = EPreviousGroup;
+		iInterfaceUid = TUid::Uid(KRemConGroupNavigationApiUid);
+		ret = KErrNone;
+		}
+	return ret;
+	}
+
+/* Before calling this method, the following MUST be set:
+ *  - Vendor Payload data
+ *  - Opcode       == Vendor Dependent
+ *  - Subunit Type == Panel
+ *  - Subunit Id   == 0
+ */
+ 
+TInt CControlCommand::ParseMetadataTransferVendorCommand(CAVRCPFragmenter& aFragmenter)
+	{
+	__ASSERT_DEBUG(iFrame->Opcode() == AVC::EVendorDependent, AvrcpUtils::Panic(EAvrcpNotFullyConstructed));
+	__ASSERT_DEBUG(iFrame->SubunitType() == AVC::EPanel,      AvrcpUtils::Panic(EAvrcpNotFullyConstructed));
+	__ASSERT_DEBUG(iFrame->SubunitID() == 0,                  AvrcpUtils::Panic(EAvrcpNotFullyConstructed));
+	
+	TInt ret = KErrNotSupported;
+	iInterfaceUid = TUid::Uid(0);
+	
+	TMetadataTransferPDUID metadataPDUID = MetadataTransferParser::GetPDUID(iVendorPayloadData);
+	
+	TPtrC8 mtPayload(iVendorPayloadData.Mid(KRemConMetadataHeaderLength));
+	
+	TUint paramLength = MetadataTransferParser::GetParamLength(iVendorPayloadData);
+	
+	if (mtPayload.Length() != paramLength)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	// If we're in a fragmented state, and we receive an un-expected
+	// PDU (i.e. not CONTINUE or ABORT) then throw away the fragment
+	// and just process the request. The specification (section 5.5.1)
+	// isn't entirely clear as to what should happen, but Sian reckons
+	// this is probably the most polite thing to do (be liberal, etc)
+	if (aFragmenter.InFragmentedState())
+		{
+		if (   metadataPDUID != ERequestContinuingResponse
+			&& metadataPDUID != EAbortContinuingResponse)
+			{
+			aFragmenter.Reset();
+			}
+		}
+		
+	switch (iFrame->Type())
+		{
+		case AVC::EControl:
+			{
+			switch (metadataPDUID)	
+				{
+				case ERequestContinuingResponse:
+					ret = ParseContinuingResponse(mtPayload, aFragmenter);
+					break;
+				case EAbortContinuingResponse:
+					ret = ParseAbortContinuingResponse(mtPayload, aFragmenter);
+					break;
+				case ESetPlayerApplicationSettingValue:
+					ret = ParseSetPlayerApplicationSettingValue(mtPayload);
+					break;
+					
+				case EInformBatteryStatusOfCT:
+					ret = ParseInformBatteryStatusOfCT(mtPayload);
+					break;
+					
+				case EInformDisplayableCharacterSet:
+					ret = KErrNotSupported;
+					break;
+				case ESetAddressedPlayer:
+					ret = ParseSetAddressedPlayer(mtPayload);
+					break;
+				case EPlayItem:
+					ret = ParsePlayItem(mtPayload);
+					break;
+				case EAddToNowPlaying:
+					ret = ParseAddToNowPlaying(mtPayload);
+					break;
+					
+				case ESetAbsoluteVolume:
+				    ret =ParseSetAbsoluteVolume(mtPayload);
+				    break;
+				default:
+					// not allowed PDUID for Control
+					ret = KErrAvrcpMetadataInvalidCommand;
+				}
+			break;
+			}
+		case AVC::EStatus:
+			{
+			switch (metadataPDUID)	
+				{
+				case EGetCapabilities:
+					ret = ParseGetCapabilities( mtPayload );
+					break;
+					
+				case EGetPlayStatus:
+					ret = ParseGetPlayStatus( mtPayload );
+					break;
+					
+				case EListPlayerApplicationSettingAttributes:
+					ret = ParseListPlayerApplicationSettingAttributes( mtPayload );
+					break;
+					
+				case EListPlayerApplicationSettingValues:
+					ret = ParseListPlayerApplicationSettingValues( mtPayload );
+					break;
+					
+				case EGetCurrentPlayerApplicationSettingValue:
+					ret = ParseGetCurrentPlayerApplicationSettingValue( mtPayload );
+					break;
+
+				case EGetPlayerApplicationSettingAttributeText:
+					ret = ParseGetPlayerApplicationSettingAttributeText( mtPayload );
+					break;
+
+				case EGetPlayerApplicationSettingValueText:
+					ret = ParseGetPlayerApplicationSettingValueText( mtPayload );
+					break;
+					
+				case EGetElementAttributes:
+					ret = ParseGetElementAttributes(mtPayload );
+					break;
+					
+				case ESetAddressedPlayer:
+					ret = ParseSetAddressedPlayer(mtPayload );
+					break;
+				
+				case EPlayItem:
+					ret = ParsePlayItem(mtPayload );
+					break;
+					
+				case EAddToNowPlaying:
+					ret = ParseAddToNowPlaying(mtPayload );
+					break;
+				default:
+					// not allowed PDUID for Status
+					ret = KErrAvrcpMetadataInvalidCommand;
+				}
+				
+			break;
+			}
+		case AVC::ENotify:
+			{
+			if (metadataPDUID==ERegisterNotification)
+				{
+				if (mtPayload.Length() != KLengthRegisterNotification)
+					{
+					ret = KErrAvrcpMetadataInvalidCommand;
+					}
+				else
+					{
+					ret = KErrNone;
+					TRegisterNotificationEvent eventId = static_cast<TRegisterNotificationEvent>(mtPayload[KVendorDependentEventId]);
+					switch (eventId)
+						{
+						case ERegisterNotificationPlaybackStatusChanged:
+						case ERegisterNotificationTrackChanged:
+						case ERegisterNotificationTrackReachedEnd:
+						case ERegisterNotificationTrackReachedStart:
+						case ERegisterNotificationBatteryStatusChanged:
+						case ERegisterNotificationPlayerApplicationSettingChanged:
+							{
+							iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+							iOperationId = RAvrcpIPC::SetIPCOperationIdFromEventId(eventId);
+							break;
+							}
+						// Note: ERegisterNotificationPlaybackPosChanged takes a 4 byte parameter
+						case ERegisterNotificationPlaybackPosChanged:
+							{
+							iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+							iOperationId = RAvrcpIPC::SetIPCOperationIdFromEventId(eventId);
+							iCommandData.Close();
+							ret = iCommandData.Create(KLengthPlaybackPosChangedParam);
+							if (ret == KErrNone)
+								{
+								iCommandData.Append(mtPayload.Right(KLengthPlaybackPosChangedParam));
+								}
+							else
+								{
+								ret = KErrAvrcpMetadataInternalError;
+								}
+							break;
+							}
+							
+						// Note ERegisterNotificationSystemStatusChanged is not supported
+						case ERegisterNotificationSystemStatusChanged_NotSupported:
+							{
+							ret = KErrNotSupported;
+							break;
+							}
+							
+						case ERegisterNotificationNowPlayingContentChanged:
+							{
+							iInterfaceUid = TUid::Uid(KRemConNowPlayingApiUid);
+							iOperationId = RAvrcpIPC::SetIPCOperationIdFromEventId(eventId);
+							break;
+							}
+						case ERegisterNotificationAvailablePlayersChanged:
+						case ERegisterNotificationAddressedPlayerChanged:
+						case ERegisterNotificationUidsChanged:
+							{
+							iInterfaceUid = TUid::Uid(KUidAvrcpInternalInterface);
+							iOperationId = RAvrcpIPC::SetIPCOperationIdFromEventId(eventId);
+							ret = KErrAvrcpInternalCommand;
+							break;
+							}
+						case ERegisterNotificationVolumeChanged	:
+							{
+							ret = ParseVolumeChangedNotification(mtPayload);
+							break;
+							}
+						default:
+							{
+							ret = KErrAvrcpMetadataInvalidParameter;
+							}
+						}
+					}
+				}
+			else
+				{
+				ret = KErrAvrcpMetadataInvalidCommand;
+				}
+			break;
+			}
+		case AVC::EReserved1:
+		case AVC::EReserved2:
+		case AVC::EReserved3:
+		case AVC::EReserved4:
+			{
+			ret = KErrAvrcpInvalidCType;
+			break;
+			}
+		default:
+			ret = KErrAvrcpMetadataInvalidCommand;
+		}
+
+	return ret;
+	}
+
+/** Allocate correct space and append the payload to iCommandData
+ */
+TInt CControlCommand::AppendIncomingPayload(const TPtrC8& aPayload)
+	{
+	iCommandData.Close();
+	if (iCommandData.Create(aPayload.Length()) != KErrNone)
+		{
+		return KErrAvrcpMetadataInternalError;
+		}
+	iCommandData.Append(aPayload);
+	return KErrNone;
+	}
+
+/** Decode a InformBatteryStatusOfCT PDU ID: 0x18 and call the BatteryStatus API 
+ */
+TInt CControlCommand::ParseInformBatteryStatusOfCT(TPtrC8& aMtPayload)
+												 
+	{
+	if (aMtPayload.Length() != KLengthInformBatteryStatusOfCTPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	TUint8 batteryStatus = aMtPayload[0];
+	if (batteryStatus > EFullCharge)
+		{
+		return KErrAvrcpMetadataInvalidParameter;
+		}
+	
+	if (AppendIncomingPayload(aMtPayload) != KErrNone)
+		{
+		return KErrAvrcpMetadataInvalidParameter;
+		}
+	
+	iOperationId = EInformBatteryStatusOfCT;
+	iInterfaceUid = TUid::Uid(KRemConBatteryApiUid);
+		
+	TInt ret = RespondToInforms(EInformBatteryStatusOfCT);
+	if (ret != KErrNone)
+		{
+		return ret;
+		}
+	
+	return KErrAvrcpHandledInternallyInformRemCon;
+	}
+
+
+/** Decode PDU ID 0x40 - fragmentation support
+ */
+TInt CControlCommand::ParseContinuingResponse(TPtrC8& aMtPayload,
+										  CAVRCPFragmenter& aFragmenter)
+	{
+	// Check if in fragmentation state, return error if not
+	if (! aFragmenter.InFragmentedState())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	// Check if the parameter matches the fragmented response
+	TMetadataTransferPDUID pduId = MetadataTransferParser::GetPDUID(aMtPayload);
+	if (pduId != aFragmenter.GetPDU())
+		{
+		return KErrAvrcpMetadataInvalidParameter;
+		}
+	
+	RBuf8 respPayload;
+	CAVCFrame* frame = NULL;
+	TRAPD(err, frame = CAVCVendorDependentResponse::NewL(KBluetoothSIGVendorId));
+	err = respPayload.Create(KAVCMaxVendorDependentPayload); //longest resp
+	if (err == KErrNone)
+		{
+		respPayload.Append(aFragmenter.GetNextFragmentHeader());
+		respPayload.Append(aFragmenter.GetNextFragment());
+		frame->SetType(AVC::EStable);
+		frame->Append(respPayload);
+		delete iFrame;
+		iFrame = frame;
+		respPayload.Close();
+		}
+	else
+		return KErrAvrcpMetadataInternalError;
+	
+	return KErrAvrcpHandledInternallyRespondNow;
+	}
+
+
+/** Decode PDU ID 0x41 - fragmentation support
+ */
+TInt CControlCommand::ParseAbortContinuingResponse(TPtrC8& aMtPayload,
+												CAVRCPFragmenter& aFragmenter)
+	{
+	// Check if in fragmentation state, return error if not
+	if (! aFragmenter.InFragmentedState())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	// Check if the parameter matches the fragmented response
+	TMetadataTransferPDUID pduId = MetadataTransferParser::GetPDUID(aMtPayload);
+	if (pduId != aFragmenter.GetPDU())
+		{
+		return KErrAvrcpMetadataInvalidParameter;
+		}
+	
+	aFragmenter.Reset();
+	
+	CAVCFrame* frame = NULL;
+	TRAPD(err, frame = CAVCVendorDependentResponse::NewL(KBluetoothSIGVendorId));
+	if (err == KErrNone)
+		{
+		frame->Append(EAbortContinuingResponse);
+		frame->Append(EUnfragmented);
+		// the package length is 0
+		frame->Append( 0 );
+		frame->Append( 0 );
+		frame->SetType(AVC::EStable);
+		delete iFrame;
+		iFrame = frame;
+		}
+	else
+		{
+		return KErrAvrcpMetadataInternalError;
+		}
+	
+	return KErrAvrcpHandledInternallyRespondNow;
+	}
+
+
+/** Decode a SetPlayerApplicationSettingValue PDU ID: 0x14 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseSetPlayerApplicationSettingValue(TPtrC8& aMtPayload)
+	{
+	if (aMtPayload.Length() < KMinLengthSetPASValuePdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	// get the number of attributes contained 
+	TUint8 numAttributes = aMtPayload[KVendorDependentNumberAttributes];
+	
+	// Each attribute is 16 bits long
+	if (sizeof(numAttributes) + numAttributes*sizeof(TUint16) != aMtPayload.Length())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = ESetPlayerApplicationSettingValue;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+/** Decode a GetCapabilities PDU ID: 0x10 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetCapabilities(TPtrC8& aMtPayload)
+	{
+	// check enough data available
+	if (aMtPayload.Length() != KLengthGetCapabilitiesPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	// pass one byte of data to Player Information API 
+	iOperationId = EGetCapabilities;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+/** Decode a GetPlayStatus PDU ID: 0x30 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetPlayStatus(TPtrC8& aMtPayload)
+	{
+	// no payload in this command
+	if (aMtPayload.Length()!=KLengthGetPlayStatusPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = EGetPlayStatus;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return KErrNone;
+	}
+
+/** Decode a ListPlayerApplicationSettingAttributes PDU ID: 0x11 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseListPlayerApplicationSettingAttributes(TPtrC8& aMtPayload)
+	{
+	// check length, there should be no parameters
+	if (aMtPayload.Length()!=KLengthListPASAttributesPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+
+	iOperationId = EListPlayerApplicationSettingAttributes;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return KErrNone;
+	}
+
+/** Decode a ListPlayerApplicationSettingValues PDU ID: 0x12 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseListPlayerApplicationSettingValues(TPtrC8& aMtPayload)
+	{
+	// check length, there should be 1 byte of data
+	if (aMtPayload.Length() != KLengthListPASValuesPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	iOperationId = EListPlayerApplicationSettingValues;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+/** Decode a GetCurrentPlayerApplicationSettingValue PDU ID: 0x13 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetCurrentPlayerApplicationSettingValue(TPtrC8& aMtPayload)
+	{
+	// check the length
+	if (aMtPayload.Length() < KMinLengthGetCurrentPASValuePdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	TUint8 numAttributes = aMtPayload[KVendorDependentNumberAttributes];
+	if (sizeof(numAttributes) + numAttributes != aMtPayload.Length())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = EGetCurrentPlayerApplicationSettingValue;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+/** Decode a GetPlayerApplicationSettingAttributeText PDU ID: 0x15 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetPlayerApplicationSettingAttributeText(TPtrC8& aMtPayload)
+	{
+	if (aMtPayload.Length() < KMinLengthGetPASAttributeTextPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	// get the number of attributes contained 
+	TUint8 numAttributes = aMtPayload[KVendorDependentNumberAttributes];
+	if (sizeof(numAttributes) + numAttributes != aMtPayload.Length())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = EGetPlayerApplicationSettingAttributeText;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+/** Decode a GetPlayerApplicationSettingValueText PDU ID: 0x16 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetPlayerApplicationSettingValueText(TPtrC8& aMtPayload)
+	{
+	if (aMtPayload.Length() < KMinLengthGetPASValueTextPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	// get number of values; preceded by attributeId (1byte) and numValues (1byte)
+	TUint8 numValues = aMtPayload[KVendorDependentNumberAttribsPdu16];
+	if (sizeof(TUint8) + sizeof(numValues) + numValues != aMtPayload.Length())
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = EGetPlayerApplicationSettingValueText;
+	iInterfaceUid = TUid::Uid(KRemConPlayerInformationUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+	
+/** Decode a GetElementAttributes PDU ID: 0x20 and call the PlayerInformation API 
+ */
+TInt CControlCommand::ParseGetElementAttributes(TPtrC8& aMtPayload)
+	{
+	LOG_FUNC
+	
+	if (aMtPayload.Length() < KMinLengthGetElementAttributesPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	iOperationId = EGetElementAttributes;
+	iInterfaceUid = TUid::Uid(KRemConMediaInformationApiUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+TInt CControlCommand::ParseSetAddressedPlayer(TPtrC8& aMtPayload)
+	{
+	LOG_FUNC
+	
+	if (aMtPayload.Length() < KLengthSetAddressedPlayerPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+
+	iOperationId = EAvrcpInternalSetAddressedPlayer;
+	iInterfaceUid = TUid::Uid(KUidAvrcpInternalInterface);
+	TInt err = AppendIncomingPayload(aMtPayload);
+
+	return err == KErrNone ? KErrAvrcpInternalCommand : err;
+	}
+
+TInt CControlCommand::ParsePlayItem(TPtrC8& aMtPayload)
+	{
+	LOG_FUNC
+	
+	if (aMtPayload.Length() < KMinLengthAddToNowPlayingPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	iOperationId = EPlayItem;
+	iInterfaceUid = TUid::Uid(KRemConNowPlayingApiUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+TInt CControlCommand::ParseAddToNowPlaying(TPtrC8& aMtPayload)
+	{
+	LOG_FUNC
+	
+	if (aMtPayload.Length() < KMinLengthAddToNowPlayingPdu)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+		
+	iOperationId = EAddToNowPlaying;
+	iInterfaceUid = TUid::Uid(KRemConNowPlayingApiUid);
+	return AppendIncomingPayload(aMtPayload);
+	}
+
+TInt CControlCommand::ParseUidsChangedNotification(TPtrC8& /*aMtPayload*/)
+	{
+	LOG_FUNC
+	iInterfaceUid = TUid::Uid(KRemConMediaBrowseApiUid);
+	iOperationId = RAvrcpIPC::SetIPCOperationIdFromEventId(ERegisterNotificationUidsChanged);
+	
+	return KErrAvrcpHandledInternallyRespondNow;	
+	}
+
+TInt CControlCommand::ParseVolumeChangedNotification(TPtrC8& /*aMtPayload*/)
+	{
+	LOG_FUNC
+	iInterfaceUid = TUid::Uid(KRemConAbsoluteVolumeTargetApiUid);
+	iOperationId = KRemConAbsoluteVolumeNotification;
+	
+	return KErrNone;
+	}
+
+TInt CControlCommand::GenerateMetadataResponsePayload(MRemConBearerObserver& aObserver, RBuf8& aFramePayload, const RBuf8& aResponseData)
+	{
+	TInt err = KErrNone;
+	
+	// If it's a very large response; this requires re-allocating the buffer
+	if (aResponseData.Length() > KAVCFrameMaxLength)
+		{
+		aFramePayload.Close();
+		if (aFramePayload.Create(aResponseData.Length()) != KErrNone)
+			return KErrAvrcpMetadataInternalError;
+		}
+	
+	// Obtain the PDU from the combined PDU + (possible) notification eventId
+	TMetadataTransferPDU pduId = RAvrcpIPC::GetPDUIdFromIPCOperationId(iOperationId);
+	aFramePayload.Zero();
+	aFramePayload.Append(pduId);
+	aFramePayload.Append(EUnfragmented);
+
+	//Check whether it is absolute volume response.
+	TBool absoluteVolumeResponse = EFalse;
+	if (pduId == ESetAbsoluteVolume)
+		{
+		absoluteVolumeResponse = ETrue;
+		}
+	else if (pduId == ERegisterNotification)
+		{
+		TRegisterNotificationEvent eventId = RAvrcpIPC::GetEventIdFromIPCOperationId(iOperationId);
+		if (eventId == ERegisterNotificationVolumeChanged)
+			{
+			absoluteVolumeResponse = ETrue;
+			}
+		}
+	
+	TPtr8 responseData(NULL, 0);
+	if (absoluteVolumeResponse)
+		{
+		responseData.Set(aResponseData.RightTPtr(aResponseData.Length()));
+		}
+	else
+		{
+		// Read 4 byte Big-Endian error code before the payload
+		RAvrcpIPCError response;
+		TRAP(err, response.ReadL(aResponseData));
+		
+		// If we couldn't parse the response via IPC, send an internal error
+		if (err != KErrNone)
+			{
+			return KErrAvrcpMetadataInternalError;
+			}
+		
+		// If an error occurred, return it (now that we've filled in the PDU id)
+		if (response.iError != KErrNone)
+			{
+			return response.iError;
+			}
+		
+		// Pass the rest of the response (minus error code) to be parsed
+		responseData.Set(aResponseData.RightTPtr(aResponseData.Length() - KLengthErrorResponse));
+		}
+	
+	switch (pduId)
+		{
+		case ESetPlayerApplicationSettingValue:
+			{
+			// the package length is 0
+			aFramePayload.Append( 0 );
+			aFramePayload.Append( 0 );
+			break;
+			}
+		
+		case EGetCapabilities:
+			{
+			TRAP(err,GenerateMetadataGetCapabilitiesResponsePayloadL(aObserver, aFramePayload, aResponseData));
+			if(err != KErrNone)
+				{
+				return KErrAvrcpMetadataInternalError;
+				}
+			break;
+			}
+		case EListPlayerApplicationSettingAttributes:
+		case EListPlayerApplicationSettingValues:
+		case EGetCurrentPlayerApplicationSettingValue:
+		case EGetPlayerApplicationSettingAttributeText:
+		case EGetPlayerApplicationSettingValueText:
+		case EGetElementAttributes:
+		case EGetPlayStatus:
+		case EPlayItem:
+		case EAddToNowPlaying:
+		case ESetAddressedPlayer:
+			{
+			// the package length is the response length
+			TInt packageLength = responseData.Length();
+			aFramePayload.Append(packageLength>>8);
+			aFramePayload.Append(packageLength);
+			aFramePayload.Append(responseData);
+			break;
+			}
+		case ESetAbsoluteVolume:
+			{			
+			TRAPD(err, GenerateSetAbsoluteVolumeResponsePayloadL(aFramePayload,responseData));
+			if (err != KErrNone)
+				{
+				return KErrAvrcpMetadataInternalError;
+				}
+		    break;
+			}
+		case ERegisterNotification:
+			{		
+			GenerateNotificationResponsePayload(aFramePayload, responseData);
+			break;
+			}		
+		default:
+			{
+			return KErrNotSupported;
+			}
+		}
+
+	// Success. Error conditions have been handled previously
+	return KErrNone;
+	}
+
+void CControlCommand::GenerateSetAbsoluteVolumeResponsePayloadL(
+		RBuf8& aFramePayload, 
+		const TDesC8& responseData)
+	{
+	RRemConAbsoluteVolumeResponse response;
+	CleanupClosePushL(response);
+	
+	response.ReadL(responseData);
+	if (response.iError != KErrNone)
+	    {
+	    User::Leave(response.iError);
+	    }
+	TUint8 absVol = KAvrcpMaxAbsoluteVolume * response.iVolume / response.iMaxVolume;	
+	TUint16 len = KLengthSetAbsoluteVolumeResponseParamter<<8 & 0xffff;
+	TPckgBuf<TUint16> parameterLength(len);
+	aFramePayload.Append(parameterLength);
+	aFramePayload.Append( absVol );
+	
+	CleanupStack::PopAndDestroy(&response);
+	}
+
+void CControlCommand::DoGenerateNotifyVolumeChangeResponsePayloadL(
+		RBuf8& aFramePayload, 
+		const TDesC8& responseData)
+	{
+	RRemConAbsoluteVolumeResponse response;
+	CleanupClosePushL(response);
+	
+	response.ReadL(responseData);
+	if (response.iError != KErrNone)
+	    {
+	    User::Leave(response.iError);
+	    }
+	TUint8 absVol = KAvrcpMaxAbsoluteVolume * response.iVolume / response.iMaxVolume;
+	TRegisterNotificationEvent eventId = RAvrcpIPC::GetEventIdFromIPCOperationId(iOperationId);
+	TUint16 len = KLengthNotifyVolumeChangeResponseParameter<<8 & 0xffff;
+	TPckgBuf<TUint16> parameterLength(len);
+	aFramePayload.Append( parameterLength );
+	aFramePayload.Append( eventId );
+	aFramePayload.Append( absVol );
+	
+	CleanupStack::PopAndDestroy(&response);
+	}
+
+TInt CControlCommand::GenerateNotificationResponsePayload(RBuf8& aFramePayload, const TDesC8& responseData)
+	{
+	TInt err = KErrNone;
+	
+	TRegisterNotificationEvent eventId = RAvrcpIPC::GetEventIdFromIPCOperationId(iOperationId);
+	switch(eventId)
+		{
+	case ERegisterNotificationVolumeChanged:
+		{
+		TRAPD(err, DoGenerateNotifyVolumeChangeResponsePayloadL(aFramePayload, responseData));
+		if (err != KErrNone)
+			{
+			return KErrAvrcpMetadataInternalError;
+			}
+		break;
+		}
+	case ERegisterNotificationPlaybackStatusChanged:
+	case ERegisterNotificationTrackChanged:
+	case ERegisterNotificationTrackReachedEnd:
+	case ERegisterNotificationTrackReachedStart:
+	case ERegisterNotificationPlaybackPosChanged:
+	case ERegisterNotificationBatteryStatusChanged:
+	case ERegisterNotificationPlayerApplicationSettingChanged:
+	case ERegisterNotificationNowPlayingContentChanged:
+	case ERegisterNotificationAvailablePlayersChanged:
+	case ERegisterNotificationAddressedPlayerChanged:
+	case ERegisterNotificationUidsChanged:
+		{
+		TUint paramLength = responseData.Length() + 1;
+		aFramePayload.Append( paramLength >>8 );
+		aFramePayload.Append( paramLength );
+		aFramePayload.Append( eventId );
+		aFramePayload.Append( responseData );
+		break;
+		}
+	default:
+		{
+		err = KErrNotSupported;
+		break;
+		}
+		};
+
+	return err;
+	}
+
+TInt CControlCommand::GenerateMetadataGetCapabilitiesResponsePayloadL(MRemConBearerObserver& /* aObserver */, RBuf8& aFramePayload, const RBuf8& aResponseData)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG( iPlayerInfoManager != NULL, AvrcpUtils::Panic(EAvrcpNotFullyConstructed));
+	TPtr8 responseData = aResponseData.RightTPtr(aResponseData.Length() - KLengthErrorResponse);
+
+	if(responseData[KCapabilityIdOffset] == ECapabilityIdEventsSupported)
+		{
+		RBuf8 eventsBuf;
+		eventsBuf.CreateL(KNumberEventsNotInPlayerInfoApi);
+		CleanupClosePushL(eventsBuf);
+		TInt count = 0;
+		
+		// if ClientId has been set it means that we've told the remote that
+		// commands are being addressed to a particular player, so return
+		// player specific info.  Otherwise return generic support.
+		if(ClientId() == KNullClientId || iPlayerInfoManager->AbsoluteVolumeSupportedL(ClientId()))
+			{
+			count++;
+			eventsBuf.Append(ERegisterNotificationVolumeChanged );
+			}
+		
+		if(ClientId() == KNullClientId || iPlayerInfoManager->BrowsingSupportedL(ClientId()))
+			{
+			count += 2;
+			eventsBuf.Append(ERegisterNotificationNowPlayingContentChanged );
+			eventsBuf.Append(ERegisterNotificationUidsChanged );
+			}
+		
+		// Always mark support for stuff that's handled internally rather than
+		// by the player
+		count+= 2;
+		eventsBuf.Append(ERegisterNotificationAvailablePlayersChanged );
+		eventsBuf.Append(ERegisterNotificationAddressedPlayerChanged );
+		
+		responseData[1] += count;
+
+		TInt packageLength = responseData.Length()+ count;
+		aFramePayload.Append(packageLength>>8);
+		aFramePayload.Append(packageLength);
+		aFramePayload.Append(responseData);
+		aFramePayload.Append(eventsBuf);
+		
+		CleanupStack::PopAndDestroy(&eventsBuf);
+		}
+	else
+		{
+		TInt packageLength = responseData.Length();
+		aFramePayload.Append(packageLength>>8);
+		aFramePayload.Append(packageLength);
+		aFramePayload.Append(responseData);
+		}
+	return KErrNone;
+	}
+TMetadataTransferPDUID MetadataTransferParser::GetNotifyEventID(const TPtrC8& aData)
+	{
+	return static_cast<TMetadataTransferNotifyEventID>(aData[KVendorDependentNotifyEventIdOffset]);
+	}
+	
+TMetadataTransferPDUID MetadataTransferParser::GetPDUID(const TPtrC8& aData)
+	{
+	return static_cast<TMetadataTransferPDUID>(aData[KVendorDependentRequestPDUId]);
+	}
+
+TUint16 MetadataTransferParser::GetParamLength(const TPtrC8& aData)
+	{
+	TUint16 paramLength;
+	paramLength  = aData[KVendorDependentRequestParamLenMSB] << 8;
+	paramLength += aData[KVendorDependentRequestParamLenLSB];
+	return paramLength;
+	}
+
+TUint16 MetadataTransferParser::GetPassThroughOperationId(const TPtrC8& aData)
+	{
+	TUint16 operation;
+	operation  = aData[KPassThroughRequestOperationIdMSB] << 8;
+	operation += aData[KPassThroughRequestOperationIdLSB];
+	return operation;
+	}
+
+	
+TInt CControlCommand::RespondToInforms(TMetadataTransferPDUID aMetadataPDUID)
+	{
+	CAVCFrame* frame = NULL;
+	TRAPD(err, frame = CAVCVendorDependentResponse::NewL(KBluetoothSIGVendorId));
+		
+	RBuf8 respPayload;
+	err = respPayload.Create(KAVRCPMinVendorDependentResponseLen);
+	if (err == KErrNone)
+		{
+		respPayload.Append(aMetadataPDUID);
+		respPayload.Append(EUnfragmented);   // No fragmentation needed
+		respPayload.Append(0);	// no params
+		respPayload.Append(0);	// no params
+	
+		frame->SetType(AVC::EAccepted);
+		frame->Append(respPayload);
+		
+		delete iFrame;
+		iFrame = frame;
+		respPayload.Close();
+		}
+	else
+		{
+		err = KErrAvrcpMetadataInternalError;
+		}
+	return err;	
+	}
+
+void CControlCommand::GenerateMetadataRejectPayloadL(TInt aError)
+	{
+	CAVCFrame* frame = CAVCVendorDependentResponse::NewL(KBluetoothSIGVendorId);
+	frame->SetType(AVC::ERejected);
+
+	frame->Append(iFrame->Data()[6]); // PDU ID
+	frame->Append(EUnfragmented);
+	
+	// Param length - 2 bytes
+	frame->Append(0x0);
+	frame->Append(0x1);
+
+	TUint8 avcError = 0;
+	switch (aError)
+		{
+		case KErrAvrcpMetadataInvalidCommand:
+			{
+			avcError = EInvalidCommand;
+			break;
+			}
+		case KErrAvrcpMetadataInvalidParameter:
+			{
+			avcError = EInvalidParameter;
+			break;
+			}
+		case KErrAvrcpMetadataParameterNotFound:
+			{
+			avcError = EParameterNotFound;
+			break;
+			}
+		case KErrAvrcpMetadataInternalError:
+			{
+			avcError = EInternalError;
+			break;
+			}
+		case KErrAvrcpAirInvalidCommand:
+		case KErrAvrcpAirInvalidParameter:
+		case KErrAvrcpAirParameterNotFound:
+		case KErrAvrcpAirInternalError:
+		case KErrAvrcpAirSuccess:
+		case KErrAvrcpAirUidChanged:
+		case KErrAvrcpAirReserved:
+		case KErrAvrcpAirInvalidDirection:
+		case KErrAvrcpAirNotADirectory:
+		case KErrAvrcpAirDoesNotExist:
+		case KErrAvrcpAirInvalidScope:
+		case KErrAvrcpAirRangeOutOfBounds:
+		case KErrAvrcpAirUidIsADirectory:
+		case KErrAvrcpAirMediaInUse:
+		case KErrAvrcpAirNowPlayingListFull:
+		case KErrAvrcpAirSearchNotSupported:
+		case KErrAvrcpAirSearchInProgress:
+		case KErrAvrcpAirInvalidPlayerId:
+		case KErrAvrcpAirPlayerNotBrowesable:
+		case KErrAvrcpAirPlayerNotAddressed:
+		case KErrAvrcpAirNoValidSearchResults:
+		case KErrAvrcpAirNoAvailablePlayers:
+		case KErrAvrcpAirAddressedPlayerChanged:
+			{
+			avcError = KErrAvrcpAirBase - aError;
+			break;
+			}
+		}
+	frame->Append(avcError);
+	delete iFrame;
+	iFrame = frame;
+	}
+
+/** Decode a SetAbsoluteVolume PDU ID: 0x50 and SetAbsoluteTarget call the  API 
+ */
+TInt CControlCommand::ParseSetAbsoluteVolume(TPtrC8& aMtPayload)
+	{
+	if (aMtPayload.Length() != KLengthSetAbsoluteVolumeRequestParameter)
+		{
+		return KErrAvrcpMetadataInvalidCommand;
+		}
+	
+	iOperationId = KRemConSetAbsoluteVolume;
+	iInterfaceUid = TUid::Uid(KRemConAbsoluteVolumeTargetApiUid);
+	
+	TBuf8<KAbsoluteVolumeRequestDataSize> payload;
+	TRAPD(err, DoParseSetAbsoluteVolumeL(aMtPayload, payload));
+	if (err != KErrNone)
+		{
+		return KErrAvrcpMetadataInternalError;
+		}
+	
+	return AppendIncomingPayload(payload);
+	}
+
+void CControlCommand::DoParseSetAbsoluteVolumeL(const TPtrC8& aMtPayload, TDes8& aPayload)
+	{
+	RRemConAbsoluteVolumeRequest request;
+	CleanupClosePushL(request);
+	request.iVolume = KAbsoluteVolumeMask & aMtPayload[KLengthSetAbsoluteVolumeRequestParameter - 1];
+	request.iMaxVolume = KAvrcpMaxAbsoluteVolume;
+	request.WriteL(aPayload);
+	CleanupStack::PopAndDestroy(&request);
+	}