bluetooth/btsdp/server/protocol/reqhandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 15 Jan 2010 08:13:17 +0200
changeset 0 29b1cd4cb562
child 51 20ac952a623c
permissions -rw-r--r--
Revision: 200951_001

// 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*/);	
	}