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