bluetoothappprofiles/avrcp/playerinformation/src/playerevents.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 18 Jan 2010 20:28:57 +0200
changeset 0 f63038272f30
permissions -rw-r--r--
Revision: 201001 Kit: 201003

// 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:
// This file contains the events part of the PlayerInformation API.
// 
//

/**
 @file
 @publishedAll
 @released
*/

#include <bluetooth/logger.h>
#include <remconinterfaceselector.h>
#include <remconbeareravrcp.h>
#include <playerinformationtarget.h>
#include <playerinformationtargetobserver.h>

#include "playerapplicationsetting.h"
#include "eventsmask.h"
#include "playereventsutils.h"
#include "playerinformation.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_AVRCP_PLAYER_INFO);
#endif

EXPORT_C void MPlayerEventsObserver::PlaybackStatusChanged(TPlaybackStatus aStatus)
	{
	DoPlaybackStatusChanged(aStatus);
	}

EXPORT_C void MPlayerEventsObserver::TrackChanged(TUint64 aTrackId, TUint32 aLengthInMilliseconds) 
	{
	DoTrackChanged(aTrackId, aLengthInMilliseconds); 
	}

EXPORT_C void MPlayerEventsObserver::TrackReachedEnd() 
	{
	DoTrackReachedEnd();
	}

EXPORT_C void MPlayerEventsObserver::TrackReachedStart()
	{
	DoTrackReachedStart();
	}

EXPORT_C void MPlayerEventsObserver::SetPlaybackPosition(TUint32 aMilliseconds) 
	{
	DoSetPlaybackPosition(aMilliseconds); 
	}
	
EXPORT_C void MPlayerEventsObserver::SetBatteryStatus(TTargetBatteryStatus aBatteryStatus) 
	{
	DoSetBatteryStatus(aBatteryStatus); 
	}

void CPlayerInfoTarget::ProcessGetStatusAndBeginObserving( const TUint aOperationId, TRegisterNotificationEvent aEventId, const TDesC8& aData)
	{
	// check the event is in the supported list
	if (!iSupportedNotificationEventList->Find(aEventId))
		{
		// Not supported so return error
		SendError(KErrAvrcpMetadataInvalidParameter, aOperationId, ERemConNotifyResponseInterim);
		return;
		}
		
	// if the event is EPlaybackPosChanged then the timeinterval is included in the RegisterNofication
	if (aEventId == ERegisterNotificationPlaybackPosChanged)
		{
		// decode the playback interval from aData
		TInt error = 0;
		RRemConPlayerInformation32BitResponse request;
		TRAP(error, request.ReadL(aData));
		
		if (error != KErrNone)
			{
			// Not supported so return error
			SendError(KErrAvrcpMetadataParameterNotFound, aOperationId, ERemConNotifyResponseInterim);
			return;
			}
		
		// save the playback interval
		iPlayBackIntervalInMilliseconds = request.iValue * 1000;
		
		// and the current position
		iLastNotifiedPlaybackPositionInMilliseconds = iPlaybackPositionInMilliseconds;
		}
		
	// and request another notification (if there is not one already pending)
	// on the next state change  
	if (KErrNotFound ==iPendingNotificationEventList.Find(aEventId))
		{
		if (iPendingNotificationEventList.Append(aEventId) != KErrNone)
			{
			return SendError(KErrAvrcpMetadataInternalError, aOperationId);   // Try to send internal error if OOM
			}
		}
		
	// send the current status
	SendNotificationResponse(aEventId, ERemConNotifyResponseInterim );
	}

void CPlayerInfoTarget::ProcessGetStatus( const TUint aOperationId, TRegisterNotificationEvent aEventId)
	{
	// send the current value
	if (iSupportedNotificationEventList->Find(aEventId))
		{
		SendNotificationResponse(aEventId, ERemConNotifyResponseChanged );
		}
	else
		{
		// Not found so return error
		SendError(KErrAvrcpMetadataInvalidParameter, aOperationId, ERemConNotifyResponseChanged);
		}
	}
		
/* ProcessGetPlayStatus This returns the current values of SongLength, 
   SongPosition and PlayStatus.
   Note if SongLength and SongPosition are not supported KPlaybackPositionUnknown is returned
 */
void CPlayerInfoTarget::ProcessGetPlayStatus()
	{
	LOG_FUNC

	TInt error =0;

	// format the response in a RRemConPlayerInformationGetPlayStatusResponse
	RRemConPlayerInformationGetPlayStatusResponse response;
	response.iTrackLength = iLengthInMilliseconds;
	response.iPlayPos = iPlaybackPositionInMilliseconds;
	response.iStatus = iPlayBackStatus;
	
	TRAP(error, response.WriteL(iOutBuf));
	if (error == KErrNone)
		{
		// send the response back to the CT
		error = InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
												EGetPlayStatus, ERemConResponse, iOutBuf );
		}
	}

/* ProcessGetPlayStatusUpdate returns the current play status if it has changed
 * relative to the provided play status, otherwise the request is queued until
 * the play status changes.
 */
