bluetoothappprofiles/avrcp/mediabrowseapi/src/remconmediabrowsetargetbase.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 12:06:42 +0200
changeset 1 6a1fe72036e3
parent 0 f63038272f30
child 19 43824b19ee35
permissions -rw-r--r--
Revision: 201001 Kit: 201004

// Copyright (c) 2008-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/remconmediabrowsetargetbase.h>
#include <remconmediainformationtargetobserver.h>
#include <remconinterfaceselector.h>
#include <remconmediabrowsepanic.h>
#include <bluetooth/logger.h>
#include "mediabrowse.h"
#include "remconmediabrowsefault.h"
#include "remconqueuemessage.h"

const TInt KDatabaseUnawareUidCounter = 0;
#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_REMCONMEDIABROWSEAPI);
_LIT8(KLogFormat, "Operation Id = 0x%x, Data Lengh = %d");
#endif
//=========================================================================================
// Construction/Destruction
//=========================================================================================
CRemConMediaBrowseTargetBase::CRemConMediaBrowseTargetBase(CRemConInterfaceSelector& aInterfaceSelector, 
		MRemConDatabaseAwareMediaLibraryBrowseObserver& aMlObserver, 
		MRemConDatabaseAwareNowPlayingBrowseObserver& aNpObserver,
		TUint16 aMediaLibraryStateCookie)
	: CRemConInterfaceBase(TUid::Uid(KRemConMediaBrowseApiUid),
			KMaxLengthMediaBrowseMsg,
			aInterfaceSelector,
			ERemConClientTypeTarget),
	iInterfaceSelector(aInterfaceSelector),
	iRcdamlbo(&aMlObserver),
	iRcdanpbo(&aNpObserver),
	iMediaLibraryStateCookie(aMediaLibraryStateCookie),
	iAttributeIterator(iMediaAttributeIds),
	iNullIterator(iNullArray),
	iSearchInProgress(EFalse),
	iLastMlscUpdate(aMediaLibraryStateCookie)
	{
	}

CRemConMediaBrowseTargetBase::CRemConMediaBrowseTargetBase(CRemConInterfaceSelector& aInterfaceSelector, 
		MRemConDatabaseUnawareMediaLibraryBrowseObserver& aMlObserver, 
		MRemConDatabaseUnawareNowPlayingBrowseObserver& aNpObserver)
	: CRemConInterfaceBase(TUid::Uid(KRemConMediaBrowseApiUid),
			KMaxLengthMediaBrowseMsg,
			aInterfaceSelector,
			ERemConClientTypeTarget),
	iInterfaceSelector(aInterfaceSelector),
	iRcdumlbo(&aMlObserver),
	iRcdunpbo(&aNpObserver),
	iAttributeIterator(iMediaAttributeIds),
	iNullIterator(iNullArray),
	iSearchInProgress(EFalse)
	{
	}

CRemConMediaBrowseTargetBase::~CRemConMediaBrowseTargetBase()
	{
	iMediaAttributeIds.Close();
	iNullArray.Close();
	iOutBuf.Close();
	iSearchString.Close();
	iGetPathResponse->Close();
	iGiaResponse->Close();
	iGflResponse->Close();
	delete iGetPathResponse;
	delete iGiaResponse;
	delete iGflResponse;
	
	if (iNextMessageCallBack)
		{
		iNextMessageCallBack->Cancel();
		delete iNextMessageCallBack;
		}
	if (iNextItemCallBack)
		{
		iNextItemCallBack->Cancel();
		delete iNextItemCallBack;
		}
	
	iMsgQueue->Reset();
	delete iMsgQueue;
	}

void CRemConMediaBrowseTargetBase::BaseConstructL(TBool aSearchSupported)
	{
	iMsgQueue = new(ELeave)TRemConMessageQueue();

	iGetPathResponse = new(ELeave)RRemConGetPathResponse();
	iGiaResponse = new(ELeave)RRemConGetItemAttributesResponse();
	iGflResponse = new(ELeave)RRemConGetFolderItemsResponse();

	TCallBack cb(&NextMessageCb, this);
	iNextMessageCallBack = new(ELeave)CAsyncCallBack(cb, CActive::EPriorityStandard);
	
	TCallBack itemCallBack(&NextItemCallBack, this);
	iNextItemCallBack = new(ELeave)CAsyncCallBack(itemCallBack, CActive::EPriorityStandard);
	
	RRemConInterfaceFeatures features;
	User::LeaveIfError(features.Open());
	CleanupClosePushL(features);

	if(aSearchSupported)
		{
		features.AddOperationL(ESearchOperationId);
		iSearchSupported = ETrue;
		}
	
	if(DatabaseAware())
		{
		features.AddOperationL(EUIDPersistency);
		}
	
	//Mandate the following operationIds to be supported in the client
	features.AddOperationL(EGetFolderItemsOperationId);
	features.AddOperationL(EChangePathOperationId);
	features.AddOperationL(EGetItemAttributesOperationId);
	features.AddOperationL(ESetBrowsedPlayerOperationId);
	
	iOutBuf.CreateL(KMediaBrowseOutBufMaxLength);
	
	CRemConInterfaceBase::BaseConstructL(features, ETrue); // it's true, this interface is a bulk interface
	CleanupStack::PopAndDestroy(&features);
	}
	
//=========================================================================================
// RemCon interface stuff, called from interface selector
//=========================================================================================

