--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothappprofiles/avrcp/mediabrowseapi/src/remconmediabrowsetargetbase.cpp Mon Jan 18 20:28:57 2010 +0200
@@ -0,0 +1,1291 @@
+// 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;
+ }