void CPlayerInfoTarget::ProcessGetPlayStatusUpdate(const TDesC8& aData)
	{
	LOG_FUNC

	// Bearer should never send us more than one of these in parallel
	__ASSERT_DEBUG(!iPlaybackStatusUpdatePending, PlayerEventsUtils::Panic(ETwoGetPlayStatusUpdatesQueued));
	
	TInt error =0;
	RRemConPlayerInformationGetPlayStatusUpdateRequest request;
	TRAP(error, request.ReadL(aData));
	__ASSERT_DEBUG(error == KErrNone, PlayerEventsUtils::Panic(EBadlyFormattedInternalData));

	if(request.iStatus != iPlayBackStatus)
		{
		// format the response in a RRemConPlayerInformationGetPlayStatusResponse
		RRemConPlayerInformationGetPlayStatusUpdateResponse response;
		response.iStatus = iPlayBackStatus;
		
		TRAP(error, response.WriteL(iOutBuf));
		if (error == KErrNone)
			{
			// send the response back to the CT
			error = InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
													EGetPlayStatusUpdate, ERemConResponse, iOutBuf );
			if(error != KErrNone)
				{
				// We will try and send the response again next time we get an update
				iPlaybackStatusUpdatePending = ETrue;
				}
			}
		}
	else
		{
		iPlaybackStatusUpdatePending = ETrue;
		}
	}

// Send a Notification message for aEventID 
void CPlayerInfoTarget::SendNotificationResponse( TRegisterNotificationEvent aEventId, TRemConMessageSubType aMsgSubType )
	{
	LOG_FUNC

	TInt error = 0;
	iOutBuf.Zero();

	if (!iSupportedNotificationEventList->Find(aEventId))
		{
		return SendError(KErrAvrcpMetadataInvalidParameter, 
						 RAvrcpIPC::SetIPCOperationIdFromEventId(aEventId), aMsgSubType);
		}
		
	switch (aEventId)
		{
		case ERegisterNotificationTrackReachedEnd:
		case ERegisterNotificationTrackReachedStart:
			{
			// no extra data for reached start or end 
			RAvrcpIPCError response;
			response.iError = KErrNone;
			TRAP(error, response.WriteL(iOutBuf));
			break;
			}

		case ERegisterNotificationPlaybackStatusChanged:
			{
			// 8bit response -- current playback status 
			RRemConPlayerInformation8BitResponse response;
			response.iValue = iPlayBackStatus;
			TRAP(error, response.WriteL(iOutBuf));
			break;
			}

		case ERegisterNotificationBatteryStatusChanged:
			{
			// 8bit response -- current battery status
			RRemConPlayerInformation8BitResponse response;
			response.iValue = iBatteryStatus;
			TRAP(error, response.WriteL(iOutBuf));
			break;
			}

		case ERegisterNotificationPlaybackPosChanged:
			{
			// 32bit response -- current playback position in millisecond 
			RRemConPlayerInformation32BitResponse response;
			response.iValue = iPlaybackPositionInMilliseconds;
			TRAP(error, response.WriteL(iOutBuf));
			break;
			}

		case ERegisterNotificationTrackChanged:
			{
			// 64bit response -- index of the current track 
			RRemConPlayerInformation64BitResponse response;
			response.iValue = iTrackId;
			TRAP(error, response.WriteL(iOutBuf));
			break;
			}

		case ERegisterNotificationPlayerApplicationSettingChanged:
			{
			// Send a list of the settings that have changed followed by their value 
			// starting with the number of attributes to be sent 
			RRemConPlayerAttributeIdsAndValues response;
			response.iNumberAttributes = 0;
		
			// for every entry in the list
			THashMapIter<TInt, CPlayerApplicationSettings*> iter(iPlayerApplicationSettings);
			CPlayerApplicationSettings* const* setting = iter.NextValue();
			while ( setting != NULL )
				{
				TUint8 value = (*setting)->GetCurrentValue();
				TInt ret1 = response.iAttributeValue.Append(value);
				TInt ret2 = response.iAttributeId.Append((*setting)->GetAttributeID());
				if (ret1 != KErrNone || ret2 != KErrNone)
					{
					response.Close();
					return SendError(KErrAvrcpMetadataInternalError, 
									 RAvrcpIPC::SetIPCOperationIdFromEventId(aEventId),
									 aMsgSubType);   // Try to send internal error if OOM
					}
				response.iNumberAttributes++;
				setting = iter.NextValue();
				}
				
			TRAP(error, response.WriteL(iOutBuf));
			response.Close();
			break;
			}
		default:
			return;
		}
	
	if (error == KErrNone)
		{
		// send the response back to the CT
		TUint operationId = RAvrcpIPC::SetIPCOperationIdFromEventId(aEventId);
		error = InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
												operationId, ERemConResponse, aMsgSubType, iOutBuf);
		}
	}
	 
