bluetooth/btsdp/server/protocol/reqhandler.cpp
changeset 0 29b1cd4cb562
child 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btsdp/server/protocol/reqhandler.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,757 @@
+// 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 <bluetooth/logger.h>
+#include <btsdp.h>
+#include "reqhandler.h"
+#include "pduhandler.h"
+#include "listener.h"
+#include "sdpconsts.h"
+#include "SDPDatabase.h"
+#include "MAttributeVisitor.h"
+#include "ExtractorVisitor.h"
+#include "responsesizevisitor.h"
+#include "DataEncoder.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_SDP_SERVER);
+#endif
+
+// statics
+
+void SdpReqHandler::HandleL(CSdpDatabase &aDatabase, const TSdpPdu &aReqPdu, TSdpPdu &aRespPdu)
+	{
+	switch (aReqPdu.iPduId)
+		{
+	case EServiceSearchRequest: 
+		HandleServiceSearchL(aDatabase, aReqPdu, aRespPdu);
+		break;
+	case EServiceAttributeRequest:
+		HandleServiceAttributeL(aDatabase, aReqPdu, aRespPdu);
+		break;
+	case EServiceSearchAttributeRequest:
+		HandleServiceSearchAttributeL(aDatabase, aReqPdu, aRespPdu);
+		break;
+	default:
+		User::Leave(KErrArgument);
+		}
+	}
+
+#ifdef __FLOGGING__
+TInt SdpReqHandler::RunError(TInt aError, const TSdpPdu& aReqPdu, TSdpPdu &aRespPdu)
+#else
+TInt SdpReqHandler::RunError(TInt aError, const TSdpPdu& /*aReqPdu*/, TSdpPdu &aRespPdu)
+#endif
+/**
+	Send an appropriate error.
+
+	Parameter format is
+ @verbatim
+		Error Code					TUint16
+		Error Info					Variable (0 in 1.0B)
+ @endverbatim
+**/
+	{
+	TSdpErrorCodes code;
+	switch (aError)
+		{
+	case KErrNotSupported:
+		code = EInvalidSdpVersion;
+		break;
+	case KErrBadHandle:
+		code = EInvalidServiceRecordHandle;
+		break;
+	case KErrOverflow: 
+	case KErrUnderflow:
+	case KErrTooBig:
+		code = EInvalidPduSize;
+		break;
+	case KErrNotReady:
+	case KErrUnknown:
+	case KErrLocked:
+		code = EInvalidContinuationState;
+		break;
+	case KErrNoMemory:
+	case KErrHardwareNotAvailable:
+		code = EInsufficientResources;
+		break;
+	case KErrCorrupt:
+	case KErrArgument:
+	default:
+		code = EInvalidRequestSyntax;
+		break;
+		}
+	aRespPdu.iPduId = EErrorResponse;
+	aRespPdu.iParams.SetLength(2);
+	BigEndian::Put16(&aRespPdu.iParams[0], TUint16(code));
+
+	LOG1(_L("SdpReqHandler::RunError(%d)"), aError);
+	
+	return KErrNone;
+	}
+
+
+void SdpReqHandler::HandleServiceSearchL(CSdpDatabase &aDatabase, const TSdpPdu &aReqPdu, TSdpPdu &aRespPdu)
+/**
+	Handle Service Search request.
+	Request parameter format is
+ @verbatim
+		Service search pattern		DES
+		Max record handle count		TUint16
+		Continuation state			1 + 0-16 bytes
+ @endverbatim
+
+	Response parameter format is
+ @verbatim
+		Total record count			TUint16
+		Records in this response	TUint16
+		Record handle list			TUint32 * N
+		Continuation State			1 + 0-16 bytes
+ @endverbatim
+
+**/
+	{
+// Parse the request parameters
+	TPtr8 params(aReqPdu.iParams);
+	CSdpPDUHandler* pHandler = new 	(ELeave) CSdpPDUHandler();
+	CleanupStack::PushL(pHandler);
+	TInt maxTotalRecCount;
+	TInt len;
+	TInt rem;
+	TInt sentRecords=0;
+
+	CSizeAccumulator* collector = CSizeAccumulator::NewL();
+	CleanupStack::PushL(collector);
+
+	CSdpSearchPattern* pattern = pHandler->UUIDListLC(params, KRecHandleCountSize, len, rem, maxTotalRecCount);
+
+// first level checks for continuation parameter
+	TBool inContFlag = pHandler->ContinuationL(params, len, rem);
+
+	CResponseSizeVisitor::SizeRespSSL(aDatabase, *pattern, *collector);
+	CleanupStack::PopAndDestroy(/*pattern*/);
+
+	TInt fullSize = collector->HandleCount(); // we only have handles here
+	fullSize = Min(fullSize, maxTotalRecCount); // we may not be able to send all the handles anyway
+//	TInt16 localCRC = collector->CRC(); this is for attributes
+	if (inContFlag)
+		{
+		if (fullSize != (TInt)pHandler->FullLength()) User::Leave(KErrUnknown);	// continuation check
+		sentRecords = pHandler->ContinuationOffset(); // this is in count of handles, not bytes
+		if (fullSize <= sentRecords) User::Leave(KErrUnknown);	// continuation check
+//		if (localCRC != pHandler->ReqCRC()) User::Leave(KErrUnknown); continuation check for attributes
+		}
+	else
+		sentRecords = 0;
+
+/*
+	working out if we can send all or some of the data:
+	if there is a maxhandles outstanding, comply with that
+	if we can send all the data (handles) fine
+	if we can't send all the data, reduce the buffer by the continuation
+*/
+	TInt pduSize = fullSize - sentRecords;	// the count of handles left to send
+
+	// start with the buffer size less: the minimum allocated, the total and this time handle count size and the empty continuation header
+	TInt bufferUsableLen = aRespPdu.iParams.MaxLength() - ((KRspHandleCountSize * 2) + KContStateHeader);
+
+	TBool outContFlag;
+	if (bufferUsableLen < (pduSize * KSdpRecordHandleSize)) 
+		{// has to be a continuation
+		outContFlag = ETrue;
+		bufferUsableLen -= KSdpContinuationStateLength;	// we need the header now
+/*
+	when sending attributes (AR, SAS) make sure we don't leave a single byte
+	to be sent in the next continuation. Otherwise, reduce the payload this
+	time by 1 to make sure we comply with the specification and send a minimum
+	of two bytes in the response.
+*/
+		}
+	else
+		{
+		outContFlag = EFalse;
+// we can complete this request this time
+		}
+
+	TUint handleCount = bufferUsableLen >> 2;	// get the length in handle speak
+	pduSize = Min(pduSize, handleCount);
+
+// end of common second stage continuation processing
+
+	// Write the response packet
+	aRespPdu.iPduId = EServiceSearchResponse;
+	aRespPdu.iParams.SetMax();
+	TPtr8 responseHandles(0,0);
+	responseHandles.Set(&aRespPdu.iParams[0], 0, aRespPdu.iParams.MaxLength());
+	TInt oldLen = responseHandles.Length();	// erm, zero
+	responseHandles.SetLength(oldLen + (KRspHandleCountSize * 2));
+	BigEndian::Put16(&responseHandles[KRspTotalCountOffset], (TUint16)fullSize);
+	BigEndian::Put16(&responseHandles[KRspHandleCountOffset], (TUint16)pduSize);
+	for (TInt i = 0; i < pduSize; i++)
+		{
+		oldLen = responseHandles.Length();
+		responseHandles.SetLength(oldLen + KSdpRecordHandleSize);
+		BigEndian::Put32(&responseHandles[oldLen], collector->HandleAt(sentRecords+i));
+		}
+	oldLen = responseHandles.Length();
+	if (outContFlag)
+		{
+		responseHandles.SetLength(oldLen + KSdpContinuationStateLength + KContStateHeader);
+		responseHandles[oldLen] = KSdpContinuationStateLength;
+		BigEndian::Put32(&responseHandles[oldLen + KContContOff + KContStateHeader], (sentRecords + pduSize));
+		BigEndian::Put32(&responseHandles[oldLen + KContTotOff + KContStateHeader], fullSize);
+		BigEndian::Put16(&responseHandles[oldLen + KContCrcOff + KContStateHeader], 0);		// CRC not used
+		}
+	else
+		{
+		responseHandles.SetLength(oldLen + KContStateHeader);
+		responseHandles[oldLen] = 0;
+		}
+	aRespPdu.iParams.SetLength(responseHandles.Length());
+	CleanupStack::PopAndDestroy(2 /*collector, pHandler*/);
+	}
+
+
+void SdpReqHandler::HandleServiceAttributeL(CSdpDatabase &aDatabase, const TSdpPdu &aReqPdu, TSdpPdu &aRespPdu)
+/**
+	Handle Service Attribute request.
+	Request parameter format is
+ @verbatim
+		Service record handle		TUint32
+		Max Attribute byte count	TUint16
+		Attribute ID/range list		DES
+		Continuation state			1 + 0-16 bytes
+ @endverbatim
+
+	Response parameter format is
+ @verbatim
+		byte count of attr list		TUint16
+		Attribute ID & Value		DES
+		Continuation State			1 + 0-16 bytes
+ @endverbatim
+
+**/
+	{
+// Parse the request parameters
+	TPtr8 fullParams(aReqPdu.iParams);
+	if (fullParams.Length() < KRecAttribListOffset)
+	    {
+	    User::Leave(KErrUnderflow);
+	    }
+	
+	TPtrC8 params = fullParams.Right(fullParams.Length() - KRecAttribListOffset);
+	TInt len = params.Length();
+	TInt rem;
+	TInt sentRecords=0;
+	TInt sentAttributes=0;
+
+	CSdpPDUHandler* pHandler = new (ELeave) CSdpPDUHandler();
+	CleanupStack::PushL(pHandler);
+	CSizeAccumulator* collector = CSizeAccumulator::NewL();
+	CleanupStack::PushL(collector);
+
+
+// record handle to search
+	TSdpServRecordHandle recordHandle = BigEndian::Get32(&fullParams[KRecHandleOffset]);
+// maximum byte count for results
+	TInt maxTotalAttributeCount = BigEndian::Get16(&fullParams[KAttributeCountOffset]);
+
+// list of Attributes or ranges	
+	CSdpAttrIdMatchList *attMatchList = pHandler->AttrListLC(params, rem);
+// check for the continuation header
+	TBool inContFlag = pHandler->ContinuationL(params, len, rem);
+// find the record
+	CSdpServRecord* theRecord=0;
+// changed to allow testing with Tsdpdp.cpp
+	for(TServRecordIter recIter(aDatabase.RecordIter()); recIter; recIter++)
+	{// Iterate thru records in Db
+		if ((*recIter).Handle() == recordHandle)
+			{
+			theRecord = recIter;
+			break;
+			}
+		}
+//  record not found
+	if (!theRecord) User::Leave(KErrBadHandle);
+
+// size the response
+	CResponseSizeVisitor::SizeRespARL(*theRecord, *attMatchList, *collector);
+	CleanupStack::PopAndDestroy(/*attMatchList*/);
+// should only have one record in the size list
+	if (collector->HandleCount() != 1) User::Leave(KErrArgument);
+
+// some checks on continuation
+	TUint fullSize = collector->SizeLeft(); // we can always send all bytes the max is only per PDU
+// the sizer includes the DES size, but we need the record size less the header
+// NOTE in the SAS case we have a set (DES) of DES, but no DES header in front of them.
+	TUint innerRecSize = collector->HandleSize(0); // should be totalSize less the DES
+	TInt desSize = CSdpPDUHandler::DesSize(innerRecSize);
+	if (fullSize == 0) fullSize += desSize; // give it the smallest header if there are no attributes
+	TInt16 localCRC=0;
+	TUint sentBytes;
+	TUint sentBytesCurAttr;			// bytes left to send on current attribute
+	if (inContFlag)
+		{
+		if (fullSize != pHandler->FullLength()) User::Leave(KErrUnknown);	// continuation check
+		sentBytes = pHandler->ContinuationOffset(); // this is in bytes, not handles like service search
+		if (fullSize <= sentBytes) User::Leave(KErrUnknown);	// continuation check
+// FIXME removed CRC handling until continuation works
+//		localCRC = collector->CrcAttribs();						// CRC is only for attributes
+		if (localCRC != pHandler->ReqCRC()) User::Leave(KErrUnknown);				// continuation check for attributes
+// collector->StartAt uses the bytes already sent to discover how far through an 
+// attribute we are at the beginning of a new pdu in a continued response  
+		TBool adj = collector->StartAt(sentBytes, sentBytesCurAttr, sentRecords, sentAttributes); // this is for attributes
+		if (!adj) User::Leave(KErrUnknown);
+		}
+	else
+		{
+		sentBytes = 0;
+		sentBytesCurAttr = 0;
+		}
+/*
+	working out if we can send all or some of the data:
+	if there is a maxbytes outstanding, comply with that
+	if we can send all the data (bytes) fine
+	if we can't send all the data, reduce the buffer by the continuation
+*/
+	TInt pduSize = fullSize - sentBytes;	// the data left to send
+
+// start with the buffer size less: 
+//		the byte count size and the empty continuation header
+	TInt bufferUsableLen = aRespPdu.iParams.MaxLength() - (KRspAttributeCountSize + KContStateHeader);
+	TInt bufferThisSize = Min(maxTotalAttributeCount, bufferUsableLen);
+
+	TBool outContFlag;
+	if (bufferThisSize < pduSize) 
+		{
+		outContFlag = ETrue;
+		bufferUsableLen -= KSdpContinuationStateLength;	// we need the header now
+		bufferThisSize = Min(maxTotalAttributeCount, bufferUsableLen); // again the smallest
+//	when sending attributes (AR, SAS) make sure we don't leave a single byte
+//	to be sent in the next continuation. Otherwise, reduce the payload this
+//	time by 1 to make sure we comply with the specification and send a minimum
+//	of two bytes in the response.
+		if ((pduSize - bufferThisSize) == 1) bufferThisSize -= 1;
+		if (!inContFlag)
+			{
+// FIXME CRC removed until continuation works
+//			localCRC = collector->CrcAttribs();
+			}
+		}
+	else
+		{
+		outContFlag = EFalse;
+// we can complete this request this time
+		}
+
+	pduSize = Min(pduSize, bufferThisSize);
+
+// end of common second stage continuation processing
+
+	TInt writtenSize = pduSize;  // we will be reducing the pduSize
+
+// Write the response packet
+	aRespPdu.iPduId = EServiceAttributeResponse;
+	aRespPdu.iParams.SetMax();
+	TPtr8 responseAttributes(0,0);
+	responseAttributes.Set(&aRespPdu.iParams[0], 0, aRespPdu.iParams.MaxLength());
+	TElementEncoder attributeEncoder(responseAttributes);
+	TInt oldLen = responseAttributes.Length();
+	responseAttributes.SetLength(oldLen + KRspAttributeCountSize);
+	BigEndian::Put16(&responseAttributes[KRspAttributeCountOffset], (TUint16)(pduSize));
+	CAttrSizeItem* currentAttItem = collector->AttributeOf(sentRecords, sentAttributes);
+	TInt lastAttr = collector->AttrCount(sentRecords);
+	TPtrC8 attrValPtr(0,0);
+	TBuf8<1> wBuffer(1); // used for byte insertion
+	if (!inContFlag)
+		{// write the outer DES
+		attributeEncoder.WriteDES(innerRecSize);
+		pduSize -= desSize;
+		}
+	else
+		{ // we are writing a continuation so straight into attribute data
+		if (sentBytesCurAttr)
+			{ // we have to write the rest of a previous attribute
+			TPtrC8 partPtr(0,0);
+			attrValPtr.Set(currentAttItem->Attr()->Value().Des());
+			TInt unsentBytes = attrValPtr.Length() + KAttributeIdSize - sentBytesCurAttr;
+			switch(sentBytesCurAttr)
+				{ // we may have to send some of the attribute ID
+			case 1:
+				// Append most significant byte to responseAttributes
+				wBuffer[0] = (TUint8)(currentAttItem->AttID() >> 8);
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize--;
+				unsentBytes--;
+				// No break statement is deliberate because we want to append 
+				// the least significant byte to responseAttributes too
+			case 2:
+				wBuffer[0] = (TUint8)(currentAttItem->AttID() & 0xff);
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize--;
+				unsentBytes--;
+				partPtr.Set(attrValPtr);	// it's a whole pointer
+				break;
+			default:
+				partPtr.Set(attrValPtr.Right(unsentBytes));
+				break;
+				}
+			if (unsentBytes - pduSize > 0)	
+				{// we don't even get to send one complete attribute...
+				partPtr.Set(partPtr.Left(pduSize)); // adjust the size of the des we send.
+				pduSize = 0;
+				}
+			else
+				{
+				pduSize -= unsentBytes;
+				sentAttributes++;
+				}
+				attributeEncoder.WriteDesc(partPtr);
+			}
+		// what should be here ?
+		}
+// now send bytes up to what's left of pduSize
+	TInt currentAttr = sentAttributes;
+	TInt attrSize;
+	while (pduSize > 0 && currentAttr < lastAttr)
+		{
+		if (currentAttr > lastAttr) User::Leave(KErrUnknown); // should go past the last attribute
+		currentAttItem = collector->AttributeOf(sentRecords, currentAttr);
+		if (pduSize < 3)
+			{
+			wBuffer[0] = KAttrIdHeader;
+			responseAttributes.Append(&wBuffer[0], 1);
+			pduSize--;
+			if (pduSize == 0) break;
+			}
+		if (pduSize == 1)
+			{
+			wBuffer[0] = (TUint8)(currentAttItem->AttID() >> 8);
+			responseAttributes.Append(&wBuffer[0], 1);
+			pduSize = 0;
+			break;
+			}
+		attributeEncoder.WriteUint(currentAttItem->AttID(), 2);
+		pduSize -= KAttributeIdSize; // the attrID with its header
+		if (pduSize == 0) break;
+		attrSize = currentAttItem->Size();
+		attrValPtr.Set(currentAttItem->Attr()->Value().Des());
+		if (attrSize > pduSize)
+			{
+			TPtrC8 partPtr;
+			partPtr.Set(attrValPtr.Left(pduSize));
+			attributeEncoder.WriteDesc(partPtr);
+			pduSize = 0;
+			}
+		else
+			{
+			attributeEncoder.WriteDesc(attrValPtr);
+			pduSize -= attrSize;
+			}
+		currentAttr++;
+		}
+	oldLen = responseAttributes.Length();
+	if (outContFlag)
+		{
+		responseAttributes.SetLength(oldLen + KSdpContinuationStateLength + KContStateHeader);
+		responseAttributes[oldLen] = KSdpContinuationStateLength;
+		BigEndian::Put32(&responseAttributes[oldLen + KContContOff + KContStateHeader], (sentBytes + writtenSize));
+		BigEndian::Put32(&responseAttributes[oldLen + KContTotOff + KContStateHeader], fullSize);
+		BigEndian::Put16(&responseAttributes[oldLen + KContCrcOff + KContStateHeader], localCRC);		// CRC not used
+		}
+	else
+		{
+		responseAttributes.SetLength(oldLen + KContStateHeader);
+		responseAttributes[oldLen] = 0;
+		}
+	aRespPdu.iParams.SetLength(responseAttributes.Length());
+	CleanupStack::PopAndDestroy(2 /*collector, pHandler*/);	
+	}
+
+void SdpReqHandler::HandleServiceSearchAttributeL(CSdpDatabase &aDatabase, const TSdpPdu &aReqPdu, TSdpPdu &aRespPdu)
+/**
+	Handle Service Attribute Search request.
+	Request parameter format is
+ @verbatim
+		Service search pattern		DES
+		Max Attribute byte count	TUint16
+		Attribute ID/range list		DES
+		Continuation state			1 + 0-16 bytes
+ @endverbatim
+
+	Response parameter format is
+ @verbatim
+		Total byte count			TUint16
+		for each record matched		DES of
+		Attribute ID & Value		DES
+		Continuation State			1 + 0-16 bytes
+ @endverbatim
+
+**/
+	{
+// Parse the request parameters
+	TPtr8 params(aReqPdu.iParams);
+	TInt len = params.Length();
+	TInt rem;
+	TInt sentRecords=0;
+	TInt sentAttributes=0;
+	TInt maxTotalAttributeCount = 0;
+
+	CSdpPDUHandler* pHandler = new (ELeave)	CSdpPDUHandler();
+	CleanupStack::PushL(pHandler);
+	CSizeAccumulator* collector = CSizeAccumulator::NewL();
+	CleanupStack::PushL(collector);
+
+
+	CSdpSearchPattern* pattern = pHandler->UUIDListLC(params, KRecHandleCountSize, len, rem, maxTotalAttributeCount);
+
+// list of Attributes or ranges	
+	TPtrC8 attrParams = params.Right(rem);
+	CSdpAttrIdMatchList *attMatchList = pHandler->AttrListLC(attrParams, rem);
+// check for the continuation header
+	TBool inContFlag = pHandler->ContinuationL(params, len, rem);
+// size the response
+	CResponseSizeVisitor::SizeRespSAL(aDatabase, *pattern, *attMatchList, *collector);
+	CleanupStack::PopAndDestroy(2 /*attMatchList, pattern*/);
+	TInt totalHandles = collector->HandleCount();
+
+// some checks on continuation
+	TUint fullSize = collector->SizeLeft(); // we can always send all bytes the max is only per PDU
+// the sizer includes the DES size, but we need the record size less the header
+// NOTE in the SAS case we have a set (DES) of DES, but no DES header in front of them.
+	TInt desSize = CSdpPDUHandler::DesSize(fullSize);// the DES of DES length is not calculated by SizeLeft
+	fullSize += desSize;		// this avoids the case of zero attributes
+	TInt16 localCRC = 0;
+	TUint sentBytes;
+	TUint sentBytesCurAttr;			// bytes on current attribute already sent
+	if (inContFlag)
+		{
+		if (fullSize != pHandler->FullLength()) User::Leave(KErrUnknown);	// continuation check
+		sentBytes = pHandler->ContinuationOffset(); // this is in bytes, not handles like service search
+		if (fullSize <= sentBytes) User::Leave(KErrUnknown);	// continuation check
+
+// FIXME removed CRC handling until continuation works
+//		localCRC = collector->CrcAttribs();						// CRC is only for attributes
+		if (localCRC != pHandler->ReqCRC()) User::Leave(KErrUnknown);				// continuation check for attributes
+
+// collector->StartAt uses the bytes already sent to discover how far through an 
+// attribute we are at the beginning of a new pdu in a continued response....   
+// ....in SAS we need to remove the desSize from any sent bytes as the outer DES is not
+// counted in StartAt.
+// ....also NOTE: because further down we ensure that no new record DES or part thereof)
+// is sent at the end of a pdu any non-zero value returned in sentBytesCurAttr will not be
+// just that (bytes already sent of current attribute split in process of continuation)
+		TBool adj = collector->StartAt(sentBytes - desSize, sentBytesCurAttr, sentRecords, sentAttributes); // this is for attributes
+		if (!adj) User::Leave(KErrUnknown);
+		}
+	else
+		{
+		sentBytes = 0;
+		sentBytesCurAttr = 0;
+		}
+
+/*
+	working out if we can send all or some of the data:
+	if there is a maxbytes outstanding, comply with that
+	if we can send all the data (bytes) fine
+	if we can't send all the data, reduce the buffer by the continuation
+	and then take the minimum of the maxbytes or what is left of the buffer
+*/
+	TInt pduSize = fullSize - sentBytes;	// the data left to send
+
+// start with the buffer size less: 
+//		the byte count size and the empty continuation header
+	TInt bufferUsableLen = aRespPdu.iParams.MaxLength() - (KRspAttributeCountSize + KContStateHeader);
+	TInt bufferThisSize = Min(maxTotalAttributeCount, bufferUsableLen);
+
+	TBool outContFlag;
+	if (bufferThisSize < pduSize) 
+		{
+		outContFlag = ETrue;
+		bufferUsableLen -= KSdpContinuationStateLength;	// we need the header now
+		bufferThisSize = Min(maxTotalAttributeCount, bufferUsableLen); // again the smallest
+//	when sending attributes (AR, SAS) make sure we don't leave a single byte
+//	to be sent in the next continuation. Otherwise, reduce the payload this
+//	time by 1 to make sure we comply with the specification and send a minimum
+//	of two bytes in the response.
+		if ((pduSize - bufferThisSize) == 1) bufferThisSize -= 1;
+		if (!inContFlag)
+			{
+// FIXME CRC removed until continuation works
+//			localCRC = collector->CrcAttribs();
+			}
+		}
+	else
+		{
+		outContFlag = EFalse;
+// we can complete this request this time
+		}
+
+	pduSize = Min(pduSize, bufferThisSize);
+
+// end of common second stage continuation processing
+
+	TInt writtenSize = pduSize;  // we will be reducing the pduSize
+
+// Write the response packet
+	aRespPdu.iPduId = EServiceSearchAttributeResponse;
+	aRespPdu.iParams.SetMax();
+	TPtr8 responseAttributes(0,0); //used to point along aRespPdu.iParams, and manage this
+	responseAttributes.Set(&aRespPdu.iParams[0], 0, aRespPdu.iParams.MaxLength());
+	//NB attributeEncoder below writes to responseAttributes (pointing at response PDU)...
+	TElementEncoder attributeEncoder(responseAttributes);
+	TInt oldLen = responseAttributes.Length();
+	responseAttributes.SetLength(oldLen + KRspAttributeCountSize);
+	BigEndian::Put16(&responseAttributes[KRspAttributeCountOffset], (TUint16)(pduSize));
+	CAttrSizeItem* currentAttItem = collector->AttributeOf(sentRecords, sentAttributes);
+	TInt lastAttr = collector->AttrCount(sentRecords);
+	TPtrC8 attrValPtr(0,0);
+	TBuf8<1> wBuffer(1); // used for byte insertion
+	if (!inContFlag)
+		{// write the outer DES
+		attributeEncoder.WriteDES(fullSize - desSize);
+		pduSize -= desSize;
+		}
+	else
+		{ // we are writing a continuation so straight into attribute data
+		if (sentBytesCurAttr)
+			{ // we have to write the rest of a previous attribute
+			TPtrC8 partPtr(0,0);
+			attrValPtr.Set(currentAttItem->Attr()->Value().Des());
+			TInt unsentBytes = attrValPtr.Length() + KAttributeIdSize - sentBytesCurAttr;
+			switch(sentBytesCurAttr)
+				{ // we may have to send some of the attribute ID
+			// coverity[unterminated_case]
+			case 1:
+				wBuffer[0] = (TUint8)(currentAttItem->AttID() >> 8);
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize--;
+				unsentBytes--;
+			// coverity[fallthrough]
+			case 2:
+				wBuffer[0] = (TUint8)(currentAttItem->AttID() & 0xff);
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize--;
+				unsentBytes--;
+				partPtr.Set(attrValPtr); // it's a whole pointer
+				break;
+			default:
+				partPtr.Set(attrValPtr.Right(unsentBytes));
+				break;
+				}
+			if (unsentBytes - pduSize > 0)	
+				{// we don't even get to send one complete attribute...
+				partPtr.Set(partPtr.Left(pduSize)); // adjust the size of the des we send.
+				pduSize = 0;
+				}
+			else
+				{
+				pduSize -= unsentBytes;
+				sentAttributes++;
+				}
+			attributeEncoder.WriteDesc(partPtr);
+			}
+		// what should be here ?
+		}
+// now send bytes up to what's left of pduSize
+	TInt currentAttr = sentAttributes;
+	TInt currentRec = sentRecords;
+	TInt attrSize;
+	while (pduSize > 0 && currentRec < totalHandles)
+		{ // for all handles
+		if (currentAttr == 0)
+			{ // write the DES header for this list of attributes
+			TUint lRecordSize = collector->HandleSize(currentRec);
+			TInt lDesSize = CSdpPDUHandler::DesSize(lRecordSize);
+			if (pduSize <= lDesSize)
+				{ // a new record DES: don't write it if it or part of it would
+				  // come on the end of the data part of a pdu - leave it
+				  // for the next pdu
+				writtenSize -= pduSize;
+				BigEndian::Put16(&responseAttributes[KRspAttributeCountOffset], (TUint16)(writtenSize));
+				pduSize = 0;
+				break;
+				}
+			if(lRecordSize)
+				{//only if record has any attributes to return 
+				 //(=> lastAttr will be > 0 when calculated below)
+				attributeEncoder.WriteDES(lRecordSize);
+				pduSize -= lDesSize;
+				}
+			}
+		lastAttr = collector->AttrCount(currentRec);
+		while (pduSize > 0 && currentAttr < lastAttr)
+			{ // for all attributes
+			currentAttItem = collector->AttributeOf(currentRec, currentAttr);
+			//if clauses below put part of attribute id on the end of a pdu
+			if (pduSize < 3)
+				{
+				wBuffer[0] = KAttrIdHeader;
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize--;
+				if (pduSize == 0) break;
+				}
+			if (pduSize == 1)
+				{
+				wBuffer[0] = (TUint8)(currentAttItem->AttID() >> 8);
+				responseAttributes.Append(&wBuffer[0], 1);
+				pduSize = 0;
+				break;
+				}
+			//if we've got here we can at least write a complete attr id onto the pdu
+			attributeEncoder.WriteUint(currentAttItem->AttID(), 2);
+			pduSize -= KAttributeIdSize; // the attrID with its header
+			if (pduSize == 0) break;
+			attrSize = currentAttItem->Size();
+			attrValPtr.Set(currentAttItem->Attr()->Value().Des());
+			if (attrSize > pduSize)
+				{
+				TPtrC8 partPtr;
+				partPtr.Set(attrValPtr.Left(pduSize)); //from left pduSize bytes
+				attributeEncoder.WriteDesc(partPtr);
+				pduSize = 0;
+				}
+			else
+				{
+				attributeEncoder.WriteDesc(attrValPtr);
+				pduSize -= attrSize;
+				}
+			currentAttr++;
+			}
+		if (pduSize == 0) break;
+		currentRec++;
+		currentAttr = 0;
+		}
+	oldLen = responseAttributes.Length();
+	if (outContFlag)
+		{
+		responseAttributes.SetLength(oldLen + KSdpContinuationStateLength + KContStateHeader);
+		responseAttributes[oldLen] = KSdpContinuationStateLength;
+		BigEndian::Put32(&responseAttributes[oldLen + KContContOff + KContStateHeader], (sentBytes + writtenSize));
+		BigEndian::Put32(&responseAttributes[oldLen + KContTotOff + KContStateHeader], fullSize);
+		BigEndian::Put16(&responseAttributes[oldLen + KContCrcOff + KContStateHeader], localCRC);		// CRC not used
+		}
+	else
+		{
+		responseAttributes.SetLength(oldLen + KContStateHeader);
+		responseAttributes[oldLen] = 0;
+		}
+	aRespPdu.iParams.SetLength(responseAttributes.Length());
+	CleanupStack::PopAndDestroy(2 /*collector, pHandler*/);	
+	}
+
+
+
+