/** 
@internalComponent
@released

Gets a pointer to a specific interface version.

@return A pointer to the interface, NULL if not supported.
*/
TAny* CRemConMediaBrowseTargetBase::GetInterfaceIf(TUid aUid)
	{
	TAny* ret = NULL;
	if ( aUid == TUid::Uid(KRemConInterfaceIf1) )
		{
		ret = reinterpret_cast<TAny*>(
			static_cast<MRemConInterfaceIf*>(this)
			);
		}

	return ret;
	}

void CRemConMediaBrowseTargetBase::MrcibNewMessage(TUint aOperationId,
			const TDesC8& aData)
	{
	LOG_FUNC
	LOG2(KLogFormat, aOperationId, aData.Length());

	TMetadataTransferPDU currentOp = RAvrcpIPC::GetPDUIdFromIPCOperationId(aOperationId);
	switch(currentOp)
		{
	case EGetFolderItemsOperationId:
		AddToOperationQueue(EMbGetFolderItems, 
							EGetFolderItemsOperationId, aData);
		if (!iMsgQueue->IsEmpty() && !iInProgress && !iMsgQueue->Find(
			      TUid::Uid(KRemConMediaBrowseApiUid)
				 ,EGetFolderItemsOperationId))
			{
			iNextMessageCallBack->CallBack();
			}
		break;
	case EChangePathOperationId:
		ProcessChangePath(aData);
		break;
	case EGetItemAttributesOperationId:
		{
		AddToOperationQueue(EMbGetItemAttributes, 
							EGetItemAttributesOperationId, aData);
		if (!iMsgQueue->IsEmpty() && !iInProgress && !iMsgQueue->Find(
					      TUid::Uid(KRemConMediaBrowseApiUid)
						 ,EGetItemAttributesOperationId))
					{
					iNextMessageCallBack->CallBack();
					}
		break;
		}
	case ESearchOperationId:
		ProcessSearch(aData);
		break;
	case ESetBrowsedPlayerOperationId:
		ProcessGetPath(aData);
		break;	
	case EMediaLibraryStateCookieUpdateOperationId:
		ProcessMediaLibraryStateCookieUpdate(aData);
		break;
	default:
		__ASSERT_DEBUG(EFalse, MediaBrowseFault::Fault(EUnexpectedOperationId));
		break; 
		};
	}

//=========================================================================================
// Browse Interface functions, called from derived classes
//=========================================================================================
void CRemConMediaBrowseTargetBase::DoFolderListing(const TArray<TRemConItem>& aFolderListing, 
		TUint16 aMediaLibraryStateCookie, 
		TInt aResult)
	{
	if (aResult != KErrNone)
		{
		SendGetFolderItemsResponse(aResult, KNullDesC8);
		return;
		}	
	
	// If asserted here, means the client calls the interface FolderListing() 
	// more than once corresponding to the only once call 
	// MrcmbtoGetFolderListing()
	__ASSERT_DEBUG(iGflResponse->iItems.Count() == 0, MediaBrowsePanic::Panic(EFolderListingProvidedTwice));
	
	// Store clients state cookie to pass it back when requesting each item. 
	// This will ensure that we don't miss state change during the course of 
	// the non-atomic GetFolderListing operation
	iGflResponse->iUidCounter = aMediaLibraryStateCookie;
	
	// Store these UIDs, then ask for info about them
	if (iGflResponse->CopyItems(aFolderListing) != KErrNone)
		{
		SendGetFolderItemsResponse(KErrAvrcpAirInternalError, KNullDesC8);
		return;
		}
	
	iGflResponse->iCurrentListingSize = KGetFolderItemsResponseBaseSize;
	iGflResponse->iCurrentItem = -1;
	RequestNextItem();
	}

void CRemConMediaBrowseTargetBase::DoFolderUpResult(TUint aItemCount, TInt aResult)
	{
	SendChangePathResponse(aItemCount, aResult);
	}

void CRemConMediaBrowseTargetBase::DoFolderDownResult(TUint aItemCount, TInt aResult)
	{
	SendChangePathResponse(aItemCount, aResult);
	}

void CRemConMediaBrowseTargetBase::DoGetPathResult(TUint aItemCount, 
		TUint16 aMediaLibraryStateCookie, 
		TInt aResult)
	{
	if (aResult != KErrNone)
		{
		iGetPathResponse->Close();
		return SendError(EMbSetBrowsedPlayer,
				ESetBrowsedPlayerOperationId, aResult);
		}
	
	// Store the current UIDs counter.
	iMediaLibraryStateCookie = aMediaLibraryStateCookie;
	
	TInt status = KErrAvrcpAirBase - KErrAvrcpAirSuccess;
	iGetPathResponse->iStatus = status;
	iGetPathResponse->iUidCounter = aMediaLibraryStateCookie;
	iGetPathResponse->iNumberItems = aItemCount;
	
	RBuf8 responseBuf;
	TInt error = responseBuf.Create(iGetPathResponse->Size());
	if (error != KErrNone)
		{
		iGetPathResponse->Close();
		return SendError(EMbSetBrowsedPlayer, 
				ESetBrowsedPlayerOperationId, KErrAvrcpAirInternalError);
		}
	
	iGetPathResponse->iPduId = AvrcpBrowsing::ESetBrowsedPlayer;
	TRAP(error, iGetPathResponse->WriteL(responseBuf));
	if (error == KErrNone)
		{
		// Send the response back to the CT
		error = InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				ESetBrowsedPlayerOperationId, responseBuf );
		}
	
	iGetPathResponse->Close();
	responseBuf.Close();
	}

