bluetoothappprofiles/avrcp/playerinformation/src/playersettings.cpp
author hgs
Wed, 13 Oct 2010 13:15:31 +0300
changeset 67 16e4b9007960
parent 0 f63038272f30
permissions -rw-r--r--
201039_4

// 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
 @publishedAll
 @released
*/

#include <remconinterfaceselector.h>
#include <playerinformationtarget.h>

#include "playerapplicationsetting.h"
#include "playerinformation.h"
#include "playersettingsutils.h"


void CPlayerInfoTarget::SendError(TInt aError, TInt aOperationId)
	{
	TInt error = 0;
	RAvrcpIPCError response;
	response.iError = aError;
	TRAP(error, response.WriteL(iOutBuf));   // Try to send internal error if OOM
	if (error == KErrNone)
		{
		InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
										aOperationId, ERemConResponse, iOutBuf);
		}								
	}


// PDU 0x11
void CPlayerInfoTarget::ProcessListPlayerApplicationAttributes(TInt aOperationId)
	{
	RRemConPlayerListOfAttributes response;
	response.iNumberAttributes = iPlayerApplicationSettings.Count();

	THashMapIter<TInt, CPlayerApplicationSettings*> iter(iPlayerApplicationSettings);
	const TInt* attribute = iter.NextKey();
	while ( attribute != NULL )
		{
		if (response.iAttributes.Append(*attribute) != KErrNone)
			{
			response.Close();
			return SendError(KErrAvrcpMetadataInternalError, aOperationId);   // Try to send internal error if OOM
			}
		attribute = iter.NextKey();
		}

	// send the response back to the CT
	TInt error = 0;
	TRAP(error, response.WriteL(iOutBuf));   // Try to send internal error if OOM
	response.Close();
	if (error != KErrNone)
		{
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
									EListPlayerApplicationSettingAttributes, ERemConResponse, iOutBuf );
	}

// PDU 0x12
void CPlayerInfoTarget::ProcessListPlayerApplicationValues(const TDesC8& aData, TInt aOperationId)
	{
	TInt error =0;
	RDesReadStream readStream;
	readStream.Open(aData);
	TInt attributeID = 0;
	TRAP(error, attributeID = readStream.ReadUint8L());
	readStream.Close();
	if (error != KErrNone)
		{
		return SendError(KErrAvrcpMetadataParameterNotFound, aOperationId);  // Nothing in packet
		}
		
	// Send the number of values for this attribute (1 byte),
	// followed by the defined values themselves (n x 1byte)
	CPlayerApplicationSettings* thisSetting = GetSetting(attributeID);
	if (thisSetting == NULL) 
		{
		// Attribute Id not found
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);  
		}
		
	RRemConPlayerListOfAttributes response;
	RArray<TUint>* values = thisSetting->GetValues();
	TInt numValues = values->Count();
	response.iNumberAttributes = numValues;

	// Make sure that we always have at least one result to return
	// Table 5.18 says that the number of results provided has an
	// allowed value of 1-255, so we cannot return zero results.
	if (response.iNumberAttributes == 0)
		{
		return SendError(KErrAvrcpMetadataParameterNotFound, aOperationId);  // No attributes matched
		}

	for ( TInt i = 0; i < numValues; i++ )
		{
		TInt value = (*values)[i];
		if (response.iAttributes.Append(value) != KErrNone)
			{
			response.Close();
			return SendError(KErrAvrcpMetadataInternalError, aOperationId);   // Try to send internal error if OOM
			}
		}

	// send the response back to the CT
	TRAP(error, response.WriteL(iOutBuf));   // Try to send internal error if OOM
	response.Close();
	if (error != KErrNone)
		{
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
									aOperationId, ERemConResponse, iOutBuf );
	}

