bluetooth/btsdp/agent/engine.cpp
changeset 0 29b1cd4cb562
--- /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 <es_sock.h>
+#include <btsdp.h>
+#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<TSdpServRecordHandle>(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;
+	}
+