void CRemConMediaBrowseTargetBase::DoSearchResult(TUint aNumberItemsFound,
		TUint16 aMediaLibraryStateCookie, 
		TInt aResult)
	{
	// GetFoldItems should in progress When this interface is called.
	__ASSERT_DEBUG(iSearchInProgress, MediaBrowsePanic::Panic(ESearchResultWithoutRequest));

	SendSearchResponse(aResult, aNumberItemsFound, aMediaLibraryStateCookie);
	}

void CRemConMediaBrowseTargetBase::DoMediaLibraryStateChange(TUint16 aMediaLibraryStateCookie)
	{
	if(DatabaseAware())
		{
		__ASSERT_DEBUG(aMediaLibraryStateCookie != KDatabaseUnawareUidCounter, MediaBrowsePanic::Panic(EZeroMediaLibraryStateCookie));
		
		// For database aware players we need to update if we have a pending update and
		// the new value is different to what we last 
		if(iMlscUpdatePending && (aMediaLibraryStateCookie != iLastMlscUpdate))
			{
			// Send update with new value
			SendMediaLibraryStateCookieUpdateResponse(aMediaLibraryStateCookie);
			}	
		
		// Always store the last value here.  When we are asked for an update
		// we will be provided with the value the update should be relative 
		// to, so we will compare to this. 
		iLastMlscUpdate = aMediaLibraryStateCookie;
		}
	else
		{
		__ASSERT_DEBUG(aMediaLibraryStateCookie == KDatabaseUnawareUidCounter, MediaBrowseFault::Fault(ENonZeroMediaLibraryStateCookie));
		
		if(iMlscUpdatePending)
			{
			SendMediaLibraryStateCookieUpdateResponse(aMediaLibraryStateCookie);
			}
		else
			{
			// For database aware clients the value can never change.  Indicate that
			// the client has informed us of the state change by incrementing our
			// update value so that we know there's been a change.  The value doesn't
			// matter, we just have to make it something other than zero.
			iLastMlscUpdate = 1;
			}
		}
	}

void CRemConMediaBrowseTargetBase::DoFolderItemResult(const TRemConItemUid& aFolderID,
		const TDesC8& aFolderName, 
		TFolderItemType aFolderType, 
		TFolderItemPlayable aPlayable,
		const TArray<TMediaElementAttribute>& aAttributes,
		TInt aResult)
	{
	// GetFolderItems should in progress When this interface is called.
	__ASSERT_DEBUG(iGetFolderListing || iGetItemAttributes, MediaBrowsePanic::Panic(EFolderItemResultWithoutRequest));

	if(iGetFolderListing)
		{
		ProcessFolderItemResult(aFolderID, aFolderName, aFolderType, aPlayable, aResult);
		}
	
	if (iGetItemAttributes)
		{
		ProcessGetItemAttributesResult(aAttributes, aResult);
		}
	}

void CRemConMediaBrowseTargetBase::DoMediaElementItemResult(const TRemConItemUid& aMediaID, 
		const TDesC8& aMediaName, 
		TMediaItemType aMediaType, 
		const TArray<TMediaElementAttribute>& aAttributes,
		TInt aResult)
	{
	__ASSERT_DEBUG((iGetFolderListing || iGetItemAttributes), MediaBrowsePanic::Panic(EMediaElementItemResultWithoutRequest));

	if (iGetFolderListing)
		{
		ProcessMediaElementItemResult(aMediaID, aMediaName, aMediaType, aAttributes, aResult);
		}
	
	if (iGetItemAttributes)
		{
		ProcessGetItemAttributesResult(aAttributes, aResult);
		}		
	}

//=========================================================================================
// Utility functions, called internally
//=========================================================================================

void CRemConMediaBrowseTargetBase::ProcessMediaLibraryStateCookieUpdate(const TDesC8& aData)
	{
	// Try to read the incoming request
	RRemConUidsChangedRequest request;
	TRAPD(error, request.ReadL(aData));
	__ASSERT_DEBUG(error == KErrNone, MediaBrowseFault::Fault(EBadlyFormattedMediaLibraryStateCookieUpdate));
	static_cast<void>(error == error); // stops compiler warnings (assert above indicates design contract).

	if(request.iInitialUidCounter != iLastMlscUpdate)
		{
		// The client has updated the uid counter since the bearer
		// last asked.  Tell it the new value.  The Send..Response 
		// function deals with all the state management necessary,
		// including whether we are database aware or unaware
		SendMediaLibraryStateCookieUpdateResponse(iLastMlscUpdate);
		}
	else
		{
		// Bearer still up to date.  Remember that it's waiting for
		// an update.
		iMlscUpdatePending = ETrue;
		}
	}