// PDU 0x15
void CPlayerInfoTarget::ProcessGetPlayerApplicationAttributeText(const TDesC8& aData, TInt aOperationId)
	{
	TInt error =0;

	// If we can't parse the request, then return an error
	RRemConPlayerListOfAttributes request;
	TRAP(error, request.ReadL(aData));
	if (error != KErrNone)
		{
		request.Close();
		return SendError(KErrAvrcpMetadataInvalidCommand, aOperationId);
		}
		
	// Iterate through requested attributes and remove
	// those which we don't have any settings for.
	for (TInt i = 0; i < request.iNumberAttributes; i++)
		{
		if (! AttributeSettingExists(request.iAttributes[i]))
			{
			request.iAttributes.Remove(i);
			request.iNumberAttributes--;
			i--;
			}
		}
		
	// Make sure that we always have at least one result to return
	// Table 5.18 says that the number of results provided has an
	// allowed value of 1-255, so we cannot return zero results.
	RRemConGetPlayerApplicationTextResponse response;
	response.iNumberAttributes = request.iNumberAttributes;
	if (response.iNumberAttributes == 0)
		{
		request.Close();
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);  // No attributes matched
		}

	// for every attribute text requested
	for (TInt i = 0; i < request.iNumberAttributes; i++)
		{
		// start with the attribute id requested and the character set
		RSettingWithCharset setting;
		TInt attId = request.iAttributes[i];
		CPlayerApplicationSettings* thisSetting = GetSetting(attId);
		setting.iAttributeId = attId;
		setting.iCharset = KUtf8MibEnum;
		const TPtrC8 text = thisSetting->GetAttributeText();
		setting.iStringLen = text.Length();
		setting.iString = text.Alloc();
		
		// If OOM, try to return an internal error.
		if (setting.iString == NULL)
			{
			request.Close();
			response.Close();
			return SendError(KErrAvrcpMetadataInternalError, aOperationId);
			}
			
		// If OOM, try to return an internal error.
		if (response.iAttributes.Append(setting) != KErrNone)
			{
			setting.Close();
			request.Close();
			response.Close();
			return SendError(KErrAvrcpMetadataInternalError, aOperationId);
			}
		}
	request.Close();
	
	// Allocate a buffer for the formatted message
	RBuf8 messageBuffer;
	if ( messageBuffer.Create(response.Size()) != KErrNone )
		{
		// On OOM drop the message
		response.Close();
		return;
		}
		
	// send the response back to the CT
	TRAP(error, response.WriteL(messageBuffer));   // Try to send internal error if OOM
	response.Close();
	if (error != KErrNone)
		{
		messageBuffer.Close();
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
									aOperationId, ERemConResponse, messageBuffer );
	
	messageBuffer.Close();
	}

// PDU 0x14
void CPlayerInfoTarget::ProcessSetPlayerApplicationValue(const TDesC8& aData, TInt aOperationId)
	{
	RRemConPlayerAttributeIdsAndValues request;
	TInt error = 0;

	// If we can't parse the request, then return an error
	TRAP(error, request.ReadL(aData));
	if (error != KErrNone)
		{
		request.Close();
		return SendError(KErrAvrcpMetadataInvalidCommand, aOperationId);
		}
		
	// Iterate through all the settings we're sent to update and set them
	for (TInt i = 0; i < request.iNumberAttributes; i++)
		{
		if (! AttributeValueCanBeSet(request.iAttributeId[i], request.iAttributeValue[i]))
			{
			// remove this setting
			request.iAttributeId.Remove(i);
			request.iAttributeValue.Remove(i);
			request.iNumberAttributes--;
			i--;
			}
		}
	__ASSERT_DEBUG(request.iAttributeId.Count() == request.iAttributeValue.Count(), PlayerSettingsUtils::Panic(EPlayerSettingsFunnyLengthData));
	
	// Section 5.7 of the AVRCP specification (page 56) says:
	// If CT sent a PDU with nonexistent PDU ID or a PDU containing
	// only one parameter with nonexistent parameter ID, TG shall return
	// REJECTED response with Error Status Code. If CT sent a PDU with
	// multiple parameters where at least one ID is existent and the
	// others are nonexistent, TG shall proceed with the existent ID and
	// ignore the non-existent IDs.
	//
	// This means we return REJECTED if we have nothing to set
	if (request.iNumberAttributes == 0)
		{
		request.Close();
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);
		}
		
	// If an application setting notifier has been requested then notify it
	// If the notification succeeds, then set all the values

	TRAP(error, iApplicationSettingNotifier.MpasnSetPlayerApplicationValueL(request.iAttributeId, request.iAttributeValue));
	if ( error == KErrNone )
		{
		for (TInt i = 0; i < request.iNumberAttributes; i++)
			{
			CPlayerApplicationSettings* thisSetting = GetSetting(request.iAttributeId[i]);
			thisSetting->SetCurrentValue(request.iAttributeValue[i]);
			}
		}
	else
		{
		// Return an AVRCP internal error via RemCon. See section 5.7.1 of specification.
		request.Close();
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	request.Close();
     // Send a notification if one has been registered
     if ( KErrNotFound != iPendingNotificationEventList.Find( ERegisterNotificationPlayerApplicationSettingChanged ))
          {
           SendNotificationResponse( ERegisterNotificationPlayerApplicationSettingChanged, ERemConNotifyResponseChanged );
          }
       
		
	// SendError KErrNone is used to send a valid response
	SendError(KErrNone, aOperationId);
	}

