bluetoothengine/bteng/btengdiscovery/src/btengsdpquery.cpp
changeset 0 f63038272f30
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothengine/bteng/btengdiscovery/src/btengsdpquery.cpp	Mon Jan 18 20:28:57 2010 +0200
@@ -0,0 +1,360 @@
+/*
+* Copyright (c) 2006 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:  Helper class for performing SDP queries.
+*
+*/
+
+
+
+#include <btsdp.h>
+
+#include "btengsdpquery.h"
+#include "btengdiscovery.h"
+#include "btengsdpattrparser.h"
+#include "debug.h"
+
+
+/**  Array granularity for storing SDP attribute values (the number 
+  *  of attributes returned from an attribute search is expected 
+  *  to be small).
+  */
+const TInt KBTEngDefaultGranularity = 2;
+
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// C++ default constructor
+// ---------------------------------------------------------------------------
+//
+CBTEngSdpQuery::CBTEngSdpQuery( MBTEngSdpResultReceiver* aNotifier )
+:   iAttrValArray( KBTEngDefaultGranularity ),
+    iResultNotifier( aNotifier )
+    {
+    }
+
+
+// ---------------------------------------------------------------------------
+// Symbian 2nd-phase constructor
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::ConstructL()
+    {
+    TRACE_FUNC_ENTRY
+    iParser = CBTEngSdpAttrParser::NewL( &iAttrResArray );
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// NewL
+// ---------------------------------------------------------------------------
+//
+CBTEngSdpQuery* CBTEngSdpQuery::NewL( MBTEngSdpResultReceiver* aNotifier )
+    {
+    CBTEngSdpQuery* self = new( ELeave ) CBTEngSdpQuery( aNotifier );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Destructor
+// ---------------------------------------------------------------------------
+//
+CBTEngSdpQuery::~CBTEngSdpQuery()
+    {
+    TRACE_FUNC_ENTRY
+    Cancel();
+    delete iSdpAgent;
+    delete iParser;
+    iResultNotifier = NULL;
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::Cancel()
+    {
+    TRACE_FUNC_ENTRY
+    if( iQueryType != EQueryIdle )
+        {
+            // Only call the agent when a search is active, as it 
+            // involves some cleanup which is not necessary for every call.
+        iSdpAgent->Cancel();
+        iQueryType = EQueryIdle;
+        }
+    iRecHandleArray.Close();
+    iAttrResArray.Close();
+    iAttrValArray.ResetAndDestroy();
+    iRequestedAttrId = 0;
+    }
+
+
+// ---------------------------------------------------------------------------
+// Service query
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::RemoteSdpQueryL( const TBTDevAddr& aAddr, const TUUID& aService )
+    {
+    TRACE_FUNC_ENTRY
+    CheckSdpAgentL( aAddr );
+    if( iQueryType == EQueryIdle )
+        {
+        iQueryType = EServiceQuery;
+        iAttrResArray.Close();
+        iAttrValArray.ResetAndDestroy();
+        iRequestedAttrId = 0;
+        }
+    iRecHandleArray.Close();
+    CSdpSearchPattern* searchPattern = CSdpSearchPattern::NewL();
+    CleanupStack::PushL( searchPattern );
+    searchPattern->AddL( aService );
+    iSdpAgent->SetRecordFilterL( *searchPattern );  // Copies searchpattern.
+    iSdpAgent->NextRecordRequestL();
+    CleanupStack::PopAndDestroy( searchPattern );
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// Attribute query
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::RemoteSdpQueryL( const TBTDevAddr& aAddr, 
+    const TSdpServRecordHandle aHandle, const TSdpAttributeID aAttrId )
+    {
+    TRACE_FUNC_ENTRY
+    CheckSdpAgentL( aAddr );
+        // this could be part of a ServiceSearchAttribute query.
+    if( iQueryType == EQueryIdle )
+        {
+        iQueryType = EAttrQuery;
+        iRecHandleArray.Close();
+        }
+    iRequestedAttrId = aAttrId;
+    iAttrResArray.Close();
+    iAttrValArray.ResetAndDestroy();
+    iSdpAgent->AttributeRequestL( aHandle, aAttrId );
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// ServiceAttribute query
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::RemoteSdpQueryL( const TBTDevAddr& aAddr, const TUUID& aService, 
+    const TSdpAttributeID aAttrId )
+    {
+    TRACE_FUNC_ENTRY
+    if( iQueryType != EQueryIdle )
+        {
+        User::Leave( KErrInUse );
+        }
+    iQueryType = EServiceAttrQuery;
+    iRequestedAttrId = aAttrId;
+    iRecHandleArray.Close();
+    iAttrResArray.Close();
+    iAttrValArray.ResetAndDestroy();
+        // First query for the requested service.
+    RemoteSdpQueryL( aAddr, aService );
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// From class MSdpAgentNotifier.
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::NextRecordRequestComplete( TInt aError, 
+    TSdpServRecordHandle aHandle, TInt aTotalRecordsCount )
+    {
+    TRACE_FUNC_ENTRY
+    if( aError == KErrNone && aTotalRecordsCount > 0 )
+        {
+            // Store the result and wait for the next.
+        iRecHandleArray.Append( aHandle );
+        TRAP( aError, iSdpAgent->NextRecordRequestL() );
+        }
+    else if( aError == KErrEof )
+        {
+            // The last notification does not contain any result anymore.
+        if( iQueryType == EServiceAttrQuery )
+            {
+            if( iRecHandleArray.Count() > 0 )
+                {
+                    // Request the attribute for all matching service records.
+                TRAP( aError, RemoteSdpQueryL( iBDAddr, iRecHandleArray[ 0 ], 
+                                                iRequestedAttrId ) );
+                }
+            else
+                {
+                    // No matches, inform the client.
+                iQueryType = EQueryIdle;
+                iResultNotifier->ServiceAttributeSearchComplete( aHandle, 
+                                                                  iAttrResArray, 
+                                                                  aError );
+                }
+            }
+        else
+            {
+                // This was a ServiceSearch, inform our client of the result.
+            TInt recCount = iRecHandleArray.Count();
+            if( recCount > 0 )
+                {
+                    // KErrEof is used to indicate no matching records.
+                aError = KErrNone;
+                }
+            iQueryType = EQueryIdle;
+            iResultNotifier->ServiceSearchComplete( iRecHandleArray, recCount, 
+                                                     aError );
+            }
+        }
+    NotifyError( aError );
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// From class MSdpAgentNotifier.
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::AttributeRequestResult( TSdpServRecordHandle aHandle, 
+    TSdpAttributeID aAttrID, CSdpAttrValue* aAttrValue )
+    {
+    TRACE_FUNC_ENTRY
+        // Ownership of CSdpAttrValue is passed here. Store the object so 
+        // that the result array can point to string values rather than 
+        // copy them (the object is anyway already constructed).
+    iAttrValArray.Append( aAttrValue );
+    iParser->SetAttributeID( aAttrID );
+        // Adds the attribute values synchronously to the result array.
+    TRAP_IGNORE( aAttrValue->AcceptVisitorL( *iParser ) );
+    (void) aHandle;
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// From class MSdpAgentNotifier.
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::AttributeRequestComplete( TSdpServRecordHandle aHandle, 
+    TInt aError )
+    {
+    TRACE_FUNC_ENTRY
+    if( aError )
+        {
+        NotifyError( aError );
+        return;
+        }
+
+    if( iQueryType == EAttrQuery )
+        {
+            // This was a AttributeSearch, inform our client of the result.
+        iQueryType = EQueryIdle;
+        iResultNotifier->AttributeSearchComplete( aHandle, iAttrResArray, aError );
+        }
+    else if( iQueryType == EServiceAttrQuery )
+        {
+        TInt index = iRecHandleArray.Find( aHandle );
+        if( index <= KErrNotFound )
+            {
+            NotifyError( KErrCorrupt );
+            return;
+            }
+            // Pop the handle, since we don't need it anymore.
+        iRecHandleArray.Remove( index );
+        if( iRecHandleArray.Count() == 0 )
+            {
+                // This was the last result.
+            aError = KErrEof;
+            iQueryType = EQueryIdle;
+            }
+        iResultNotifier->ServiceAttributeSearchComplete( aHandle, iAttrResArray, 
+                                                          aError );
+        if( aError != KErrEof && iQueryType == EServiceAttrQuery )
+            {
+                // Request the attribute from the next matching record (if 
+                // not cancelled in the callback, hence the check for the
+                // querytype) This will reset the attribute result array.
+            TRAP( aError, RemoteSdpQueryL( iBDAddr, iRecHandleArray[ 0 ], 
+                                            iRequestedAttrId ) );
+            NotifyError( aError );  // Does nothing if there is no error.
+            }
+        }
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::NotifyError( TInt aError )
+    {
+    if( aError == KErrNone || aError == KErrEof || iResultNotifier == NULL )
+        {
+        return;
+        }
+    TSdpQueryType query = iQueryType;
+    iQueryType = EQueryIdle;    // Need to set this before calling clients, 
+                                // as they may call us straight again.
+    if( query == EServiceQuery )
+        {
+        iResultNotifier->ServiceSearchComplete( RSdpRecHandleArray(), 0, aError );
+        }
+    else if( query == EAttrQuery )
+        {
+        iResultNotifier->AttributeSearchComplete( 0, RSdpResultArray(), aError );
+        }
+    if( query == EServiceAttrQuery )
+        {
+        iResultNotifier->ServiceAttributeSearchComplete( 0, RSdpResultArray(), 
+                                                          aError );
+        }
+    }
+
+
+// ---------------------------------------------------------------------------
+// ?implementation_description
+// ---------------------------------------------------------------------------
+//
+void CBTEngSdpQuery::CheckSdpAgentL( const TBTDevAddr& aAddr )
+    {
+    if( !iSdpAgent || aAddr != iBDAddr )
+        {
+        delete iSdpAgent;
+        iSdpAgent = NULL;
+        iBDAddr = aAddr;
+        iSdpAgent = CSdpAgent::NewL( *this, iBDAddr );
+        }
+    else
+        {
+        iSdpAgent->Cancel();
+        }
+    if( iQueryType != EQueryIdle && iQueryType != EServiceAttrQuery )
+        {
+            // Another query is ongoing and has not been cancelled.
+        User::Leave( KErrInUse );
+        }
+    }