void CRemConMediaBrowseTargetBase::ProcessGetItemAttributes(const TDesC8& aData)
	{
	TRemConFolderScope scope;
	TRemConItemUid item;
	TUint16 uidCounter;
	TInt err = ParseGetItemAttributesRequest(aData, scope, item, uidCounter);
	if (err != KErrNone)
	    {
	    SendGetItemAttributesResponse(err, KNullDesC8);
	    return;
	    }
	
	iAttributeIterator.Start();	
	iInProgress = ETrue;
	iGetItemAttributes = ETrue;	
	TInt result = KErrNone;
	if(scope == ENowPlayingFolder)
		{
		if (!DatabaseAware() && (0 == uidCounter))//Database UnAware
			{
			result = iRcdunpbo->MrcdunpboGetItem(item, iAttributeIterator);
			}
		else if (DatabaseAware() && (uidCounter > 0))
			{
			result = iRcdanpbo->MrcdanpboGetItem(item, iAttributeIterator, uidCounter);
			}
		else
			{
			result = KErrAvrcpAirInvalidParameter;
			}
		}
	else
		{
		if (!DatabaseAware() && (0 == uidCounter))//Database UnAware
			{
			result = iRcdumlbo->MrcdumlboGetItem(scope, item, iAttributeIterator);
			}
		else if (DatabaseAware() && (uidCounter > 0))
			{
			result = iRcdamlbo->MrcdamlboGetItem(scope, item, iAttributeIterator, 
					                     uidCounter);
			}
		else
			{
			result = KErrAvrcpAirInvalidParameter;
			}
		}
	
	// The call back function returns error.
	if (result != KErrNone)
		{
		SendGetItemAttributesResponse(result, KNullDesC8);
		}
	}

void CRemConMediaBrowseTargetBase::SendGetItemAttributesResponse(TInt aResult, const TDesC8& aData)
	{
	if(aResult != KErrNone)
		{
		SendError(EMbGetItemAttributes, EGetItemAttributesOperationId, aResult);
		}
	else
		{
		InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				EGetItemAttributesOperationId, aData);
		}
	
	iInProgress = EFalse;
	iGetItemAttributes = EFalse;
	iMediaAttributeIds.Reset();
	iGiaResponse->Close();
	
	if (!iMsgQueue->IsEmpty())
		{
		iNextMessageCallBack->CallBack();
		}
	}

void CRemConMediaBrowseTargetBase::ProcessChangePath(const TDesC8& aData)
	{
	// Try to read the incoming request
	TInt error = KErrNone;
	RRemConChangePathRequest request;
	TRAP(error, request.ReadL(aData));

	// Couldn't parse the request; tell them it was invalid
	if (error != KErrNone)
		{
	    SendError(EMbChangePath, EChangePathOperationId, KErrAvrcpAirInvalidParameter);
	    return;
		}

	if(request.iDirection == AvrcpBrowsing::KUp)
		{
		if (DatabaseAware() && (request.iUidCounter > 0))
			{
			iRcdamlbo->MrcdamlboFolderUp(request.iUidCounter);
			}
		else if (!DatabaseAware() && (0 == request.iUidCounter))
			{
			iRcdumlbo->MrcdumlboFolderUp();
			}
		else
			{
			SendError(EMbChangePath, EChangePathOperationId, KErrAvrcpAirInvalidParameter);
			}
		}
	else if(request.iDirection == AvrcpBrowsing::KDown)
		{
		if (DatabaseAware() && (request.iUidCounter > 0))
			{
			iRcdamlbo->MrcdamlboFolderDown(request.iElement, request.iUidCounter);
			}
		else if (!DatabaseAware() && (0 == request.iUidCounter))
			{
			iRcdumlbo->MrcdumlboFolderDown(request.iElement);
			}
		else
			{
			SendError(EMbChangePath, EChangePathOperationId, KErrAvrcpAirInvalidParameter);
			}
		}
	else
		{
		SendError(EMbChangePath, EChangePathOperationId, KErrAvrcpAirInvalidDirection);
		}
	}

void CRemConMediaBrowseTargetBase::SendSearchResponse(TInt aResult, TUint aNumberItemsFound, TUint16 aMediaLibraryStateCookie)
	{	
	iSearchString.Close();
	
	// format the response in a RRemConSearchResponse
	RRemConMediaErrorResponse errResponse;
	RRemConSearchResponse response;
	TInt symbianError = errResponse.SymbianErrorCheck(aResult);
	
	response.iStatus = errResponse.SymbianErrToStatus(symbianError);	
	response.iPduId = AvrcpBrowsing::ESearch;//0x80
	response.iUidCounter = aMediaLibraryStateCookie;
	response.iNumberItems = aNumberItemsFound;
	TRAPD(error, response.WriteL(iOutBuf));
	if (error == KErrNone)
		{
		// send the response back to the CT
		error = InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				ESearchOperationId,  iOutBuf );
		}
	
	//Search operatin complete.
	iSearchInProgress = EFalse;
	}

void CRemConMediaBrowseTargetBase::ProcessSearch(const TDesC8& aData)
	{
	// Don't trouble the client with this if they've informed us they don't
	// support it
	if (!iSearchSupported)
		{
		return SendSearchResponse(KErrAvrcpAirSearchNotSupported, 0, 0);
		}
	
	// We know a search operation is in progress
	if (iSearchInProgress)
		{
		return SendSearchResponse(KErrAvrcpAirSearchInProgress, 0, 0);
		}
		
	// Try to read the incoming request
	TInt error = KErrNone;
	RRemConSearchRequest request;
	TRAP(error, request.ReadL(aData));

	// Couldn't parse the request; tell them it was invalid
	if (error != KErrNone)
		{
		error = (error == KErrNoMemory) ? 
				KErrAvrcpAirInternalError : KErrAvrcpAirInvalidParameter;
		
		return SendSearchResponse(error, 0, 0);
		}

	// Check the character set
	if (request.iCharset != KUtf8MibEnum)
		{
		iSearchInProgress = EFalse;
		return SendSearchResponse(KErrAvrcpAirInvalidParameter, 0, 0);
		}
	
	iSearchString.Close();
	iSearchString.Assign(request.iSearchString);
	//iSearchString has taken ownership of request's search string.
	request.iSearchString.Assign(NULL);
	
	iSearchInProgress = ETrue;
	
	if(DatabaseAware())
		{
		iRcdamlbo->MrcdamlboSearch(iSearchString);
		}
	else
		{
		iRcdumlbo->MrcdumlboSearch(iSearchString);
		}
	
	request.Close();
	}