// PDU 0x13
void CPlayerInfoTarget::ProcessGetCurrentPlayerApplicationValue(const TDesC8& aData, TInt aOperationId)
	{
	TInt error = 0;

	// If we can't parse the request, then return an error
	RRemConPlayerListOfAttributes request;
	TRAP(error, request.ReadL(aData));
	if (error != KErrNone)
		{
		request.Close();
		return SendError(KErrAvrcpMetadataInvalidCommand, aOperationId);
		}
		
	// Look through requested attributes, and assemble a response
	// for those which we posess. If none are found, return an error
	RRemConPlayerAttributeIdsAndValues response;
	response.iNumberAttributes = 0;
	request.iAttributes.Sort();
	
	for (TInt i = 0; i < request.iNumberAttributes; i++)
		{
		TInt attToSend = request.iAttributes[i];
		if (AttributeSettingExists(attToSend))
			{
			TInt ret1 = response.iAttributeId.Append(attToSend);
			TInt ret2 = response.iAttributeValue.Append(GetSetting(attToSend)->GetCurrentValue());
			if (ret1 != KErrNone || ret2 != KErrNone)
				{
				request.Close();
				response.Close();
				return SendError(KErrAvrcpMetadataInternalError, aOperationId);   // Try to send internal error if OOM
				}
			response.iNumberAttributes++;
			}
		}
	request.Close();
		
	// Make sure that we always have at least one result to return
	// Table 5.18 says that the number of results provided has an
	// allowed value of 1-255, so we cannot return zero results.
	if (response.iNumberAttributes == 0)
		{
		response.Close();
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);  // No attributes matched
		}
		
	// send the response back to the CT
	TRAP(error, response.WriteL(iOutBuf));   // Try to send internal error if OOM
	response.Close();
	if (error != KErrNone)
		{
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
									aOperationId, ERemConResponse, iOutBuf );
	}

// PDU 0x16
void CPlayerInfoTarget::ProcessGetPlayerApplicationValueText(const TDesC8& aData, TInt aOperationId)
	{
	RDesReadStream readStream;
	readStream.Open(aData);

	TInt error = 0;
	TInt reqAttribute = 0;
	
	// Read the attribute id
	TRAP(error, reqAttribute = readStream.ReadUint8L());
	if (error != KErrNone)
		{
		readStream.Close();
		return SendError(KErrAvrcpMetadataParameterNotFound, aOperationId);  // Nothing in packet
		}
		
	// If we don't have settings for this attribute, return an error
	CPlayerApplicationSettings* thisSetting = GetSetting(reqAttribute);
	if (thisSetting == NULL)
		{
		readStream.Close();
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);  // Attribute not found
		}
		
	// Read the number of PAS values
	TInt numSettings = 0;
	TRAP(error, numSettings = readStream.ReadUint8L());
	if (error != KErrNone)
		{
		readStream.Close();
		return SendError(KErrAvrcpMetadataParameterNotFound, aOperationId);  // Nothing in packet
		}
	
	RArray<TInt> valueTextsRequested;
	for (TInt i = 0; i < numSettings; i++)
		{
		TInt requestedValueText = 0;
		TRAP(error, requestedValueText = readStream.ReadUint8L());
		if (error == KErrNone)
			{
			if (valueTextsRequested.Append(requestedValueText) != KErrNone)
				{
				readStream.Close();
				return SendError(KErrAvrcpMetadataInternalError, aOperationId);   // Try to send internal error if OOM
				}
			}
		}
	
	readStream.Close();
	
	// format the response in a RRemConGetPlayerApplicationTextResponse
	RRemConGetPlayerApplicationTextResponse response;
	response.iNumberAttributes = 0;

	// for every attribute text requested
	RPointerArray<HBufC8>* textValues = thisSetting->GetValuesTexts();
	RArray<TUint>* values = thisSetting->GetValues();
	TInt numRequested = valueTextsRequested.Count();
	for (TInt i = 0; i < numRequested; i++)
		{
		// start with the attribute id requested and the character set
		RSettingWithCharset setting;
		TInt valueToSend = valueTextsRequested[i];
		setting.iAttributeId = valueToSend;
		setting.iCharset = KUtf8MibEnum;

		// text length followed by the text
		TInt found = values->Find(valueToSend);
		if (found != KErrNotFound)
			{
			HBufC8* text = (*textValues)[found];
			setting.iStringLen = text->Length();
			setting.iString = text->Alloc();
			
			// If OOM, try to return an internal error.
			if (setting.iString == NULL)
				{
				response.Close();
				valueTextsRequested.Close();
				return SendError(KErrAvrcpMetadataInternalError, aOperationId);
				}
				
			// If OOM, try to return an internal error. Of course, this could fail too
			if (response.iAttributes.Append(setting) != KErrNone)
				{
				response.Close();
				setting.Close();
				valueTextsRequested.Close();
				return SendError(KErrAvrcpMetadataInternalError, aOperationId);
				}
				
			response.iNumberAttributes++;
			}
		}
	valueTextsRequested.Close();
	
	// Make sure that we always have at least one result to return
	// Table 5.18 says that the number of results provided has an
	// allowed value of 1-255, so we cannot return zero results.
	if (response.iNumberAttributes == 0)
		{
		response.Close();
		return SendError(KErrAvrcpMetadataInvalidParameter, aOperationId);
		}
		
	// Allocate a buffer for the formatted message
	RBuf8 messageBuffer;
	if ( messageBuffer.Create(response.Size()) != KErrNone )
		{
		// On OOM drop the message
		response.Close();
		return;
		}
		
	// send the response back to the CT
	TRAP(error, response.WriteL(messageBuffer));   // Try to send internal error if OOM
	response.Close();
	if (error != KErrNone)
		{
		messageBuffer.Close();
		return SendError(KErrAvrcpMetadataInternalError, aOperationId);
		}
		
	InterfaceSelector().SendUnreliable(TUid::Uid(KRemConPlayerInformationUid),
									aOperationId, ERemConResponse, messageBuffer );
	messageBuffer.Close();
	}

