bluetoothmgmt/btmgr/eirclient/src/eirdatacodec.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothmgmt/btmgr/eirclient/src/eirdatacodec.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,481 @@
+// Copyright (c) 2008-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:
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <bluetooth/eirdatacodec.h>
+#include <utf.h>
+#include <bluetooth/logger.h>
+#include "eirmanclientlogger.h"
+
+void Panic(TEirCodecPanics aCode)
+	{
+	User::Panic(KEirCodecPanicName, aCode);
+	}
+
+// Offset from Length to EIR Tag in EIR data
+const TUint8 KEIRLengthToTagOffset = 1;
+// Offset from EIR Tag to EIR data (value) in EIR data
+const TUint8 KEIRTagToDataOffset = 1;
+// number of bits in eir tag bit mask
+const TUint8 KSizeOfEIRTagBitMask = 32;
+
+/**
+Extended Inquiry Response Parser Class
+This class can take a TNameRecord reference from inquiry result, 
+which could hold both local name and Extended Inquiry Response. 
+It provides API to parse and return the local name and data for any Extended Inquiry Response tag.
+*/
+
+/** 
+ Default Constructs an TExtendedInquiryResponseDataCodec object.
+@internalTechnology
+ */
+EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec()
+: iEir(NULL, 0, 0), iOffset(0)
+	{
+	LOG_FUNC
+	}
+
+/** 
+ Constructs an TExtendedInquiryResponseDataCodec object from an TNameRecord.
+
+ TNameRecord.iName will be copied into a 8-bit descriptor and stored in the object.
+
+ @param aDes Data buffer for extended inquiry response 
+ @internalTechnology
+*/
+EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(const TNameRecord& aNameRecord)
+: iEir(NULL, 0, 0), iOffset(0)
+	{
+	LOG_FUNC
+	// We won't check if the descriptor is the standard EIR size, as it can be reformed to have a whole name.
+	Set(aNameRecord);
+	}
+
+/** 
+ Constructs an TExtendedInquiryResponseDataCodec object from an TNameRecord.
+
+ TNameRecord.iName will be copied into a 8-bit descriptor and stored in the object.
+
+ @param aDes Data buffer for extended inquiry response 
+ @internalTechnology
+*/
+EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(TNameRecord& aNameRecord)
+: iEir(NULL, 0, 0), iOffset(0)
+	{
+	LOG_FUNC
+	// We won't check if the descriptor is the standard EIR size, as it can be reformed to have a whole name.
+	Set(aNameRecord);
+	}
+
+/** 
+ Constructs an TExtendedInquiryResponseDataCodec object from an 8-bit data buffer.
+
+ A copy of the buffer is stored in the object.
+
+ @param aDes Data buffer for extended inquiry response 
+ @internalTechnology
+*/
+EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(const TDesC8& aDes)
+:  iEir(NULL, 0, 0), iOffset(0)
+	{
+	LOG_FUNC
+	Set(aDes);
+	}
+/** 
+ Constructs an TExtendedInquiryResponseDataCodec object from an 8-bit data buffer.
+
+ A copy of the buffer is stored in the object.
+
+ @param aDes Data buffer for extended inquiry response 
+ @internalTechnology
+*/
+EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(TDes8& aDes)
+:  iEir(NULL, 0, 0), iOffset(0)
+	{
+	LOG_FUNC
+	Set(aDes);
+	}
+
+/**
+ Retrieve the EIR data for a particular Data Type 
+ @param aDataType Data Type to look for
+ @param aDes Pointer descriptor that will point to the EIR data for the Data Type requested if successful 
+ (please note this does not contain the Data Type byte) 
+ @return an error code
+ @internalTechnology
+ */
+EXPORT_C TInt TExtendedInquiryResponseDataCodec::GetData(TExtendedInquiryResponseDataType aDataType, TPtrC8& aDes) const
+	{
+	LOG_FUNC
+	TInt offset = NextDataType(0);
+
+	while(offset >= KErrNone)
+		{
+		if(iEir[offset] == aDataType)
+			{
+			aDes.Set(iEir.Mid(offset + KEIRTagToDataOffset, iEir[offset - KEIRLengthToTagOffset] - KEIRLengthToTagOffset));
+			return KErrNone;
+			}
+		else
+			{
+			offset = NextDataType(offset);
+			}
+		}
+
+	return offset;
+	}
+/**
+ Retrieve the EIR data for the next Data Type 
+ @param aOffset Offset to current data type (0 to start traversing)
+ @param aDes Pointer descriptor that will point to the EIR data for the Data Type requested if successful 
+ (please note this does not contain the Data Type byte) 
+ @return TExtendedInquiryResponseDataType (TExtendedInquiryResponseDataType::EEirInvalid means there is no more eir data to get)
+ @internalTechnology
+ */
+EXPORT_C TExtendedInquiryResponseDataType TExtendedInquiryResponseDataCodec::GetNextData(TPtrC8& aDes)
+	{
+	LOG_FUNC
+	TExtendedInquiryResponseDataType dataType;
+	iOffset = NextDataType(iOffset);
+
+	if(iOffset >= KErrNone && iEir.Length() >= (iOffset +iEir[iOffset - KEIRLengthToTagOffset]))
+		{
+		aDes.Set(iEir.Mid(iOffset + KEIRTagToDataOffset, iEir[iOffset-KEIRLengthToTagOffset] - KEIRLengthToTagOffset));
+		dataType = static_cast<TExtendedInquiryResponseDataType>(iEir[iOffset]);
+		}
+	else
+		{
+		// Data must be malformed or corrupted
+		dataType = EEirInvalid;
+		}
+
+	return dataType;
+	}
+	
+/**
+ Find out whether a particular Data Type is present in this TExtendedInquiryResponseDataCodec
+ @param aDataType Data Type to look for
+ @return ETrue if Data Type present, EFalse otherwise
+ @internalTechnology
+ */
+EXPORT_C TBool TExtendedInquiryResponseDataCodec::IsDataTypePresent(TExtendedInquiryResponseDataType aDataType) const
+	{
+	LOG_FUNC
+	TInt offset = NextDataType(0);
+	TBool found = EFalse;
+
+	while(offset >= KErrNone)
+		{
+		if(iEir[offset] == aDataType)
+			{
+			found = ETrue;
+			break;
+			}
+
+		offset = NextDataType(offset);
+		}
+
+	return found;
+	}
+
+/**
+ Obtain the Device Name from inquiry response. This may be a complete or partial name depending on what is available.
+ @param aName is the Device Name converted into Unicode format
+ @return TEIRDataCompleteness (Partial or Complete) or an error code
+ @internalTechnology
+ */
+EXPORT_C TInt TExtendedInquiryResponseDataCodec::GetDeviceName(TPtrC8& aName) const
+	{
+	LOG_FUNC
+	TInt completeness = EDataComplete;
+	TInt error = GetData(EEirLocalNameComplete, aName);
+	if(error != KErrNone)
+		{
+		error = GetData(EEirLocalNamePartial, aName);
+		completeness = EDataPartial;
+		}
+
+	return error == KErrNone ? completeness : error;
+	}
+
+/**
+ Set the Device Name in the EIR buffer from a TPtrC8. This may be a complete or partial name.
+ @param aName is the Device Name converted into Unicode format
+ @param aIsComplete is marking if the name is complete or partial
+ @return TInt an error code
+ @internalTechnology
+ */
+EXPORT_C TInt TExtendedInquiryResponseDataCodec::SetDeviceName(const TPtrC8& aName, TBool aIsComplete)
+	{
+	TPtrC8 name;
+	TInt error = KErrNotFound;
+	TInt offset = NextDataType(0);
+	TInt nameTag = (aIsComplete ? EEirLocalNameComplete : EEirLocalNamePartial);
+	TBool replaceCurrentName = ETrue;
+
+	while(offset >= KErrNone)
+		{
+		if(iEir[offset] == EEirLocalNameComplete || iEir[offset] == EEirLocalNamePartial)
+			{
+			name.Set(iEir.Mid(offset + KEIRTagToDataOffset, iEir[offset-KEIRLengthToTagOffset] - KEIRLengthToTagOffset));
+			if(iEir[offset] == EEirLocalNameComplete && name.Compare(aName) == 0)
+				{
+				// The only scenario we don't want to replace the existing name is when
+				// it's complete and same as the new one (aName)
+				replaceCurrentName = EFalse;
+				LOG(_L("We won't replace the current name"));
+				}
+			error = KErrNone;
+			break;
+			}
+		else
+			{
+			offset = NextDataType(offset);
+			}
+		}
+
+	if(error == KErrNotFound)
+		// no device name present, we will add the name to the end of the eir data
+		{
+		if(iEir.MaxLength() < (iEir.Length() + KEIRTagToDataOffset + KEIRLengthToTagOffset + aName.Length()))
+			{
+			// Not enough space to store this name and its length & tag
+			return KErrNoMemory;
+			}
+		// Add length
+		iEir.Append(aName.Length() + KEIRTagToDataOffset);
+		// Append Tag
+		iEir.Append(nameTag);
+		// Append value
+		iEir.Append(aName);
+		LOG1(_L("EIR data with name appended: %d bytes of data"), iEir.Size());
+		LOGHEXDESC(iEir);
+		error = KErrNone;
+		}
+	else if(replaceCurrentName)
+		// device name exists in current eir and it's either partial or different from aName, we'll update it with aName
+		{
+		// move all the data on the right of device name to the left and then append new name after it
+		TPtr8 rightPtr = iEir.RightTPtr(iEir.Length() - offset - name.Length() - KEIRLengthToTagOffset);
+		iEir.Replace(offset-KEIRLengthToTagOffset, iEir.Length() - offset + KEIRLengthToTagOffset, rightPtr);
+		iEir.SetLength(offset + rightPtr.Length() - KEIRTagToDataOffset);
+		// Check if the new name is too big for iEir
+		if(iEir.MaxLength() < (iEir.Length() + KEIRTagToDataOffset + KEIRLengthToTagOffset + aName.Length()))
+			{
+			// Not enough space to store this name and its length & tag
+			return KErrNoMemory;
+			}
+		// Add length
+		iEir.Append(aName.Length() + KEIRTagToDataOffset);
+		// Append Tag
+		iEir.Append(nameTag);
+		// Append value
+		iEir.Append(aName);
+		LOG(_L("Reshuffled EIR data:"));
+		LOGHEXDESC(iEir);
+		error = KErrNone;
+		}
+	// otherwise we do nothing, as this is the case of an identical complete name is already present.
+	if(iNameRecord)
+		{
+		if(iEir.Length() > 0)
+			{
+			iNameRecord->iName.SetLength(((iEir.Length() + iEir.Length()%2)/2));
+			}
+		else
+			{
+			iNameRecord->iName.SetLength(0);
+			}
+		}
+	return error;
+	}
+
+/**
+ Private Setter, used by the constructor and the assignment operator
+ @param aNameRecord TNameRecord to create extended inquiry response from
+ */
+EXPORT_C void TExtendedInquiryResponseDataCodec::Set(const TNameRecord& aNameRecord)
+	{
+	LOG_FUNC
+	Set(const_cast<TNameRecord&>(aNameRecord));
+	}
+
+/**
+ Private Setter, used by the constructor and the assignment operator
+ @param aNameRecord TNameRecord to create extended inquiry response from
+ */
+EXPORT_C void TExtendedInquiryResponseDataCodec::Set(TNameRecord& aNameRecord)
+	{
+	LOG_FUNC
+	TUint8* name = (TUint8*)(aNameRecord.iName.Ptr());
+	TPtr8 namePtr(name, aNameRecord.iName.Size(), aNameRecord.iName.MaxSize());
+	iNameRecord = &aNameRecord; 
+	Set(namePtr);
+	}
+
+/**
+ Private Setter, used by the constructor and the assignment operator
+ @param aDes Data buffer to create extended inquiry response from
+ */
+EXPORT_C void TExtendedInquiryResponseDataCodec::Set(const TDesC8& aDes)
+	{
+	LOG_FUNC
+	TPtr8 ptr(const_cast<TUint8*>(aDes.Ptr()), aDes.Length(), aDes.Length());
+	Set(ptr);
+	}
+/**
+ Private Setter, used by the constructor and the assignment operator
+ @param aDes Data buffer to create extended inquiry response from
+ */
+EXPORT_C void TExtendedInquiryResponseDataCodec::Set(TDes8& aDes)
+	{
+	LOG_FUNC
+	// Check length consistency
+	TUint16 length = ComputeSignificantLength(aDes);
+	if(length > KNameRecord8BitMaxLength)
+		{
+		// Reset the EIR and discard any data it may contain, since it was malformed anyway
+		iEir.Set(NULL, 0, 0);
+		}
+	else
+		{
+		iEir.Set(const_cast<TUint8*>(aDes.Ptr()), length, aDes.MaxLength());
+		}
+	}
+
+EXPORT_C void TExtendedInquiryResponseDataCodec::Copy(TDesC8& aDes)
+	{
+	__ASSERT_DEBUG(iEir.MaxLength() >= aDes.Length(), Panic(EEirCodecDataTooLarge));
+	TUint16 length = ComputeSignificantLength(aDes);
+	iEir.Copy(aDes);
+	iEir.SetLength(length);
+	iNameRecord->iName.SetLength((iEir.Length()/2)+1);
+	}
+
+EXPORT_C TInt TExtendedInquiryResponseDataCodec::DoSanityCheck(TDes8& aDes)
+	{
+	TInt error = KErrNone;
+	TInt offset = NextDataType(0);
+	TUint32 eirTagPresentFlag = 0;	// this is a bit mask for existing eir types
+	TInt tag = 0;
+
+	while(offset >= KErrNone)
+		{
+		if(!IsValideDataType(iEir[offset]))
+			{
+			// the next data type isn't a known(valid) eir tag
+			error = KErrNotSupported;
+			break;
+			}
+		if(static_cast<TInt>(iEir[offset - KEIRLengthToTagOffset]) < 0)
+			{
+			// length is less than 0
+			error = KErrUnderflow;
+			break;
+			}
+		tag = (iEir[offset] == EEirVendorSpecific ? KSizeOfEIRTagBitMask : iEir[offset]);
+		// check if the tag is already present from previous parsing
+		if(eirTagPresentFlag & (1 << tag))
+			{
+			error = KErrAlreadyExists;
+			break;
+			}
+		else
+			{
+			// set flag for this data tag
+			eirTagPresentFlag |= (1 << tag);
+			offset = NextDataType(offset);
+			}
+		}
+	error = offset == KErrCorrupt ? offset : error;
+	if(error != KErrNone)
+		{
+		// remove all the data after this LTV unit
+		iEir.SetLength(offset - KEIRLengthToTagOffset);
+		aDes.SetLength(offset - KEIRLengthToTagOffset);
+		}
+	return error;
+	}
+
+/**
+ Computes the length of Significant part in an Extended Inquiry Response data.
+
+ @param aDes Extended Inquiry Response data
+ */
+TUint16 TExtendedInquiryResponseDataCodec::ComputeSignificantLength(const TDesC8 &aDes)
+	{
+	LOG_FUNC
+	TUint desLen = aDes.Length();
+	const TUint8 *ptr = aDes.Ptr();
+	TUint16 i = 0;
+	while(i < desLen && ptr[i] != 0x00)
+		{
+		i += ptr[i] + KEIRLengthToTagOffset;
+		}
+	return i;
+	}
+
+/**
+ @param aOffset Offset to current data type (0 to start traversing)
+ @return an error code or Offset to the next EIR data type
+ */
+TInt TExtendedInquiryResponseDataCodec::NextDataType(TInt aOffset) const
+	{
+	LOG_FUNC
+	// Check bounds
+	TInt length = iEir.Length();
+	TInt ret = KErrNotFound;
+	
+	// Check the values of aOffset and length are correct, and aOffset is not bigger than the length
+	if(aOffset > 0 && aOffset <= length)
+		{
+		// Get the Offset for next data type, aDes[aOffset-1] holds the length for each EIR structure
+		TInt newOffset = iEir[aOffset - KEIRLengthToTagOffset] + aOffset + KEIRLengthToTagOffset;
+		if(newOffset > length)
+			{
+			if(newOffset != length + KEIRLengthToTagOffset)
+				{
+				ret = KErrCorrupt;
+				}
+			}
+		else
+			{
+			ret = newOffset;
+			}
+		}
+	
+	// Offset for the first data type
+	else if(aOffset == 0 && length > 0)
+		{
+		ret = KEIRLengthToTagOffset;
+		}
+	return ret;
+	}
+
+TBool TExtendedInquiryResponseDataCodec::IsValideDataType(TInt aDataType)
+	{
+	TBool ret = EFalse;
+	if((aDataType >= EEirFlags && aDataType <= EEirOobSimplePairingRandomizerR) || aDataType == EEirVendorSpecific)
+		{
+		ret = ETrue;
+		}
+	return ret;
+	}
+