void CRemConMediaBrowseTargetBase::ProcessGetPath(const TDesC8& aData)
	{
	iGflResponse->iMaxResponse = *(reinterpret_cast<const TInt*>(aData.Ptr()));

	if(DatabaseAware())
		{
		iRcdamlbo->MrcdamlboGetPath(iGetPathResponse->iPath);
		}
	else
		{
		iRcdumlbo->MrcdumlboGetPath(iGetPathResponse->iPath);
		}
	}

void CRemConMediaBrowseTargetBase::ProcessGetFolderItems(const TDesC8& aData)
	{
	// The bearer is responsible for ensuring we have been supplied with response
	// max size before sending us any requests
	__ASSERT_DEBUG(iGflResponse->iMaxResponse != 0, MediaBrowseFault::Fault(ERequestWithoutMaxResponseBeingSet));
	
	iInProgress = ETrue;
	iGetFolderListing = ETrue;
	
	RRemConGetFolderItemsRequest request;
	TRAPD(err, request.ReadL(aData));
	if(err != KErrNone)
		{
		request.Close();
		SendGetFolderItemsResponse(KErrAvrcpAirInvalidParameter, KNullDesC8);
		return;
		}
	
	if(request.iScope == AvrcpBrowsing::KSearchScope && !iSearchSupported)
		{
		request.Close();
		SendGetFolderItemsResponse(KErrAvrcpAirSearchNotSupported, KNullDesC8);
		return;
		}
	
	if (request.CopyAttributes(iMediaAttributeIds) != KErrNone)
		{
		request.Close();
		SendGetFolderItemsResponse(KErrAvrcpAirInternalError, KNullDesC8);
		return;
		}
	iAttributeIterator.Start();

	if(request.iScope == AvrcpBrowsing::KVirtualFilesystemScope)
		{
		iScope = EBrowseFolder;
		}
	else if(request.iScope == AvrcpBrowsing::KNowPlayingScope)
		{
		iScope = ENowPlayingFolder;
		}
	else if(request.iScope == AvrcpBrowsing::KSearchScope)
		{
		iScope = ESearchResultFolder;
		}
	else
		{
		request.Close();
		SendGetFolderItemsResponse(KErrAvrcpAirInvalidScope, KNullDesC8);
		return;
		}
	
	if (request.iStartItem > request.iEndItem)
		{
		request.Close();
		SendGetFolderItemsResponse(KErrAvrcpAirRangeOutOfBounds, KNullDesC8);
		return;
		}
	
	if(iScope == ENowPlayingFolder)
		{
		if(DatabaseAware())
			{
			iRcdanpbo->MrcdanpboGetFolderListing(request.iStartItem, request.iEndItem);
			}
		else
			{
			iRcdunpbo->MrcdunpboGetFolderListing(request.iStartItem, request.iEndItem);
			}
		}
	else
		{
		if(DatabaseAware())
			{
			iRcdamlbo->MrcdamlboGetFolderListing(iScope,request.iStartItem, request.iEndItem);
			}
		else
			{
			iRcdumlbo->MrcdumlboGetFolderListing(iScope,request.iStartItem, request.iEndItem);
			}
		}
	
	request.Close();
	}

void CRemConMediaBrowseTargetBase::SendGetFolderItemsResponse(TInt aResult, const TDesC8& aData)
	{
	if(aResult != KErrNone)
		{
		SendError(EMbGetFolderItems, EGetFolderItemsOperationId, aResult);
		}
	else
		{
		InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				EGetFolderItemsOperationId, aData);
		}
	
	iInProgress = EFalse;
	iGetFolderListing = EFalse;
	iMediaAttributeIds.Reset();
	iGflResponse->Close();
	
	iNextItemCallBack->Cancel();
	
	if (!iMsgQueue->IsEmpty())
		{
		iNextMessageCallBack->CallBack();
		}
	}

TInt CRemConMediaBrowseTargetBase::NextMessageCb(TAny* aThis)
	{
	static_cast<CRemConMediaBrowseTargetBase*>(aThis)->DoNextMessage();
	return KErrNone;
	}

void CRemConMediaBrowseTargetBase::DoNextMessage()
	{
	__ASSERT_DEBUG(!iMsgQueue->IsEmpty(), MediaBrowseFault::Fault(EUnexpectedNextMessageCallback));
	CRemConQueuedMessage* msg = iMsgQueue->First();
	iMsgQueue->Remove(*msg);
	
	switch (msg->iOperationId)
		{
		case EGetFolderItemsOperationId:
			ProcessGetFolderItems(msg->Data());
			break;
		case EGetItemAttributesOperationId:
			ProcessGetItemAttributes(msg->Data());
			break;
		default:
			__ASSERT_DEBUG(EFalse, MediaBrowseFault::Fault(EUnexpectedNextMessageCallback));
			break;
		}
	delete msg;
	}

