--- /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;
+ }
+