// from MPlayerEventsObserver
 void CPlayerInfoTarget::DoPlaybackStatusChanged(TPlaybackStatus  aStatus)
	{
	LOG_FUNC

	iPlayBackStatus = aStatus;
	
	// if the playback status is in the current event list
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackStatusChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		SendNotificationResponse( ERegisterNotificationPlaybackStatusChanged, ERemConNotifyResponseChanged);
		}
	
	pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackPosChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		SendNotificationResponse(ERegisterNotificationPlaybackPosChanged, ERemConNotifyResponseChanged);
		}
	
	if(iPlaybackStatusUpdatePending)
		{
		// format the response in a RRemConPlayerInformationGetPlayStatusResponse
		RRemConPlayerInformationGetPlayStatusUpdateResponse response;
		response.iStatus = iPlayBackStatus;
		
		TRAPD(error, response.WriteL(iOutBuf));
		if (error == KErrNone)
			{
			// send the response back to the CT
			error = InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
													EGetPlayStatusUpdate, ERemConResponse, iOutBuf );
			if(error == KErrNone)
				{
				iPlaybackStatusUpdatePending = EFalse;
				}
			// if we did error we will try and respond again next time the client's play status
			// changes
			}
		}
	}
	
void CPlayerInfoTarget::DoTrackChanged(TUint64 aTrackId, TUint32 aLengthInMilliseconds)
	{
	LOG_FUNC

	iTrackId = aTrackId;
	iLengthInMilliseconds = aLengthInMilliseconds;

	// if the playback status is in the current event list 
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationTrackChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		SendNotificationResponse(ERegisterNotificationTrackChanged, ERemConNotifyResponseChanged);
		}

	pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackPosChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		iPlaybackPositionInMilliseconds = 0;	//set 0 to current position for new track
		SendNotificationResponse(ERegisterNotificationPlaybackPosChanged, ERemConNotifyResponseChanged);
		}
	}

void CPlayerInfoTarget::DoTrackReachedEnd()
	{
	LOG_FUNC
	
	iTrackPosition = EEnd;

	// if the ETrackReachedEnd status is in the current event list 
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationTrackReachedEnd );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		SendNotificationResponse(ERegisterNotificationTrackReachedEnd, ERemConNotifyResponseChanged);
		}
	
	pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackPosChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		iPlaybackPositionInMilliseconds = iLengthInMilliseconds;
		SendNotificationResponse(ERegisterNotificationPlaybackPosChanged, ERemConNotifyResponseChanged);
		}
	}

void CPlayerInfoTarget::DoTrackReachedStart()
	{
	LOG_FUNC

	iTrackPosition = EStart;

	// if the ETrackReachedStart status is in the current event list 
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationTrackReachedStart );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		SendNotificationResponse(ERegisterNotificationTrackReachedStart, ERemConNotifyResponseChanged);
		}
	
	pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackPosChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		iPlaybackPositionInMilliseconds = 0;
		SendNotificationResponse(ERegisterNotificationPlaybackPosChanged, ERemConNotifyResponseChanged);
		}
	}

void CPlayerInfoTarget::DoSetPlaybackPosition(TUint32 aMilliseconds)		
	{
	LOG_FUNC
	
	iPlaybackPositionInMilliseconds = aMilliseconds;

	// if the playback position is in the current Notification event list 
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationPlaybackPosChanged );
	if (pos != KErrNotFound)
		{
		// a notification has been requested, now check if it is due

		TUint32 difference = (iPlaybackPositionInMilliseconds > iLastNotifiedPlaybackPositionInMilliseconds) ?
        	iPlaybackPositionInMilliseconds - iLastNotifiedPlaybackPositionInMilliseconds:
        	iLastNotifiedPlaybackPositionInMilliseconds - iPlaybackPositionInMilliseconds;

		if (difference >= iPlayBackIntervalInMilliseconds)
			{
			// Due now so send
			iPendingNotificationEventList.Remove( pos );
			SendNotificationResponse(ERegisterNotificationPlaybackPosChanged, ERemConNotifyResponseChanged);			
			}	
		}
	}

void CPlayerInfoTarget::DoSetBatteryStatus(TTargetBatteryStatus aBatteryStatus)
	{
	LOG_FUNC

	TBool validStatus = ETrue;
	if (aBatteryStatus < MPlayerEventsObserver::ENormal || 
		aBatteryStatus > MPlayerEventsObserver::EFullCharge)
		{
		LOG1(_L("Invalid battery status: %d"),aBatteryStatus);
		validStatus = EFalse;
		}
	else
		{
		iBatteryStatus = aBatteryStatus;
		}

	// if the battery status is in the current event list
	TInt pos = iPendingNotificationEventList.Find( ERegisterNotificationBatteryStatusChanged );
	if (pos != KErrNotFound)
		{
		iPendingNotificationEventList.Remove( pos );
		if (validStatus)
			{
			SendNotificationResponse(ERegisterNotificationBatteryStatusChanged, ERemConNotifyResponseChanged);
			}
		}
	}
	
void CPlayerInfoTarget::SendError(TInt aError, TInt aOperationId, TRemConMessageSubType aSubType)
	{
	TInt error = 0;
	RAvrcpIPCError response;
	response.iError = aError;
	TRAP(error, response.WriteL(iOutBuf));   // Don't send error if OOM
	if (error == KErrNone)
		InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
										aOperationId, ERemConResponse, aSubType, iOutBuf);
	}