void CRemConMediaBrowseTargetBase::SendMediaLibraryStateCookieUpdateResponse(TUint16 aMediaLibraryStateCookie)
	{
	LOG_FUNC
	
	TUint16 newValue = DatabaseAware() ? aMediaLibraryStateCookie : KDatabaseUnawareUidCounter;

	TInt error = KErrNone;
	RRemConUidsChangedResponse response;
	response.iUidCounter = newValue;
	TRAP(error, response.WriteL(iOutBuf));

	if (error == KErrNone)
		{
		// send the response back to the CT
		error = InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				EMediaLibraryStateCookieUpdateOperationId, iOutBuf);
		
		iLastMlscUpdate = newValue;
		iMlscUpdatePending = EFalse;
		}
	// otherwise we couldn't update the client.  Leave our state with the update
	// pending then we'll try again next time the client tells us state has 
	// changed.  
	}

void CRemConMediaBrowseTargetBase::SendChangePathResponse(TUint aItemCount, 
		TInt aResult)
	{
	if (aResult != KErrNone)
		{
		return SendError(EMbChangePath, EChangePathOperationId, aResult);
		}
	
	TInt error = KErrNone;

	// Format the response in a RRemConChangePathResponse
	RRemConChangePathResponse response;
	response.iStatus = KErrAvrcpAirBase - KErrAvrcpAirSuccess;//0x4
	response.iNumberItems = aItemCount;
	
	RBuf8 responseBuf;
	error = responseBuf.Create(KMediaBrowseOutBufMaxLength);
	if (error != KErrNone)
		{
		responseBuf.Close();
		SendError(EMbChangePath, EChangePathOperationId, KErrAvrcpAirInternalError);
		return;
		}
	
	response.iPduId = AvrcpBrowsing::EChangePath;
	TRAP(error, response.WriteL(responseBuf));
	if (error == KErrNone)
		{
		// send the response back to the CT
		error = InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				EChangePathOperationId, responseBuf );
		}
	
	responseBuf.Close();
	}

TInt CRemConMediaBrowseTargetBase::NextItemCallBack(TAny* aThis)
	{
	static_cast<CRemConMediaBrowseTargetBase*>(aThis)->RequestNextItem();
	return KErrNone;
	}

void CRemConMediaBrowseTargetBase::RequestNextItem()
	{
	TInt err = KErrNone;
	TInt result = KErrNone;
	RBuf8 responseBuf;
	
	// If true, indicate that we have not got all the items requested, 
	// so going on. 
	// There are some array elements accessed by [] as follows without 
	// checking range for it is done in RequestNextItem function.
	if ( iGflResponse->RequestNextItem(err, responseBuf, 
			iGflResponse->iUidCounter) )
		{
		TBool folderItem = EFalse;
		if (AvrcpBrowsing::EFolderItem == iGflResponse->iItems[iGflResponse->iCurrentItem].iType)
			{
			folderItem = ETrue;
			}
		iAttributeIterator.Start();	
		if(iScope == ENowPlayingFolder)
			{
			if(DatabaseAware())
				{
				result = iRcdanpbo->MrcdanpboGetItem(
			    		iGflResponse->iItems[iGflResponse->iCurrentItem].iUid,
			    		folderItem ? iNullIterator : iAttributeIterator,
			    		iGflResponse->iUidCounter);
				}
			else
				{
				result = iRcdunpbo->MrcdunpboGetItem( 
			    		iGflResponse->iItems[iGflResponse->iCurrentItem].iUid,
			    		folderItem ? iNullIterator : iAttributeIterator);
				}
			}
		else
			{
			if(DatabaseAware())
				{
				result = iRcdamlbo->MrcdamlboGetItem(iScope,
			    		iGflResponse->iItems[iGflResponse->iCurrentItem].iUid,
			    		folderItem ? iNullIterator : iAttributeIterator,
			    		iGflResponse->iUidCounter);
				}
			else
				{
				result = iRcdumlbo->MrcdumlboGetItem(iScope, 
			    		iGflResponse->iItems[iGflResponse->iCurrentItem].iUid,
			    		folderItem ? iNullIterator : iAttributeIterator);
				}
			}
		
		// The call back function reutrns error.
		if (result != KErrNone)
			{
			SendGetFolderItemsResponse(result, KNullDesC8);
			}
		}
	// If comes here, indicate that we stop requesting the next item
	// which means two possibilities:
	// 1. Success: Have got all the items we want.
	// 2. Error: Error occured internally.
	else if ( err == KErrNone ) //Possibility 1.
		{
		SendGetFolderItemsResponse(KErrNone, responseBuf);
		}
	else // Possibility 2.
		{
		SendGetFolderItemsResponse(KErrAvrcpAirInternalError, KNullDesC8);
		}
	responseBuf.Close();
	}

void CRemConMediaBrowseTargetBase::DoItemComplete(TInt aResult)
	{
	if (aResult != KErrNone)
		{
		SendGetFolderItemsResponse(aResult, KNullDesC8);
		}
	else
		{
		// We have to put an async break in here - otherwise if large
		// numbers of items are requested we could overflow the stack
		iNextItemCallBack->CallBack();
		}
	}

void CRemConMediaBrowseTargetBase::SendError(TUint8 aPduId, 
		TUint aOperationId, 
		TInt aError)
	{
	TInt error = KErrNone;
	RRemConMediaErrorResponse response;
	response.iPduId = aPduId;
	response.iStatus = RAvrcpIPC::SymbianErrToStatus(aError);
	TRAP(error, response.WriteL(iOutBuf));
	if (error == KErrNone)
		{
		InterfaceSelector().SendBulkUnreliable(
				TUid::Uid(KRemConMediaBrowseApiUid),
				aOperationId, iOutBuf);
		}
	}