TBool CPlayerInfoTarget::AttributeSettingExists(TUint anAttributeID)
	{
	if (GetSetting(anAttributeID) == NULL)
		{
		return EFalse;
		}
	else
		{
		return ETrue;
		}
	}

TBool CPlayerInfoTarget::IsValidAttributeValue(TUint anAttributeID, TUint anAttributeValue)
	{
	// As defined in Appendix F of the AVRCP specification, page 81
	// Attribute 0x01 - range 0x01 to 0x02
	// Attribute 0x02 - range 0x01 to 0x04
	// Attribute 0x03 - range 0x01 to 0x03
	// Attribute 0x04 - range 0x01 to 0x03
	// Attribute 0x05 - 0x7f -- invalid; reserved for future use
	// Attribute 0x80 - 0xff -- vendor dependent
	if (anAttributeID >= 0x80 && anAttributeID <= 0xff && anAttributeValue <= 0xff )
		{
		return ETrue;
		}
		
	if (anAttributeID == 0x01 && anAttributeValue >= 0x01 && anAttributeValue <= 0x02)
		{
		return ETrue;
		}
		
	if (anAttributeID == 0x02 && anAttributeValue >= 0x01 && anAttributeValue <= 0x04)
		{
		return ETrue;
		}
		
	if ((anAttributeID == 0x03 || anAttributeID == 0x04)
		&& anAttributeValue >= 0x01 && anAttributeValue <= 0x03)
		{
		return ETrue;
		}
		
	// Everything else is invalid, as defined by the specification
	return EFalse;
	}

// Check that anAttributeValue is in the valid value list for anAttributeID
TBool CPlayerInfoTarget::AttributeValueCanBeSet(TUint anAttributeID, TUint anAttributeValue)
	{
	CPlayerApplicationSettings* thisSetting = GetSetting(anAttributeID);
	if (thisSetting == NULL)
		{
		return EFalse;
		}
		
	RArray<TUint>* values = thisSetting->GetValues();
	if (values == NULL)
		{
		return EFalse;
		}
		
	if (values->Find(anAttributeValue) == KErrNotFound)
		{
		return EFalse;
		}
		
	// This attribute id and value has been already defined by the RSS
	// file, and checked that it conforms to the specification when the
	// RSS file was loaded. Now allow this value to be set over the air.
	return ETrue;
	}

