diff -r 000000000000 -r 72b543305e3a messagingappbase/obexmtms/obexmtm/obexutil/source/ObexSdpUtils.cpp --- /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 + + +// +// 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(1); //granularity of 1 + + TInt attrCount; + for (attrCount=0; attrCountiAttributeComponentArray = new(ELeave) CArrayFixFlat(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 (lengthCancel(); + 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; + }