/** 
Sets an attribute value for the requested item.
*/
TInt CRemConMediaBrowseTargetBase::DoAttributeValue(
		TMediaAttributeId aAttributeId, 
		const TDesC8& aAttributeData )
	{
	iSetAttributeValue = ETrue;
	
	REAResponse resp;
	resp.iAttributeId = aAttributeId;
	resp.iCharset = KUtf8MibEnum;
	resp.iStringLen = aAttributeData.Length();
	resp.iString = aAttributeData.Alloc();
	if (!resp.iString)
		{
		iSetAttributeValue = EFalse;
		return KErrNoMemory;
		}
	TInt status = KErrNone;
	if(iGiaResponse->Size() + resp.iStringLen < iGflResponse->iMaxResponse)
		{
		status = iGiaResponse->iAttributes.Append(resp);
		if (status != KErrNone)
			{
			iSetAttributeValue = EFalse;
			resp.Close();  // make sure heap string is de-allocated
			}
		}
	
	return status;
	}

/**
Signals that all attributes requested has been supplied.
*/
 void CRemConMediaBrowseTargetBase::DoAllAttributesCompleted(TInt aResult)
	{
	TInt error = KErrNone;
	__ASSERT_DEBUG(((aResult != KErrNone) || ((aResult == KErrNone) && iSetAttributeValue)),
			MediaBrowseFault::Fault(EResultErrorCodeMismatch));
	
	if (aResult == KErrNone)
		{
		// Finalise response; update number of attributes returned
		iGiaResponse->iNumberAttributes = iGiaResponse->iAttributes.Count();
		}

	// Allocate a buffer for the formatted message
	RBuf8 messageBuffer;	
	TInt bufferSize = 
	(aResult == KErrNone) ? iGiaResponse->Size() : KBrowseResponseBaseLength;
	
	if ( messageBuffer.Create(bufferSize) != KErrNone )
		{
		SendGetItemAttributesResponse(KErrAvrcpAirInternalError, KNullDesC8);
		}
	else
		{
		// Send the result back to the CT
		iGiaResponse->iPduId = AvrcpBrowsing::EGetItemAttributes; // 0x73
		iGiaResponse->iStatus = RAvrcpIPC::SymbianErrToStatus(aResult);
		
		TRAP(error, iGiaResponse->WriteL(messageBuffer));
		if (error == KErrNone)
			{
			SendGetItemAttributesResponse(KErrNone, messageBuffer);
			}
		else
			{
			SendGetItemAttributesResponse(KErrAvrcpAirInternalError, KNullDesC8);
			}
		}
	messageBuffer.Close();
	}

TInt CRemConMediaBrowseTargetBase::ItemAttributesResult(
		const TArray<TMediaElementAttribute>& aAttributes)
	{
	TInt error = KErrNone;
	TMediaAttributeId attributeId;
	for (TInt i = 0; i < aAttributes.Count(); i++)
		{
		// check that the values supplied were requested
		attributeId = aAttributes[i].iAttributeId;
		if ( KErrNotFound == iMediaAttributeIds.Find(attributeId) )
			{
			//Omit the invalid ones
			continue;
			}
		
		error = DoAttributeValue(attributeId, *aAttributes[i].iString);
		if (error != KErrNone)
			{
			break;
			}
		}
	
	return error;
	}

void CRemConMediaBrowseTargetBase::AddToOperationQueue(TUint8 aPduId,
		TInt aOperationId, 
		const TDesC8& aData)
	{
	CRemConQueuedMessage* msg = NULL;
	TRAPD(err, msg = CRemConQueuedMessage::NewL(
			TUid::Uid(KRemConMediaBrowseApiUid), aData, aOperationId));			
	if (err == KErrNone)
		{
		iMsgQueue->AddLast(*msg);
		}
	else
		{
		SendError(aPduId, aOperationId, KErrAvrcpAirInternalError);
		}
	}

TInt CRemConMediaBrowseTargetBase::ParseGetItemAttributesRequest(
		const TDesC8& aData,
		TRemConFolderScope& aScope,
		TRemConItemUid& aItemUid,
		TUint16& aMediaLibraryStateCookie)
	{
	// Try to read the incoming request
	TInt error = KErrNone;
	RRemConGetItemAttributesRequest request;
	TRAP(error, request.ReadL(aData));

	// Couldn't parse the request;tell them it was invalid
	// Specification says unique id must not be 0x0
	if (error != KErrNone || request.iElement == 0)
		{
		request.Close();
		return KErrAvrcpAirInvalidParameter;
		}

	if (request.iNumberAttributes == 0)
		{
		// spec says this is a request for all attribs
		// current spec has 7 specified (0x01 to 0x07)
		for (TInt i = 1; i <= KMediaAttributeNum; i++)
			{
			if (iMediaAttributeIds.Append(static_cast<TMediaAttributeId>(i))
					!= KErrNone)
				{
				request.Close();
				return KErrAvrcpAirInternalError;
				}
			}
		}
	else
		{
		// No need to check 
		// request.iNumberAttributes == request.iAttributes.Count()
		// as this must be correct or request.ReadL(aData) leaves
		TUint8 value;
		TMediaAttributeId attributeid;
		for (TInt i = 0; i < request.iNumberAttributes; i++)
			{
			value = request.iAttributes[i];
			if (value > 0 && value <= KMaxMediaAttributeValue )
				{
				attributeid = static_cast<TMediaAttributeId>(value);
				if (iMediaAttributeIds.Append(attributeid) != KErrNone)
					{
					request.Close();
					return KErrAvrcpAirInternalError;
					}
				}
			}
		}
	// Check that some valid attribute ids have been found
	if (iMediaAttributeIds.Count() == 0)
		{
		request.Close();
		return KErrAvrcpAirInvalidParameter;
		}
		
	if(request.iScope == AvrcpBrowsing::KSearchScope)
		{
		aScope = ESearchResultFolder;
		}
	else if(request.iScope == AvrcpBrowsing::KVirtualFilesystemScope)
		{
		aScope = EBrowseFolder;
		}
	else if (request.iScope == AvrcpBrowsing::KNowPlayingScope)
		{
		aScope = ENowPlayingFolder;
		}
	else
		{
		request.Close();
		return KErrAvrcpAirInvalidScope;
		}
	
	aItemUid = request.iElement;
	aMediaLibraryStateCookie = request.iUidCounter;
	
	request.Close();
	return KErrNone;
	}