CPlayerApplicationSettings* CPlayerInfoTarget::GetSetting(TUint anAttributeID)
	{
	// Will return NULL if anAttributeID is not found
	CPlayerApplicationSettings** settings = iPlayerApplicationSettings.Find(anAttributeID);
	if (settings == NULL)
		{
		return NULL;
		}
		
	return *settings;
	}


// from MPlayerApplicationSettingsObserver
// exported function wrapper for internal pure virtual
EXPORT_C void MPlayerApplicationSettingsObserver::DefineAttributeL(TUint aAttributeID,
																	TDesC8& aAttributeText,
																	RArray<TUint>& aValues,
																	RArray<TPtrC8>& aValueTexts,
																	TUint aInitialValue)
	{
	DoDefineAttributeL(aAttributeID, aAttributeText, aValues, aValueTexts, aInitialValue);
	}

EXPORT_C void MPlayerApplicationSettingsObserver::SetAttributeL(TUint aAttributeID, TUint aValue)
	{
	DoSetAttributeL(aAttributeID, aValue );
	}

void CPlayerInfoTarget::DoDefineAttributeL(TUint aAttributeID,
											TDesC8& aAttributeText,
											RArray<TUint>& aValues,
											RArray<TPtrC8>& aValueTexts,
											TUint aInitialValue)
	{
	//Check Length of the player application setting attribute string is 1-255
	if(aAttributeText.Length() > KMaxPlayerApplicationSettingsValue || 
			aAttributeText.Length() < KMinPlayerApplicationSettingsValue )
		{
		User::Leave(KErrNotSupported);
		}
	
	//Check the number of player application setting values is 1-255
	if(aValues.Count() > KMaxPlayerApplicationSettingsValue || 
			aValues.Count() < KMinPlayerApplicationSettingsValue )
		{
		User::Leave(KErrNotSupported);
		}	
	
	//Check the numbers of player application setting values and 
	//player application setting value texts are equal
	if(aValues.Count() != aValueTexts.Count())
		{
		User::Leave(KErrNotSupported);
		}
	
	//Check Length of the player application setting value string is 1-255
	for(TInt i = 0; i < aValueTexts.Count(); i++ )
		{
		if(aValueTexts[i].Length() > KMaxPlayerApplicationSettingsValue ||
				aValueTexts[i].Length() < KMinPlayerApplicationSettingsValue )
			{
			User::Leave (KErrNotSupported);
			}			
		}

	for (TInt i = 0; i < aValues.Count(); i++)
		{
		// The user cannot define certain attribute ids or values; see Appendix F
		if ( ! IsValidAttributeValue(aAttributeID, aValues[i]))
			{
			User::Leave(KErrNotSupported);
			}
		}
	
	// Check the initial value, too
	if ( ! IsValidAttributeValue(aAttributeID, aInitialValue))
		{
		User::Leave(KErrNotSupported);
		}
		
	// check that aInitialValue is in aValues
	if (aValues.Find(aInitialValue) == KErrNotFound)
		{
		User::Leave(KErrNotSupported);
		}
		
	// create a new TPlayerApplicationSettings
	CPlayerApplicationSettings* newSetting = CPlayerApplicationSettings::NewL(aAttributeID, aAttributeText, aValues, aValueTexts, aInitialValue);
	CleanupStack::PushL(newSetting);
	
	// Backup the settings of aAttributeID if they exist, return NULL if the attribute ID cannot be found
	CPlayerApplicationSettings* backupSetting = GetSetting(aAttributeID); 
	
	// and save it 
	iPlayerApplicationSettings.InsertL(aAttributeID, newSetting);
	
	//Delete backupSetting, as the InsertL will replace the old objects by the provided objects 
	delete backupSetting;
	
	CleanupStack::Pop(newSetting);
	}

void CPlayerInfoTarget::DoSetAttributeL(TUint aAttributeID, TUint aValue)
	{
	// Will return NULL if the attribute ID cannot be found
	CPlayerApplicationSettings* setting = GetSetting(aAttributeID);
	if (setting == NULL)
		{
		User::Leave(KErrNotFound);
		}
		
	if ( ! IsValidAttributeValue(aAttributeID, aValue))
		{
		User::Leave(KErrNotSupported);
		}
		
	setting->SetCurrentValue(aValue);
	
	if ( KErrNotFound != iPendingNotificationEventList.Find( ERegisterNotificationPlayerApplicationSettingChanged ))
		{
		// send response
		SendNotificationResponse( ERegisterNotificationPlayerApplicationSettingChanged, ERemConNotifyResponseChanged );
		}
	}