diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btsdp/agent/requester.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btsdp/agent/requester.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,599 @@ +// 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 +#include +#include "ipcinternals.h" +#include "sdpconsts.h" +#include "sdputil.h" +#include "SDPDatabase.h" +#include "DataEncoder.h" +#include "SDPAttribute.h" +#include "EncoderVisitor.h" +#include "ExtractorVisitor.h" +#include "agtypes.h" +#include "agutil.h" +#include "agconsts.h" +#include "ProtocolWrapper.h" +#include "requester.h" +#include "engine.h" + + +void CSdpRequesterBase::NewRequestL(const TBTDevAddr& aRemDev) +/** + Attempts to connect to the remote device. + This is the first thing to do on receipt of a new request. +**/ + { + //If active, must only be active on the idle timer in idle state. + __ASSERT_ALWAYS(iState == EIdle || !IsActive(), AgPanic(ESdpAgentTwoRequests)); + if(iState != EDisconnected && aRemDev == iRemoteAddress) + {// Already Connected. Push us through to process state. + Cancel(); //NB iState should be EIdle if we are here + iState = EDisconnected; + TRequestStatus* pS = &iStatus; + User::RequestComplete(pS, KErrNone); + SetActive(); + return; + } + iRetryCount = 0; + iRemoteAddress = aRemDev; + ConnectL(); + } + +void CSdpRequesterBase::ConnectL() +/** + Ask to connect to remote device address currently set-up in this active object. + Note that we ensure that we are disconnected first...we could + be connected to a different device. +**/ + { + Disconnect(); + User::LeaveIfError(iIdleTimer.CreateLocal()); + User::LeaveIfError(iRequester.Open(iSdpSession)); + iRequester.Connect(iRemoteAddress, iStatus); + SetActive(); + } + +void CSdpRequesterBase::Disconnect() +/** + Bring down a currently existing connection. +**/ + { + iState = EDisconnected; + iIdleTimer.Close(); + iRequester.Close(); + } + +void CSdpRequesterBase::RetrieveResponseL() +/** + The initial response to a search or attribute request contains + a buffer size. To obtain the actual result, the response buffer + has to be made big enough, and then have the actual result copied + into it from the server. + RetrieveResponseL() does just this. +**/ + { + if(!iResponseHBuf || + iResponseHBuf->Size() < iResultSize) + { + delete iResponseHBuf; + iResponseHBuf = 0; + iResponseHBuf = HBufC8::NewMaxL(iResultSize); + } + iResponse.Set(iResponseHBuf->Des()); + + User::LeaveIfError(iRequester.RetrieveResult(iResponse)); + } + +CSdpRequesterBase:: CSdpRequesterBase(RSdpSession& aSdpSession, CSdpAgentEng& aParent) +: CActive(CActive::EPriorityStandard), + iSdpSession(aSdpSession), + iParent(aParent), + iRemoteAddress(0), + iResponse(0,0) +/** + Constructor: This is where ESock session and the CSdpAgentEng parent are provided. + Other member variables are given default values. +**/ + { + } + +CSdpRequesterBase::~CSdpRequesterBase() +/** + Destructor: Deletes buffer for responses. Ensures we are disconnected. +**/ + { + delete iResponseHBuf; + Disconnect(); + } + + +void CSdpRequesterBase::RunL() +/** + Runs state machine. + If no errors then: + @verbatim + state disconnected -> state requesting + The request was stored before + connection, and now is made. + state requesting -> state idle. + The request has completed. See + if any further requests come in + over a brief period. + state idle -> state disconnected + if idle state interrupted by new request, + this is notional - the RunL is immediately + called again to set state to requesting + and issue the actual request. + @endverbatim +**/ + { + if(iStatus != KErrNone) + { + if(iState == ERequesting && + ++iRetryCount < KRetryLimit) + {// Comms Error in request. Try to connect again. + ConnectL(); + return; + } + User::Leave(iStatus.Int()); + } + + switch(iState) + { + case EDisconnected: //Now connected + iState = ERequesting; + IssueRequestL(); + break; + case ERequesting: // Actual request complete + iState = EIdle; + iIdleTimer.After(iStatus, KSdpAgentIdleTimeout); + SetActive(); + //RequestCompleteL() might DELETE us, + //so it must be called last (before 'return'). + RequestCompleteL(); + return; + case EIdle: + Reset(); + break; + } + } +void CSdpRequesterBase::Reset() + { + Disconnect(); //Causes iState to become EDisconnected. + delete iResponseHBuf; //A chance to delete this - it may have become quite large + iResponseHBuf = 0; + iRetryCount = 0; + } + +void CSdpRequesterBase::DoCancel() +/** + Cancels current asynchronous request. + This will be either an SDP query being performed + through ESock or, if this CSdpRequesterBase is + in the idle state, a timer (set-up to perform + a disconnection after a defined length of time). +**/ + { + if(iState == EIdle) + { + __ASSERT_DEBUG(iIdleTimer.Handle(), AgPanic(ESdpAgentBadStateAtCancel)); + if(iIdleTimer.Handle()) + { + iIdleTimer.Cancel(); + } + } + else + { + __ASSERT_DEBUG(iRequester.IsOpen(), AgPanic(ESdpAgentBadStateAtCancel)); + if(iRequester.IsOpen()) + { + iRequester.Cancel(); + } + } + } + +CSdpSearchRequester* CSdpSearchRequester::NewL(RSdpSession& aSdpSession, CSdpAgentEng& aParent) +/** + Standard NewL method. + Note: need to pass the client side object of the SDP service session, + and the CSdpA.gentEng parent which is managing this CSdpAttributeRequester + active object. +**/ + { + CSdpSearchRequester* self = new(ELeave) CSdpSearchRequester(aSdpSession, aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +CSdpSearchRequester::~CSdpSearchRequester() +/** + Destructor +**/ + { + Cancel(); + } + +void CSdpSearchRequester::SearchRequestL(const TBTDevAddr& aRemoteDev, + CSdpSearchPattern& aUUIDFilter, + TUint16 aMaxRecCount, + const TDesC8& aContState) +/** + Sets up appropriate member variables, then attempts to connect to remote device. + (If connection is made the state machine takes over to make the actual + search request.) +**/ + { + __ASSERT_DEBUG(iUUIDFilter==NULL,AgPanic(ESdpAgentTwoRequests)); + iUUIDFilter = &aUUIDFilter; + iMaxRecCount = aMaxRecCount; + iContState.Set(aContState); + NewRequestL(aRemoteDev); + } + + +CSdpSearchRequester::CSdpSearchRequester(RSdpSession& aSdpSession, CSdpAgentEng& aParent) +:CSdpRequesterBase(aSdpSession, aParent), + iContState(0,0) +/** + Constructor: Adds this CSdpAttributeRequester to Active Scheduler. + Sets parent, and passes client side object for + session with SDP server (currently ESock). + Sets up cont state member to default 0 length descriptor. +**/ + { + CActiveScheduler::Add(this); + } + +void CSdpSearchRequester::ConstructL() +/** + Currently does nothing. +**/ + { + } + + +void CSdpSearchRequester::IssueRequestL() +/** + Requester is now connected. + Called down from requester base class. + Time to actually issue the request +**/ + { + iRequester.SdpSearchRequestL(iResultSize, + *iUUIDFilter, + iMaxRecCount, + iContState, + iStatus); + SetActive(); + } + +void CSdpSearchRequester::RequestCompleteL() +/** + Response parameter format is + @verbatim + Total service record count TUint16 + Current service record count TUint16 + Service Record Handles unadorned list of TUint32's + Continuation State 1 + 0-16 bytes + @endverbatim + +**/ + { + RetrieveResponseL(); // Use base class to suck result over IPC. Loads iResponse + iUUIDFilter = NULL; // Indicates request is complete + const TInt KRspTotalRecCountOffset = 0; + const TInt KRspCurrentRecCountOffset = 2; + const TInt KRspRecHandlesOffset = 4; + const TInt KMinRspLength = 5; // From Spec. 2 + 2 + 1 + + if(iResponse.Length() < KMinRspLength) + User::Leave(KErrSdpBadResultData); +// + TUint16 totalRecCount = BigEndian::Get16(&iResponse[KRspTotalRecCountOffset]); + TUint16 currentRecCount = BigEndian::Get16(&iResponse[KRspCurrentRecCountOffset]); + if(totalRecCount < currentRecCount) + User::Leave(KErrSdpBadResultData); + TInt byteCount = currentRecCount*sizeof(TSdpServRecordHandle); + if(byteCount + KRspRecHandlesOffset + 1 > iResponse.Length()) + User::Leave(KErrSdpBadResultData); +// + TInt contLen = iResponse[KRspRecHandlesOffset+byteCount]; + if(contLen > KSdpContinuationStateMaxLength || + KRspRecHandlesOffset + byteCount + 1 + contLen != iResponse.Length()) + User::Leave(KErrSdpBadResultData); +// + TPtrC8 recHandles(&iResponse[KRspRecHandlesOffset], byteCount); + TPtrC8 contState; + if(iResponse.Length()>KRspRecHandlesOffset + 1 + byteCount && contLen) + { + contState.Set(&iResponse[KRspRecHandlesOffset + 1 + byteCount], contLen); + } + else + { + contState.Set(0,0); + } + + //Call this last: it may well end up calling back into this + //CSdpSearchRequester object. + iParent.HandleServiceSearchResponseL(totalRecCount, + currentRecCount, + recHandles, + contState); + } + +void CSdpSearchRequester::Reset() +/** + Called when base requester idle timer expires, + and when an error is reported + Cleans up and makes sure we really are disconnected. +**/ + { + CSdpRequesterBase::Reset(); + } + +void CSdpSearchRequester::DoCancel() +/** + Cancels current asynchronous requests. + This could be with the Sdp server (currently ESOCK) + or if this CSdpRequesterBase is in idle state + it cancels the time out timer (set-up to perform + a disconnection after a defined length of time). +**/ + { + CSdpRequesterBase::DoCancel(); + + // We only need to reset iUUIDFilter if the Cancel + // has been called externally. + // If the state is EIdle, either iUUIDFilter is already NULL + // or the Cancel is being called internally by NewRequestL + if(iState != EIdle) + { + iUUIDFilter = NULL; // Indicates request is complete + } + } + +TInt CSdpSearchRequester::RunError(TInt aError) +/** + Called automatically when a RunL leaves. + Calls appropriate error handling function in the CSdpAgentEng parent. +**/ + { + /* + Make sure we are not active. RunL can leave after SetActive is called. + */ + Cancel(); + + /* + Indicates request has completed - albeit with an error. + Do NOT rely on Cancel to have called DoCancel to do this. + It will only do this if the CSdpSearchRequester is active. + */ + iUUIDFilter = NULL; + + /* + We may or may not be disconnected. + We may not be able to be re-connect + We need to be in a known state after this call. + So make sure we are disconnnected, + and leave it to the user to try to + reconnect. He will do this automatically + by trying to start a new request. + */ + Reset(); + + iParent.HandleServiceSearchError(aError); + return KErrNone; + } + + +CSdpAttributeRequester* CSdpAttributeRequester::NewL(RSdpSession& aSdpSession, CSdpAgentEng& aParent) +/** + Standard NewL method. + Note: need to pass the client side object of the SDP service session, + and the CSdpA.gentEng parent which is managing this CSdpAttributeRequester + active object. +**/ + { + CSdpAttributeRequester* self = new(ELeave) CSdpAttributeRequester(aSdpSession, aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +CSdpAttributeRequester::~CSdpAttributeRequester() +/** + Destructor +**/ + { + Cancel(); + } + +void CSdpAttributeRequester::AttributeRequestL(const TBTDevAddr& aRemoteDev, + TSdpServRecordHandle aHandle, + TUint16 aMaxAttrByteCount, + CSdpAttrIdMatchList& aMatchList, + const TDesC8& aContState) +/** + Sets up appropriate member variables, then attempts to connect to remote device. + (If connection is made the state machine takes over to make the actual + attribute request.) +**/ + { + __ASSERT_DEBUG(iMatchList==NULL,AgPanic(ESdpAgentTwoRequests)); + iHandle = aHandle; + iMaxAttrByteCount = aMaxAttrByteCount; + iMatchList = &aMatchList; + iContState.Set(aContState); + NewRequestL(aRemoteDev); + } + + +CSdpAttributeRequester::CSdpAttributeRequester(RSdpSession& aSdpSession, CSdpAgentEng& aParent) +:CSdpRequesterBase(aSdpSession, aParent), + iContState(0,0) +/** + Constructor: Adds this CSdpAttributeRequester to Active Scheduler. + Sets parent, and passes client side object for + session with SDP server (currently ESock). + Sets up cont state member to default 0 length descriptor. +**/ + { + CActiveScheduler::Add(this); + } + +void CSdpAttributeRequester::ConstructL() +/** + Currently blank. +**/ + { + } + + +void CSdpAttributeRequester::IssueRequestL() +/** + Requester is now connected. + Called down from requester base class. + Time to actually issue the request +**/ + { + iRequester.SdpAttributeRequestL(iResultSize, + iHandle, + iMaxAttrByteCount, + *iMatchList, + iContState, + iStatus); + SetActive(); + } + +void CSdpAttributeRequester::RequestCompleteL() +/** + Response parameter format is + @verbatim + byte count of attr list TUint16 + Attribute ID & Value DES + Continuation State 1 + 0-16 bytes + @endverbatim + +**/ + { + RetrieveResponseL(); // Use base class to suck result over IPC. Loads iResponse + iMatchList = NULL; // Indicates request is complete + const TInt KRspTotalCountOffset = 0; + const TInt KRspAttributeListOffset = 2; + const TInt KMinRspLength = 5; // From Spec. 2 + 2 + 1 + + if(iResponse.Length() < KMinRspLength) + User::Leave(KErrSdpBadResultData); +// + TUint16 byteCount = BigEndian::Get16(&iResponse[KRspTotalCountOffset]); + if(byteCount + KRspAttributeListOffset + 1 > iResponse.Length()) + User::Leave(KErrSdpBadResultData); +// + TInt contLen = iResponse[KRspAttributeListOffset+byteCount]; + if(contLen > KSdpContinuationStateMaxLength || + KRspAttributeListOffset + byteCount + 1 + contLen != iResponse.Length()) + User::Leave(KErrSdpBadResultData); +// + TPtrC8 attrList(&iResponse[KRspAttributeListOffset], byteCount); + TPtrC8 contState; + if(iResponse.Length()>KRspAttributeListOffset + 1 + byteCount && contLen) + { + contState.Set(&iResponse[KRspAttributeListOffset + 1 + byteCount], contLen); + } + else + { + contState.Set(0,0); + } + + //Call this last: it may well end up calling back into this + //CSdpAttributeRequester object. + iParent.HandleAttributeResponseL(attrList, contState); + } + +void CSdpAttributeRequester::Reset() +/** + Called when base requester idle timer expires, + and when an error is reported + Cleans up and makes sure we really are disconnected. +**/ + { + CSdpRequesterBase::Reset(); + } + +void CSdpAttributeRequester::DoCancel() +/** + Cancels current asynchronous requests. + This could be with the Sdp server (currently ESOCK) + or if this CSdpRequesterBase is in idle state + it cancels the time out timer (set-up to perform + a disconnection after a defined length of time). +**/ + { + CSdpRequesterBase::DoCancel(); + + // We only need to reset iMatchList if the Cancel + // has been called externally. + // If the state is EIdle, either iMatchList is already NULL + // or the Cancel is being called internally by NewRequestL + if(iState != EIdle) + { + iMatchList = NULL; // Indicates request is complete + } + } + +TInt CSdpAttributeRequester::RunError(TInt aError) +/** + Called automatically when a RunL leaves. + Calls appropriate error handling function in the CSdpAgentEng parent. +**/ + { + /* + Make sure we are not active. RunL can leave after SetActive is called. + */ + Cancel(); + + /* + Indicates request has completed - albeit with an error. + Do NOT rely on Cancel to have called DoCancel to do this. + It will only do this if the CSdpAttributeRequester is active + and the idle timer is not running. + */ + iMatchList = NULL; //indicates request has completed - albeit with an error + + /* + We may or may not be disconnected. + We may not be able to be re-connect + We need to be in a known state after this call. + So make sure we are disconnnected, + and leave it to the user to try to + reconnect. He will do this automatically + by trying to start a new request. + */ + Reset(); + + iParent.HandleAttributeError(aError); + return KErrNone; + } + + + +