--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/obexmtms/obexmtm/obexutil/source/ObexSdpUtils.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,695 @@
+// Copyright (c) 2001-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:
+//
+
+//class include
+#include "ObexSdpUtils.h"
+//system includes
+#include <es_sock.h>
+
+
+//
+// Bluetooth Assigned Numbers
+// The following constants come from the Bluetooth Specification Version 1.0B
+//
+
+const TUint32 KObexProtocolUUIDValue = 0x0008;
+const TUint32 KObexObjectPushUUIDValue = 0x1105;
+const TUint32 K_L2CAP_UUID_Value = 0x0100;
+const TUint32 K_RFCOMM_UUID_Value = 0x0003;
+
+const TUint16 KServiceClassIdAttributeId = 0x0001;
+const TUint16 KProtocolDescriptorListAttributeId = 0x0004;
+const TUint16 KBluetoothProfileDescriptorListAttributeId = 0x0009;
+const TUint16 KSupportedFormatsListAttributeId = 0x0303;
+
+
+//
+// The following structures are used to validate the record we receive from a remote
+// device
+//
+
+const TInt KAttributes = 4;
+const TInt KMaxComponents = 8;
+const TInt KTAttributeComponentMembers = 3;
+
+const TUint16 KExpectedAttributes[KAttributes] =
+ {
+ KServiceClassIdAttributeId,
+ KProtocolDescriptorListAttributeId,
+ KBluetoothProfileDescriptorListAttributeId,
+ KSupportedFormatsListAttributeId
+ };
+
+const TUint32 KExpectedComponents[KAttributes][KMaxComponents][KTAttributeComponentMembers] =
+ {
+ // KServiceClassIdAttributeId
+ {
+ {CObexSdpUtils::EUint, ETrue, KServiceClassIdAttributeId},
+ {CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+ {CObexSdpUtils::EUUID, ETrue, KObexObjectPushUUIDValue},
+ {CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+ },
+ // KProtocolDescriptorListAttributeId
+ {
+ {CObexSdpUtils::EUint, ETrue, KProtocolDescriptorListAttributeId},
+ {CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+ {CObexSdpUtils::EUUID, ETrue, K_L2CAP_UUID_Value},
+ {CObexSdpUtils::EUintPossible, ETrue, K_RFCOMM_UUID_Value}, //NOT MANDATORY TO RECEIVE THIS UINT
+ {CObexSdpUtils::EUUID, ETrue, K_RFCOMM_UUID_Value},
+ {CObexSdpUtils::EUint, EFalse, 0}, //REMOTE OBEX PORT NUMBER
+ {CObexSdpUtils::EUUID, ETrue, KObexProtocolUUIDValue},
+ {CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+ },
+ // KBluetoothProfileDescriptorListAttributeId - NOT MANDATORY ATTRIBUTE BUT IF PRESENT INDICATES THAT REMOTE DEVICE CONFORMS 100% TO OBEX OBJECT PUSH PROFILE
+ {
+ {CObexSdpUtils::EUint, ETrue, KBluetoothProfileDescriptorListAttributeId},
+ {CObexSdpUtils::EAnythingUntilNextExpectedValue, EFalse, 0},
+ {CObexSdpUtils::EUUID, ETrue, KObexObjectPushUUIDValue},
+ {CObexSdpUtils::EUint, EFalse, 0}, //VERSION OF OBEX OBJECT PUT SUPPORTED
+ {CObexSdpUtils::EAnythingUntilEnd, EFalse, 0}
+ },
+ // KSupportedFormatsListAttributeId
+ {
+ {CObexSdpUtils::EUint, ETrue, KSupportedFormatsListAttributeId},
+ {CObexSdpUtils::EUintListUntilEnd, EFalse, 0}, // FORMATS SUPPORTED - LIST OF UINTS OF UNSPECIFIED LENGTH
+ {CObexSdpUtils::ENoComponent, EFalse, 0}
+ }
+ };
+
+
+//
+// CObexSdpUtils
+//
+
+
+EXPORT_C CObexSdpUtils* CObexSdpUtils::NewL(MObexSdpUtilsObserver& aObserver)
+/**
+ * Leave safe constructor
+ *
+ * @param aObserver Used to indicate results of SDP query
+ */
+ {
+ CObexSdpUtils* self = new(ELeave) CObexSdpUtils(aObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+EXPORT_C CObexSdpUtils* CObexSdpUtils::NewLC(MObexSdpUtilsObserver& aObserver)
+/**
+ * Leave safe constructor (which adds the newly created object to the cleanup stack)
+ *
+ * @param aObserver Used to indicate results of SDP query
+ */
+ {
+ CObexSdpUtils* self = new(ELeave) CObexSdpUtils(aObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+void CObexSdpUtils::ConstructL()
+/**
+ * Necessary initial construction - Creates various structures used to check
+ * results obtained through CSdpAgent
+ */
+ {
+ // Set up structures containing expected components
+ iAttributeArray = new(ELeave) CArrayPtrFlat<CAttribute>(1); //granularity of 1
+
+ TInt attrCount;
+ for (attrCount=0; attrCount<KAttributes; attrCount++)
+ {
+ CObexSdpUtils::CAttribute* attr = new(ELeave) CAttribute;
+ CleanupStack::PushL(attr);
+ attr->iAttributeComponentArray = new(ELeave) CArrayFixFlat<TAttributeComponent>(1); //granularity of 1
+
+ TInt compCount = -1;
+ do
+ {
+ compCount++;
+ TAttributeComponent comp;
+ comp.iType = (TType)KExpectedComponents[attrCount][compCount][0];
+ comp.iSpecificValue = KExpectedComponents[attrCount][compCount][1];
+ comp.iValue = KExpectedComponents[attrCount][compCount][2];
+ attr->iAttributeComponentArray->AppendL(comp);
+ } while (KExpectedComponents[attrCount][compCount][0]!=ENoComponent);
+
+ attr->iAttributeId = KExpectedAttributes[attrCount];
+ iAttributeArray->AppendL(attr);
+ CleanupStack::Pop(attr);
+ }
+ }
+
+
+CObexSdpUtils::CObexSdpUtils(MObexSdpUtilsObserver& aObserver) : iObserver(aObserver), iIsActiveSDPQuery(EFalse)
+
+/**
+ * Simple constructor
+ *
+ * @param aObserver Used to indicate results of SDP query
+ */
+ {
+ }
+
+CObexSdpUtils::~CObexSdpUtils()
+/**
+ * Destructor. This can be called before the SDP Query completes to safely cancel the
+ * query - N.B. this is the only way to cancel a pending query
+ */
+ {
+ if (iIsActiveSDPQuery)
+ // if we are in the middle of a query
+ {
+ HaltQueryWithError(KErrAbort);
+ //iObserver.RemoteBtObexQueryResult(KErrAbort,KErrNotFound,EFalse,KErrNotFound,iSupportedFormatsList);
+ //iSdpAgent->Cancel();
+ }
+
+ if(iSdpAgent)
+ // if we have started a SDP agent
+ {
+ delete iSdpAgent;
+
+ }
+
+ iAttributeArray->ResetAndDestroy();
+ delete iAttributeArray;
+ }
+
+
+EXPORT_C void CObexSdpUtils::RemoteBtObexQueryL(TBTDevAddr aBtDevAddr)
+/**
+ * Perform SDP query on a remote bluetooth device. Result is returned through the MObexSdpUtilsObserver
+ * observer class. The query can be cancelled at any time by destroying this CObexSdpUtils
+ * object.
+ *
+ * @param aBtDevAddr The address of the bluetooth device
+ */
+ {
+ if(iSdpAgent)
+ {
+ iSdpAgent->Cancel();
+ }
+ else
+ {
+ iSdpAgent = CSdpAgent::NewL(*this,aBtDevAddr);
+ }
+
+ // Set up search for SDP records containing the OBEX Object Push Service
+ // UUID.
+ // We also specify that the record must contain L2CAP and RFCOMM UUIDS as
+ // these are mandatory according to the BT Spec
+ CSdpSearchPattern* searchPattern = CSdpSearchPattern::NewL();
+ CleanupStack::PushL(searchPattern);
+ searchPattern->AddL(TUUID(KObexObjectPushUUIDValue));
+ searchPattern->AddL(TUUID(K_L2CAP_UUID_Value));
+ searchPattern->AddL(TUUID(K_RFCOMM_UUID_Value));
+ iSdpAgent->SetRecordFilterL(*searchPattern); //makes COPY of searchPattern
+ CleanupStack::PopAndDestroy(searchPattern);
+
+ // Start the search
+ iSdpRecordCount=0;
+ iSdpAgent->NextRecordRequestL(); //Will result in call to NextRecordRequestComplete()
+ iIsActiveSDPQuery = ETrue; //Keep record of the fact that we are mid query
+ }
+
+void CObexSdpUtils::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt aTotalRecordsCount)
+/**
+ * Got an SDP record from the remote device, so check for errors and then request the
+ * first attribute.
+ *
+ * @param aError Error code
+ * @param aHandle Handle to the service recorde
+ * @param aTotalRecordsCount The total number of records
+ */
+ {
+ iSdpRecordCount++;
+
+ if (aError)
+ {
+ // return the error that has prevented us from continuing with our query
+ HaltQueryWithError(aError);
+ return;
+ }
+
+ if(iSdpRecordCount>aTotalRecordsCount)
+ {
+ // return KErrEof because we have gone through all the records and
+ // the port hasn't been found
+ HaltQueryWithError(KErrEof);
+ return;
+ }
+
+ // Got a record so let's request our first attribute
+ iNotThisSdpRecord = EFalse;
+ iRemoteObexPort = KErrNotFound;
+ iFullComplianceWithObexObjectPush = EFalse;
+ iObexObjectPushProfileVersion = KErrNotFound;
+ iSupportedFormatsList.SetLength(0);
+
+ iAttribute = -1;
+ RequestNextAttribute(aHandle); //will increment iAttribute to 0
+ }
+
+void CObexSdpUtils::AttributeRequestResult(TSdpServRecordHandle /*aHandle*/, TSdpAttributeID /*aAttrID*/, CSdpAttrValue* /*aAttrValue*/)
+/**
+ * The overload of AttributeRequestL() that we are using in NextRecordRequestComplete()
+ * should NOT result in this function being called, if it does then halt the query with an
+ * error.
+ *
+ * @param aHandle The handle to the service record
+ * @param aAttrID The SDP Attribute ID
+ * @param aAttrValue The SDP Attribute Value
+ */
+ {
+ // The overload of AttributeRequestL() that we are using in NextRecordRequestComplete
+ // should NOT result in this function being called
+ HaltQueryWithError(KErrUnknown);
+ }
+
+void CObexSdpUtils::AttributeRequestComplete(TSdpServRecordHandle aHandle, TInt aError)
+/**
+ * Called after we have got all the attribute components for current attribute being
+ * processed. If everything is OK get the next attribute or record.
+ *
+ * @param aHandle The handle to the service record
+ * @param aError The error code
+ */
+ {
+ if (!aError && !iNotThisSdpRecord && iAttribute>=iAttributeArray->Count()-1 && iExpectedType==EUintListUntilEnd)
+ {
+ // Query is complete - got all the attributes and we have finished in the middle
+ // of compiling the list of supported obex formats (we can not know in advance the
+ // no. of formats a remote device supports)
+ iObserver.RemoteBtObexQueryResult(KErrNone,iRemoteObexPort,iFullComplianceWithObexObjectPush,iObexObjectPushProfileVersion,iSupportedFormatsList);
+ HaltQuery();
+ }
+ else
+ {
+ // See if we should continue looking at the attributes for this record
+ if (iNotThisSdpRecord //there was a problem with attribute UUIDs and Uints
+ || (iExpectedType==EAnythingUntilNextExpectedValue) //we should have got whatever we expected next!
+ || (iExpectedType==EUint) //we should always get the Uint we are looking for
+ || (iExpectedType==EUUID) //we should always get the UUID we are looking for
+ || (aError)) //an error has occured, note that not finding an attribute does NOT result in KErrNotFound
+ {
+ // following is an exception to the rules above...
+ if (iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId) //it OK to not find what we expect in for attribute because attribute is not mandatory
+ {
+ // therefore everything is OK with the record so far - Request the next attribute
+ RequestNextAttribute(aHandle);
+ }
+ else
+ {
+ // this record didn't satisfy our query so try the next one
+ TRAPD(err,iSdpAgent->NextRecordRequestL()); //results in call to NextRecordRequestComplete()
+ if (err)
+ {
+ HaltQueryWithError(err);
+ }
+ }
+ }
+ else
+ {
+ // Everything is OK with the record so far - Request the next attribute
+ RequestNextAttribute(aHandle);
+ }
+ }
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildUUIDL(const TUUID& aUUID)
+/**
+ * Got a UUID attribute component. Check it is what we expected to receive. Get the next attribute component.
+ *
+ * @param aUUID The Bluetooth UUID of attribute
+ */
+ {
+ if(iNotThisSdpRecord
+ || iExpectedType==EAnythingUntilEnd) //don't care what we get for current attribute anymore!
+ {
+ // Not interested in this UUID
+ return this;
+ }
+
+ if (iExpectedType==EAnythingUntilNextExpectedValue)
+ {
+ if (iNextExpectedType==EUUID && aUUID==TUUID(iNextExpectedValue))
+ {
+ // Got the "next" value we are looking for so set current expected to "next"
+ // and carry on!
+ NextAttributeComponent();
+ }
+ else
+ {
+ // Not interested in this UUID
+ return this;
+ }
+ }
+
+ iCurrentUUID = aUUID;
+
+ if (iExpectedType!=EUUID)
+ {
+ if (iExpectedType==EUintPossible)
+ {
+ // It was possible to receive a Uint but we got a UUID instead, this is
+ // perfectly valid. Go the the next component and call this method
+ // recursively to ensure next component is processed with this UUID
+ NextAttributeComponent();
+ BuildUUIDL(aUUID);
+ return this; //to prevent us missing an attribute
+ }
+ else
+ {
+ // Shouldn't have received a UUID!
+ iNotThisSdpRecord = ETrue;
+ }
+ }
+ else if (iExpectingSpecific)
+ {
+ if (aUUID != TUUID(iExpectedValue))
+ {
+ // Havn't got what we expected!
+ iNotThisSdpRecord = ETrue;
+ }
+ else if (aUUID==TUUID(KObexObjectPushUUIDValue) && iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId)
+ {
+ // remote device complies fully with obex object push profile
+ iFullComplianceWithObexObjectPush = ETrue;;
+ }
+ }
+ else
+ {
+ // When dealing with UUIDs we are ALWAYS expecting a specific UUID.
+ // If we end up here then something has gone wrong
+ iNotThisSdpRecord = ETrue;
+ }
+
+ NextAttributeComponent();
+ return this;
+ }
+
+
+MSdpElementBuilder* CObexSdpUtils::BuildUintL(const TDesC8& aUint)
+/**
+ * Got a Uint attribute componet. Check it is what we expected to receive. Get the next attribute component.
+ *
+ * @param aUint Attribute as Uint contained in a descriptor
+ */
+ {
+ if(iNotThisSdpRecord
+ || iExpectedType==EAnythingUntilEnd) //don't care what we get for current attribute anymore!
+ {
+ // Not interested in this Uint
+ return this;
+ }
+
+ TUint32 uint = UInt32(aUint);
+
+ if (iExpectedType==EAnythingUntilNextExpectedValue)
+ {
+ if (iNextExpectedType==EUint && uint==iNextExpectedValue)
+ {
+ // Got the "next" value we are looking for so set current expected to "next"
+ // and carry on!
+ NextAttributeComponent();
+ }
+ else
+ {
+ // Not interested in this Uint
+ return this;
+ }
+ }
+
+ if (iExpectedType==ENoComponent || iExpectedType==EUUID)
+ {
+ // We should have not found a Uint
+ iNotThisSdpRecord = ETrue;
+ }
+ else if (iExpectingSpecific)
+ {
+ if (uint != iExpectedValue)
+ {
+ // Havn't got what we expected!
+ iNotThisSdpRecord = ETrue;
+ }
+ }
+ else
+ {
+ // Not expecting a specific value so the value we've got is unknown so it's
+ // a piece of info we are trying to discover
+
+ if (iCurrentAttributeId==KProtocolDescriptorListAttributeId && iCurrentUUID==TUUID(K_RFCOMM_UUID_Value))
+ {
+ // Got the port number!
+ iRemoteObexPort = uint;
+ }
+ else if (iCurrentAttributeId==KBluetoothProfileDescriptorListAttributeId && iCurrentUUID==TUUID(KObexObjectPushUUIDValue))
+ {
+ // Got the Obex Object Push Profile Version
+ iObexObjectPushProfileVersion = uint;
+ }
+ else if (iCurrentAttributeId==KSupportedFormatsListAttributeId && iCurrentUUID==TUUID(NULL))
+ {
+ // Got an Obex format supported by the remote device
+ TInt length = iSupportedFormatsList.Length();
+ if (length<KMaxObexSupportedFormats)
+ {
+ iSupportedFormatsList.SetLength(length+1);
+ iSupportedFormatsList[length]=(TUint8)uint;
+ }
+ else
+ {
+ HaltQueryWithError(KErrTooBig);
+ }
+ return this; //don't go to next expected component because a list doesn't
+ //have a defined length
+ }
+ }
+
+ NextAttributeComponent();
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildUnknownL(TUint8 /*aType*/, TUint8 /*aSizeDesc*/, const TDesC8& /*aData*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildNilL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildIntL(const TDesC8& /*aInt*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildBooleanL(TBool /*aBool*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildStringL(const TDesC8& /*aString*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildDESL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildDEAL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::StartListL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::EndListL()
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+MSdpElementBuilder* CObexSdpUtils::BuildURLL(const TDesC8& /*aURL*/)
+/**
+ * Not required for our SDP query, so we provide an empty implementation.
+ */
+ {
+ return this;
+ }
+
+TUint32 CObexSdpUtils::UInt32(const TDesC8& aUint)
+/**
+ * Convert descriptor containing Uint to a numeric Uint.
+ *
+ * The Uints can be 1, 2, 4, 8 or 16 bytes long in theory. It is doubtful
+ * that the 8 or 16 byte variety would actually be used in our Obex Object
+ * Push Service case. The attribute Id will always be a 2 byte Uint but it
+ * is unclear from the spec what size the the port number should be. It is
+ * implied that it should only be 1 byte but in the EPOC implementation, it
+ * is returned as whatever size is it registered as (there is no type/size
+ * checking carried out in the SDP server).
+ *
+ * @param aUint The Uint as a descriptor
+ */
+ {
+ TUint32 uint=0;
+
+ switch(aUint.Length())
+ {
+ case 1:
+ uint = (TUint32)aUint[0];
+ break;
+ case 2:
+ uint = (TUint32)(BigEndian::Get16(aUint.Ptr()));
+ break;
+ case 4:
+ default:
+ // for our query, if larger Uids are received they may be truncated
+ uint = BigEndian::Get32(aUint.Ptr());
+ break;
+ }
+
+ return uint;
+ }
+
+void CObexSdpUtils::HaltQuery()
+/**
+ * Halt the current SDP query by destroying the CSdpAgent.
+ */
+ {
+ iSdpAgent->Cancel();
+ iIsActiveSDPQuery = EFalse;
+ }
+
+void CObexSdpUtils::HaltQueryWithError(TInt aError)
+/**
+ * Halt the current SDP query by destroying the CSdpAgent. Inform MObexSdpUtilsObserver
+ * observer that query has completed with an error.
+ *
+ * @param aError The error code
+ */
+ {
+ iObserver.RemoteBtObexQueryResult(aError,KErrNotFound,EFalse,KErrNotFound,iSupportedFormatsList);
+ HaltQuery();
+ }
+
+void CObexSdpUtils::RequestNextAttribute(TSdpServRecordHandle aHandle)
+/**
+ * Get next attribute if we are expecting another attribute.
+ *
+ * @param aHandle The handle to the service records
+ */
+ {
+ iAttribute++;
+ if (iAttribute >= iAttributeArray->Count())
+ {
+ // Shouldn't be requesting another attribute as have no expectation
+ // for another attribute
+ HaltQueryWithError(KErrNotFound);
+ }
+ else
+ {
+ iCurrentUUID=TUUID(NULL);
+
+ iAttributeComponent=-1;
+ NextAttributeComponent(); //will increment iAttributeComponent
+
+ iCurrentAttributeId = (*iAttributeArray)[iAttribute]->iAttributeId;
+ TRAPD(err,iSdpAgent->AttributeRequestL(this,aHandle,TSdpAttributeID(iCurrentAttributeId))); //results in multiple calls to BuildUUIDL() & BuildUintL(), ending with 1 call to AttributeRequestComplete()
+ if (err)
+ {
+ HaltQueryWithError(err);
+ }
+ }
+ }
+
+void CObexSdpUtils::NextAttributeComponent()
+/**
+ * Get next attribute component if we are expecting another attribute component. Set
+ * internal expectations for the next attribute component we receive.
+ */
+ {
+ iAttributeComponent++;
+ if (iAttributeComponent >= (*iAttributeArray)[iAttribute]->iAttributeComponentArray->Count())
+ {
+ // Shouldn't be requesting another attribute component as we have no
+ // expectation for another attribute component
+ iNotThisSdpRecord = ETrue;
+ return;
+ }
+
+ // Set expectations for the next attribute component we receive
+ iExpectedType = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iType;
+ iExpectingSpecific = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iSpecificValue;
+ iExpectedValue = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent].iValue;
+
+ if (iExpectedType==EAnythingUntilNextExpectedValue)
+ {
+ iNextExpectedType = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent+1].iType;
+ iNextExpectedValue = (*(*iAttributeArray)[iAttribute]->iAttributeComponentArray)[iAttributeComponent+1].iValue;
+ }
+ }
+
+
+CObexSdpUtils::CAttribute::CAttribute()
+/**
+ * Simple constructor
+ */
+ {
+ }
+
+CObexSdpUtils::CAttribute::~CAttribute()
+/**
+ * Destructor deletes array of attributes
+ */
+ {
+ delete iAttributeComponentArray;
+ }