diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btsdp/agent/engine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btsdp/agent/engine.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,646 @@ +// Copyright (c) 2000-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: +// + +#include +#include +#include "engine.h" +#include "agutil.h" +#include "requester.h" +#include "agtypes.h" +#include "agconsts.h" +#include "sdpconsts.h" +#include "sdputil.h" +#include "SDPDatabase.h" +#include "SDPAttribute.h" +#include "ExtractorVisitor.h" +#include "BuilderVisitor.h" + +void AgPanic(TSdpAgPanic aCode) + { + User::Panic(_L("SdpAgent"),aCode); + } + +/** Adapter class. + + Very similar to CSdpServRecord ... this class does it + the right way. Perhaps CSdpServRecord should just use + one of these to build attributes. */ +NONSHARABLE_CLASS(CSdpAttrBuilder) : public CBase, public MSdpElementBuilder + { +public: + CSdpAttrBuilder(MSdpAgentNotifier& aNotifier, TSdpServRecordHandle aHandle); + ~CSdpAttrBuilder(); + + // MSdpElementBuilder interface definition + virtual MSdpElementBuilder* BuildUintL(const TDesC8& aUint); + virtual MSdpElementBuilder* BuildDESL(); + virtual MSdpElementBuilder* StartListL(); + virtual MSdpElementBuilder* EndListL(); + + void CompleteAttribute(); + +private: + MSdpAgentNotifier& iNotifier; + CSdpAttr* iCurrentAttr; + TSdpServRecordHandle iHandle; + TBool iBuiltDES; + }; + +CSdpAttrBuilder::CSdpAttrBuilder(MSdpAgentNotifier& aNotifier, TSdpServRecordHandle aHandle) + :iNotifier(aNotifier), iHandle(aHandle) + { + } + +CSdpAttrBuilder::~CSdpAttrBuilder() + { + delete iCurrentAttr; + } + +// MSdpElementBuilder interface definition +MSdpElementBuilder* CSdpAttrBuilder::BuildUintL(const TDesC8& aUint) + { + if(!iBuiltDES) + User::Leave(KErrGeneral); + if (aUint.Length()!=2) + User::Leave(KErrSdpBadAttributeId); + + CompleteAttribute(); + TSdpAttributeID id = BigEndian::Get16(&aUint[0]); + iCurrentAttr = CSdpAttr::NewL(id, this); + return iCurrentAttr; + } + +MSdpElementBuilder* CSdpAttrBuilder::BuildDESL() + {// Start of attribute list + if(iBuiltDES) + User::Leave(KErrGeneral); + iBuiltDES = ETrue; + return this; + } + +MSdpElementBuilder* CSdpAttrBuilder::StartListL() + {// Start of attribute list + if(!iBuiltDES) + User::Leave(KErrGeneral); + return this; + } + +MSdpElementBuilder* CSdpAttrBuilder::EndListL() + { + if(!iBuiltDES) + User::Leave(KErrGeneral); + CompleteAttribute(); + return this; + } + +void CSdpAttrBuilder::CompleteAttribute() + { + if(!iCurrentAttr) + { + return; + } + iNotifier.AttributeRequestResult(iHandle, iCurrentAttr->AttributeID(), iCurrentAttr->ReleaseValue()); + delete iCurrentAttr; + iCurrentAttr = 0; + } + + +/************************************************************************/ +// +// SDP agent client API (user friendly style) +// +/************************************************************************/ + +/** +This virtual function allows the M- classes to be extended in future in a binary +compatible way by providing a method that clients can override in future to +allow extra callbacks to be made via aObject. +*/ +EXPORT_C void MSdpAgentNotifier::MSAN_ExtensionInterfaceL(TUid /*aInterface*/, void*& aObject) + { + aObject = NULL; + } + +// API Facade + +EXPORT_C CSdpAgent* CSdpAgent::NewL(MSdpAgentNotifier& aNotifier, const TBTDevAddr& aDevAddr) +/** Creates a new SDP Agent object. + +@param aNotifier Interface implemented by the query requester, which the agent +calls asynchronously to pass responses to queries +@param aDevAddr The Bluetooth address of the remote device to query +@return New service discovery agent +@capability LocalServices */ + { + CSdpAgent* self = CSdpAgent::NewLC(aNotifier, aDevAddr); + CleanupStack::Pop(); + return self; + } + +EXPORT_C CSdpAgent* CSdpAgent::NewLC(MSdpAgentNotifier& aNotifier, const TBTDevAddr& aDevAddr) +/** Creates a new SDP Agent object. + +Allocate and construct a service discovery agent, leaving the object on the +cleanup stack. + +@param aNotifier Interface implemented by the query requester, which the agent +calls asynchronously to pass responses to queries +@param aDevAddr The Bluetooth address of the remote device to query +@return New service discovery agent +@capability LocalServices */ + { + CSdpAgent* self = new(ELeave) CSdpAgent(); + CleanupStack::PushL(self); + self->ConstructL(aNotifier, aDevAddr); + return self; + } + + +EXPORT_C CSdpAgent::~CSdpAgent() +/** +**/ +/** Destructor. */ + { + delete iAgentEngine; + } + + +void CSdpAgent::ConstructL(MSdpAgentNotifier& aNotifier, TBTDevAddr aDevAddr) +/** +**/ + { + iAgentEngine = CSdpAgentEng::NewL(aNotifier, aDevAddr); + } + +CSdpAgent::CSdpAgent() +/** +**/ + { + } + +EXPORT_C void CSdpAgent::SetRecordFilterL(const CSdpSearchPattern& aUUIDFilter) +/** Sets the classes of service to query for on the remote device. + +Responses from the agent will only contain records for services that belong +to the classes listed in aUUIDFilter. Service classes are represented as unique +identifiers (UUIDs). + +@param aUUIDFilter A list of UUIDs that will be matched in SDP Service Search +Requests. The function takes a copy of the object. Any previous UUID list +is deleted. +@capability LocalServices */ + { + iAgentEngine->SetRecordFilterL(aUUIDFilter); + } + +EXPORT_C void CSdpAgent::SetAttributePredictorListL(const CSdpAttrIdMatchList& /*aMatchList*/) +/** This does nothing! +(It used to create a copy of an attribute match list supplied, and place it in the CSdpAgentEng object.) + +@param aMatchList Attribute - now unused +@capability LocalServices +@deprecated*/ + { + } + +EXPORT_C void CSdpAgent::NextRecordRequestL() +/** Gets a handle to the record for the next (or first) service on the remote device +that matches the service class filter previously set. + +The function is asynchronous: on completion, it calls NextRecordRequestComplete() +on the MSdpAgentNotifier interface passed in the NewL(). + +@see MSdpAgentNotifier::NextRecordRequestComplete() +@capability LocalServices */ + { + iAgentEngine->NextRecordRequestL(); + } + +EXPORT_C void CSdpAgent::AttributeRequestL(TSdpServRecordHandle aHandle, + TSdpAttributeID aAttrID) +/** Gets the specified attribute for a remote service. + +The function is asynchronous: on completion, it calls MSdpAgentNotifier::AttributeRequestComplete(). + + +If the attribute is found, the function passes it by also calling MSdpAgentNotifier::AttributeRequestResult(). + +@param aHandle The service for which to get the attribute, specified by its +record handle +@param aAttrID The ID of the attribute to get +@see MSdpAgentNotifier::AttributeRequestComplete() +@see MSdpAgentNotifier::AttributeRequestResult() +@capability LocalServices */ + { + CSdpAttrIdMatchList* list = CSdpAttrIdMatchList::NewL(); + CleanupStack::PushL(list); + list->AddL(TAttrRange(aAttrID)); + iAgentEngine->AttributeRequestL(aHandle, *list); + CleanupStack::PopAndDestroy(list); + } + +EXPORT_C void CSdpAgent::AttributeRequestL(TSdpServRecordHandle aHandle, + const CSdpAttrIdMatchList& aMatchList) +/** Gets the specified attributes for a remote service. + +The function is asynchronous: on completion, it calls MSdpAgentNotifier::AttributeRequestComplete(). + + +The function also calls MSdpAgentNotifier::AttributeRequestResult() for each +attribute found. + +@param aHandle The service for which to get the attribute, specified by its +record handle +@param aMatchList A list of attributes to get +@see MSdpAgentNotifier::AttributeRequestComplete() +@see MSdpAgentNotifier::AttributeRequestResult() +@capability LocalServices */ + { + iAgentEngine->AttributeRequestL(aHandle, aMatchList); + } + +EXPORT_C void CSdpAgent::AttributeRequestL(MSdpElementBuilder* aBuilder, + TSdpServRecordHandle aHandle, + TSdpAttributeID aAttrID) +/** Gets the specified attribute for a remote service. + +The function is asynchronous: on completion, it calls MSdpAgentNotifier::AttributeRequestComplete(). + + +If the attribute is found, the function calls aBuilder's interface to set +the attribute ID and value. + +@param aBuilder Object implementing the MSdpElementBuilder interface. It will +be called with each type found in the response. +@param aHandle The service for which to get the attribute, specified by its +record handle +@param aAttrID The ID of the attribute to get +@see MSdpAgentNotifier::AttributeRequestComplete() +@capability LocalServices */ + { + CSdpAttrIdMatchList* list = CSdpAttrIdMatchList::NewL(); + CleanupStack::PushL(list); + list->AddL(TAttrRange(aAttrID)); + iAgentEngine->AttributeRequestL(aBuilder, aHandle, *list); + CleanupStack::PopAndDestroy(list); + } + +EXPORT_C void CSdpAgent::AttributeRequestL(MSdpElementBuilder* aBuilder, + TSdpServRecordHandle aHandle, + const CSdpAttrIdMatchList& aMatchList) +/** Gets the specified attributes for a remote service. + +The function is asynchronous: on completion, it calls MSdpAgentNotifier::AttributeRequestComplete(). + + +As each attribute is found, the function calls aBuilder's interface to set +the attribute ID and value. + +@param aBuilder Object implementing the MSdpElementBuilder interface. It will +be called with each type found in the response. +@param aHandle The service for which to get the attribute, specified by its +record handle +@param aMatchList A list of attributes to get +@see MSdpAgentNotifier::AttributeRequestComplete() +@capability LocalServices */ + { + iAgentEngine->AttributeRequestL(aBuilder, aHandle, aMatchList); + } + +EXPORT_C void CSdpAgent::Cancel() +/**Attempts to cancel an SDP Agent request. + +Calls cancel on active objects making request. +Resets all variables associated with a request and its state. + +NB The cancel operation will not, and cannot stop results from an SDP query +being sent by the remote. +@capability LocalServices */ + { + iAgentEngine->Cancel(); + } + +/************************************************************************/ +// +// ENGINE for SDP agent client API +// +/************************************************************************/ + + +CSdpAgentEng* CSdpAgentEng::NewL(MSdpAgentNotifier& aNotifier, TBTDevAddr aDevAddr) +/** Creates a new SDP Agent Engine object. + +The SDP Agent Engine is the class for the data member of the SDP Agent +which actually does the work. (The SDP Agent is a facade class.) + +@param MSdpAgentNotifier Interface to user object that will receive +notifiactions of agent commands completing. +@param TBTDevAddr Address of the remote device the SDP agent will query. */ + { + CSdpAgentEng* self = new(ELeave) CSdpAgentEng(aNotifier, aDevAddr); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +CSdpAgentEng::~CSdpAgentEng() +/** Creates a new SDP Agent Engine object. + +The SDP Agent Engine is the class for the data member of the SDP Agent +which actually does the work. (The SDP Agent is a facade class.) + +@param MSdpAgentNotifier Interface to user object that will receive +notifiactions of agent commands completing. +@param TBTDevAddr Address of the remote device the SDP agent will query.*/ + { + + delete iUUIDFilter; + delete iMatchList; + delete iDefaultAttributeBuilder; + delete iParser; + delete iBuiltRecHandles; + + if (iSearchRequester) + { + iSearchRequester->Cancel(); + delete iSearchRequester; + } + + if (iAttributeRequester) + { + iAttributeRequester->Cancel(); + delete iAttributeRequester; + } + + iSdpSession.Close(); //DEFECT_FIX + } + + +void CSdpAgentEng::ConstructL() +/** Sets ip basic requirements for SDP Agent. + +Perform ipc connection with ESock. + +Create active objects which look after requests. + +Create a parser to parse responses. (A builder for the parser is provided at point of request.)*/ + { + TInt ret=iSdpSession.Connect(); + if(ret!=KErrNone && ret!=KErrAlreadyExists) + User::Leave(ret); + //iRequester.Open(ss); + + iSearchRequester = CSdpSearchRequester::NewL(iSdpSession, *this); + iBuiltRecHandles = new(ELeave) CArrayFixSeg(8); + + iParser = CElementParser::NewL(0); + iAttributeRequester = CSdpAttributeRequester::NewL(iSdpSession, *this); + } + +CSdpAgentEng::CSdpAgentEng(MSdpAgentNotifier& aNotifier, TBTDevAddr aDevAddr) +:iCancelCalled(EFalse), + iNotifier(aNotifier), + iRemoteAddress(aDevAddr) + + +/** Constructor: caches the results notifier and the remote device address to be used.*/ + { + } + +void CSdpAgentEng::SetRecordFilterL(const CSdpSearchPattern& aUUIDFilter) +/** This function creates a new COPY of a UUID filter from the UUID filter +supplied, and places it in the CSdpAgentEng object. + +If an old UUID filter list exists, it will be deleted. (A UUID filter is +a list of UUIDs that must be contained in a service record in a remote +database if the handle of that record is to be returned by a service search.) */ + { + delete iUUIDFilter; + iUUIDFilter = 0; + iUUIDFilter = CSdpSearchPattern::NewL(); + for(TInt i = 0;i < aUUIDFilter.Count(); ++i) + { + TUUID uuid = aUUIDFilter.At(i); + iUUIDFilter->AddL(uuid); + } + iSearchRequester->Cancel(); + iSearchRequestContState.Zero(); + iBuiltRecHandles->Reset(); + iServiceSearchState = EIdle; + } + +void CSdpAgentEng::NextRecordRequestL() +/** Retrieves the next record handle from the remote server, that matches +the UUID filter previously set by SetRecordFileterL(). On completion, +an upcall will occur on MSdpAgentNotifier::NextRecordRequestComplete(). +**/ + { + __ASSERT_ALWAYS(iUUIDFilter, AgPanic(ESdpAgRecordFilterNotSet)); + + iCancelCalled = EFalse; + if(TryToCompleteNextRecordRequest()) + { + return; + } + // Need to request some fresh results + iSearchRequester->SearchRequestL(iRemoteAddress, + *iUUIDFilter, + KSdpAgMaxResultCount, + iSearchRequestContState); + iServiceSearchState = ERequesting; + } + +void CSdpAgentEng::AttributeRequestL(TSdpServRecordHandle aHandle, + const CSdpAttrIdMatchList& aMatchList) +/** Retrieve attributes from a record on the remote server. + +On completion, an up call on MSdpAgentNotifier::AttributeRequestComplete +will occur. Each attribute found will be passed up through a call to +MSdpAgentNotifier::AttributeRequestResult. + +@param aHandle Record handle to retrieve attribute from +@param aMatchList List Attribute IDs to retrieve. */ + + { + delete iDefaultAttributeBuilder; + iDefaultAttributeBuilder = 0; + iDefaultAttributeBuilder = new(ELeave) CSdpAttrBuilder(iNotifier, aHandle); + AttributeRequestL(iDefaultAttributeBuilder, aHandle, aMatchList); + } + +void CSdpAgentEng::AttributeRequestL(MSdpElementBuilder* aBuilder, + TSdpServRecordHandle aHandle, + const CSdpAttrIdMatchList& aMatchList) +/** Retrieve attributes from a record on the remote server. + +On completion, an up call on MSdpAgentNotifier::AttributeRequestComplete +will occur. As attributes are found, calls the MSdpElementBuilder +class to describe the attribute. This will be of the form +n * [AttrID:AttrVal], where n is the number of attributes actually found. + +@param aBuilder Class implementing MSdpElementBuilder interface to handle the result +@param aHandle Record handle to retrieve attribute from +@param aMatchList List Attribute IDs to retrieve. */ + { + //Reset as appropriate + iCancelCalled = EFalse; + iAttributeRequestContState.Zero(); + delete iMatchList; + iMatchList = 0; + iMatchList = CSdpAttrIdMatchList::NewL(aMatchList); + iAttributeRequestHandle = aHandle; + iParser->Reset(aBuilder); + iAttributeRequester->Cancel(); + + SendAttributeRequestL(); + } + +void CSdpAgentEng::Cancel() + { + //Tell this object not to handle anymore upcalls (till new request is made) + iCancelCalled = ETrue; + + //Cancel active objects + iSearchRequester->Cancel(); + iAttributeRequester->Cancel(); + + //Reset Service Search Stuff + iSearchRequestContState.Zero(); + iBuiltRecHandles->Reset(); + iServiceSearchState = EIdle; + iNextRecHandle = 0; + iTotalRecCount = 0; + + //Reset Attribute Request Stuff + iAttributeRequestContState.Zero(); + } + +void CSdpAgentEng::HandleServiceSearchResponseL(TUint16 aTotalRecCount, + TUint16 aCurrentRecCount, + const TDesC8& aRecHandles, + const TDesC8& aContState) +/** Upcall from service search requester. */ + { + if(iCancelCalled) + return; + + __ASSERT_DEBUG(aCurrentRecCount * KSdpRecordHandleSize == aRecHandles.Length(), + AgPanic(ESdpAgServiceSearchResultError)); + iServiceSearchState = EResultsReceived; + iTotalRecCount = aTotalRecCount; + for(TInt i = 0; i < aCurrentRecCount; ++i) + { + iBuiltRecHandles->AppendL(BigEndian::Get32(&aRecHandles[i*KSdpRecordHandleSize])); + } + iSearchRequestContState = aContState; + + TryToCompleteNextRecordRequest(); + iServiceSearchState = EResultsReceived; + + } + +void CSdpAgentEng::HandleServiceSearchError(TInt aError) +/** Pass service search error up to notifier - the handle supplied +is simply a dummy ... the notifier needs to check for errors. */ + { + if(iCancelCalled) + return; + + iNotifier.NextRecordRequestComplete(aError, 0xffffffff, 0); + } + +void CSdpAgentEng::HandleAttributeResponseL(const TDesC8& aAttributeList, + const TDesC8& aContState) +/** Up Called by Attribute Request Active Object "RunL" +Parse out the attribute response, and handle accordingly. */ + { + if(iCancelCalled) + return; + + TBool moreExpected = iParser->BufferedParseL(aAttributeList); + + if(iDefaultAttributeBuilder && iParser->Builder() == iDefaultAttributeBuilder) + {// Could have just parsed an attr. Send it up (rather than wait till next iteration) + iDefaultAttributeBuilder->CompleteAttribute(); //calls iNotifier.AttributeRequestResult() + } + + if (aContState.Length() == 0) + { + if (moreExpected) + {// Parsers not done, but we have no cont state. Oh dear. + User::Leave(KErrUnderflow); + } + + iNotifier.AttributeRequestComplete(iAttributeRequestHandle, KErrNone); + } + else + { + iAttributeRequestContState = aContState; + SendAttributeRequestL(); + } + } + +void CSdpAgentEng::HandleAttributeError(TInt aError) +/** + Pass attribute error up to notifier. +**/ + { + if(iCancelCalled) + return; + + iNotifier.AttributeRequestComplete(iAttributeRequestHandle, aError); + } + +void CSdpAgentEng::SendAttributeRequestL() +/** Ask the active object which performs attribute requests to do just that. + +The parameters used for the request are those currently stored as data members +in this object. These will have been previousfly supplied by the user.*/ + { + iAttributeRequester->AttributeRequestL(iRemoteAddress, iAttributeRequestHandle, + KSdpAgMaxByteCount, *iMatchList, iAttributeRequestContState); + } + +TBool CSdpAgentEng::TryToCompleteNextRecordRequest() +/** Checks whether it is necessary or not to ask remote device for more records. + +SdpAgent collects as many of the remote device's record handles that conform to +the record filter as it can on each service search request sent. Thus the SdpAgent +will only perform a fresh service search if either the record filter has been changed +(=> iBuiltRecordHandles->Count() returns 0) or the remote device returned a non "null" +continuation state and all the record handles currently returned by the remote device +have been processed. + +@return ETrue if NOT necessary.*/ + { + if(iServiceSearchState == EResultsReceived) + { + if(iNextRecHandle < iBuiltRecHandles->Count()) + {//We've got an answer already + //N.B. The function call iNotifier.NextRecordRequestComplete must come last + //because it can call TryToCompleteNextRecordRequest() recursively. + iNotifier.NextRecordRequestComplete(KErrNone, iBuiltRecHandles->At(iNextRecHandle++), iTotalRecCount); + return ETrue; + } + else if(iSearchRequestContState.Length() == 0 || iNextRecHandle >= iTotalRecCount) + {// No more answers available + iNotifier.NextRecordRequestComplete(KErrEof, 0, 0); + return ETrue; + } + } + return EFalse; + } +