void CRemConMediaBrowseTargetBase::ProcessMediaElementItemResult(
		const TRemConItemUid& aMediaID, 
		const TDesC8& aMediaName,
		TMediaItemType aMediaType, 
		const TArray<TMediaElementAttribute>& aAttributes,
		TInt aResult)
	{
	TInt internalError = KErrNone;
	if (aResult == KErrNone)
		{
		__ASSERT_DEBUG(aMediaType < AvrcpBrowsing::KMediaTypeReserved, MediaBrowsePanic::Panic(EInvalidMediaType));

		RItem& item = iGflResponse->iItems[iGflResponse->iCurrentItem];
		if (item.iUid == aMediaID)
			{
			item.iType = AvrcpBrowsing::EMediaElement;
			item.iCharset = KUtf8MibEnum;
			
			item.iName = aMediaName.Alloc();		
			internalError = (!item.iName) ? KErrNoMemory : internalError;
			
			if (internalError == KErrNone)
				{
				item.iNameLength = aMediaName.Length();
				item.iMediaType = static_cast<AvrcpBrowsing::TFolderType>(aMediaType);
				item.iNumberAttributes = aAttributes.Count();
				item.iLength = KMediaElementItemBaseLength + item.iNameLength;
				TInt attributeCount = aAttributes.Count();
				REAResponse attribute;
				for (TInt i = 0; i < attributeCount; i++)
					{
					// Check that the values supplied were requested
					if (KErrNotFound == iMediaAttributeIds.Find(aAttributes[i].iAttributeId))
						{
						//Omit the invalid ones
						continue;
						}
					
					attribute.iAttributeId = aAttributes[i].iAttributeId;				
					attribute.iString = aAttributes[i].iString->Alloc();
					internalError = (!attribute.iString) ? KErrNoMemory : internalError;
					if (internalError == KErrNone)
						{
						attribute.iCharset = KUtf8MibEnum;
						attribute.iStringLen = attribute.iString->Length();
						item.iAttributes.Append(attribute);
						
						item.iLength += 
						KAttributeBaseLength + attribute.iString->Length();
						}
					else
						{
						//it's useless to continue as there is an allocation issue
						break;
						}
					}
				}
			}
		else
			{
			internalError = KErrArgument;
			}
		}
	
	aResult = 
	(KErrNone == internalError)? aResult : KErrAvrcpAirInternalError;
	
	DoItemComplete(aResult);
	}

void CRemConMediaBrowseTargetBase::ProcessFolderItemResult(
		const TRemConItemUid& aFolderID,
		const TDesC8& aFolderName, 
		TFolderItemType aFolderType, 
		TFolderItemPlayable aPlayable,
		TInt aResult)
	{
	TInt internalError = KErrNone;
	if (aResult == KErrNone)
	    {
		__ASSERT_DEBUG(aFolderType < AvrcpBrowsing::EFolderTypeReserved, MediaBrowsePanic::Panic(EInvalidFolderType));
		__ASSERT_DEBUG(aPlayable < AvrcpBrowsing::KPlayableReserved, MediaBrowsePanic::Panic(EInvalidPlayableValue));
		
		RItem& item = iGflResponse->iItems[iGflResponse->iCurrentItem];
		if (item.iUid == aFolderID)
			{
			item.iType = AvrcpBrowsing::EFolderItem;
			item.iCharset = KUtf8MibEnum;
			
			item.iName = aFolderName.Alloc();
			internalError = (!item.iName) ? KErrNoMemory : internalError;
					
			item.iNameLength = aFolderName.Length();
			
			item.iFolderType = 
			static_cast<AvrcpBrowsing::TFolderType>(aFolderType);
			
			item.iPlayable = aPlayable;
			item.iLength = KFolderItemBaseLength + item.iNameLength;
			}
		else
			{
			internalError = KErrArgument;
			}
	    }
			                        
	aResult = (internalError == KErrNone) ? aResult : KErrAvrcpAirInternalError;
	
	DoItemComplete(aResult);
	}

void CRemConMediaBrowseTargetBase::ProcessGetItemAttributesResult(
			const TArray<TMediaElementAttribute>& aAttributes,
			TInt aResult)
	{
	TInt internalError = KErrNone;
	if (aResult == KErrNone)
	    {
	    internalError = ItemAttributesResult(aAttributes);
	    }				
	
	aResult =
	(KErrNone == internalError)? aResult : KErrAvrcpAirInternalError;
	
	DoAllAttributesCompleted(aResult);
	}

inline TBool CRemConMediaBrowseTargetBase::DatabaseAware() const
	{
	return iRcdanpbo ? ETrue : EFalse;
	}