|
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @internalTechnology |
|
19 */ |
|
20 |
|
21 #include <bluetooth/eirdatacodec.h> |
|
22 #include <utf.h> |
|
23 #include <bluetooth/logger.h> |
|
24 #include "eirmanclientlogger.h" |
|
25 |
|
26 void Panic(TEirCodecPanics aCode) |
|
27 { |
|
28 User::Panic(KEirCodecPanicName, aCode); |
|
29 } |
|
30 |
|
31 // Offset from Length to EIR Tag in EIR data |
|
32 const TUint8 KEIRLengthToTagOffset = 1; |
|
33 // Offset from EIR Tag to EIR data (value) in EIR data |
|
34 const TUint8 KEIRTagToDataOffset = 1; |
|
35 // number of bits in eir tag bit mask |
|
36 const TUint8 KSizeOfEIRTagBitMask = 32; |
|
37 |
|
38 /** |
|
39 Extended Inquiry Response Parser Class |
|
40 This class can take a TNameRecord reference from inquiry result, |
|
41 which could hold both local name and Extended Inquiry Response. |
|
42 It provides API to parse and return the local name and data for any Extended Inquiry Response tag. |
|
43 */ |
|
44 |
|
45 /** |
|
46 Default Constructs an TExtendedInquiryResponseDataCodec object. |
|
47 @internalTechnology |
|
48 */ |
|
49 EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec() |
|
50 : iEir(NULL, 0, 0), iOffset(0) |
|
51 { |
|
52 LOG_FUNC |
|
53 } |
|
54 |
|
55 /** |
|
56 Constructs an TExtendedInquiryResponseDataCodec object from an TNameRecord. |
|
57 |
|
58 TNameRecord.iName will be copied into a 8-bit descriptor and stored in the object. |
|
59 |
|
60 @param aDes Data buffer for extended inquiry response |
|
61 @internalTechnology |
|
62 */ |
|
63 EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(const TNameRecord& aNameRecord) |
|
64 : iEir(NULL, 0, 0), iOffset(0) |
|
65 { |
|
66 LOG_FUNC |
|
67 // We won't check if the descriptor is the standard EIR size, as it can be reformed to have a whole name. |
|
68 Set(aNameRecord); |
|
69 } |
|
70 |
|
71 /** |
|
72 Constructs an TExtendedInquiryResponseDataCodec object from an TNameRecord. |
|
73 |
|
74 TNameRecord.iName will be copied into a 8-bit descriptor and stored in the object. |
|
75 |
|
76 @param aDes Data buffer for extended inquiry response |
|
77 @internalTechnology |
|
78 */ |
|
79 EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(TNameRecord& aNameRecord) |
|
80 : iEir(NULL, 0, 0), iOffset(0) |
|
81 { |
|
82 LOG_FUNC |
|
83 // We won't check if the descriptor is the standard EIR size, as it can be reformed to have a whole name. |
|
84 Set(aNameRecord); |
|
85 } |
|
86 |
|
87 /** |
|
88 Constructs an TExtendedInquiryResponseDataCodec object from an 8-bit data buffer. |
|
89 |
|
90 A copy of the buffer is stored in the object. |
|
91 |
|
92 @param aDes Data buffer for extended inquiry response |
|
93 @internalTechnology |
|
94 */ |
|
95 EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(const TDesC8& aDes) |
|
96 : iEir(NULL, 0, 0), iOffset(0) |
|
97 { |
|
98 LOG_FUNC |
|
99 Set(aDes); |
|
100 } |
|
101 /** |
|
102 Constructs an TExtendedInquiryResponseDataCodec object from an 8-bit data buffer. |
|
103 |
|
104 A copy of the buffer is stored in the object. |
|
105 |
|
106 @param aDes Data buffer for extended inquiry response |
|
107 @internalTechnology |
|
108 */ |
|
109 EXPORT_C TExtendedInquiryResponseDataCodec::TExtendedInquiryResponseDataCodec(TDes8& aDes) |
|
110 : iEir(NULL, 0, 0), iOffset(0) |
|
111 { |
|
112 LOG_FUNC |
|
113 Set(aDes); |
|
114 } |
|
115 |
|
116 /** |
|
117 Retrieve the EIR data for a particular Data Type |
|
118 @param aDataType Data Type to look for |
|
119 @param aDes Pointer descriptor that will point to the EIR data for the Data Type requested if successful |
|
120 (please note this does not contain the Data Type byte) |
|
121 @return an error code |
|
122 @internalTechnology |
|
123 */ |
|
124 EXPORT_C TInt TExtendedInquiryResponseDataCodec::GetData(TExtendedInquiryResponseDataType aDataType, TPtrC8& aDes) const |
|
125 { |
|
126 LOG_FUNC |
|
127 TInt offset = NextDataType(0); |
|
128 |
|
129 while(offset >= KErrNone) |
|
130 { |
|
131 if(iEir[offset] == aDataType) |
|
132 { |
|
133 aDes.Set(iEir.Mid(offset + KEIRTagToDataOffset, iEir[offset - KEIRLengthToTagOffset] - KEIRLengthToTagOffset)); |
|
134 return KErrNone; |
|
135 } |
|
136 else |
|
137 { |
|
138 offset = NextDataType(offset); |
|
139 } |
|
140 } |
|
141 |
|
142 return offset; |
|
143 } |
|
144 /** |
|
145 Retrieve the EIR data for the next Data Type |
|
146 @param aOffset Offset to current data type (0 to start traversing) |
|
147 @param aDes Pointer descriptor that will point to the EIR data for the Data Type requested if successful |
|
148 (please note this does not contain the Data Type byte) |
|
149 @return TExtendedInquiryResponseDataType (TExtendedInquiryResponseDataType::EEirInvalid means there is no more eir data to get) |
|
150 @internalTechnology |
|
151 */ |
|
152 EXPORT_C TExtendedInquiryResponseDataType TExtendedInquiryResponseDataCodec::GetNextData(TPtrC8& aDes) |
|
153 { |
|
154 LOG_FUNC |
|
155 TExtendedInquiryResponseDataType dataType; |
|
156 iOffset = NextDataType(iOffset); |
|
157 |
|
158 if(iOffset >= KErrNone && iEir.Length() >= (iOffset +iEir[iOffset - KEIRLengthToTagOffset])) |
|
159 { |
|
160 aDes.Set(iEir.Mid(iOffset + KEIRTagToDataOffset, iEir[iOffset-KEIRLengthToTagOffset] - KEIRLengthToTagOffset)); |
|
161 dataType = static_cast<TExtendedInquiryResponseDataType>(iEir[iOffset]); |
|
162 } |
|
163 else |
|
164 { |
|
165 // Data must be malformed or corrupted |
|
166 dataType = EEirInvalid; |
|
167 } |
|
168 |
|
169 return dataType; |
|
170 } |
|
171 |
|
172 /** |
|
173 Find out whether a particular Data Type is present in this TExtendedInquiryResponseDataCodec |
|
174 @param aDataType Data Type to look for |
|
175 @return ETrue if Data Type present, EFalse otherwise |
|
176 @internalTechnology |
|
177 */ |
|
178 EXPORT_C TBool TExtendedInquiryResponseDataCodec::IsDataTypePresent(TExtendedInquiryResponseDataType aDataType) const |
|
179 { |
|
180 LOG_FUNC |
|
181 TInt offset = NextDataType(0); |
|
182 TBool found = EFalse; |
|
183 |
|
184 while(offset >= KErrNone) |
|
185 { |
|
186 if(iEir[offset] == aDataType) |
|
187 { |
|
188 found = ETrue; |
|
189 break; |
|
190 } |
|
191 |
|
192 offset = NextDataType(offset); |
|
193 } |
|
194 |
|
195 return found; |
|
196 } |
|
197 |
|
198 /** |
|
199 Obtain the Device Name from inquiry response. This may be a complete or partial name depending on what is available. |
|
200 @param aName is the Device Name converted into Unicode format |
|
201 @return TEIRDataCompleteness (Partial or Complete) or an error code |
|
202 @internalTechnology |
|
203 */ |
|
204 EXPORT_C TInt TExtendedInquiryResponseDataCodec::GetDeviceName(TPtrC8& aName) const |
|
205 { |
|
206 LOG_FUNC |
|
207 TInt completeness = EDataComplete; |
|
208 TInt error = GetData(EEirLocalNameComplete, aName); |
|
209 if(error != KErrNone) |
|
210 { |
|
211 error = GetData(EEirLocalNamePartial, aName); |
|
212 completeness = EDataPartial; |
|
213 } |
|
214 |
|
215 return error == KErrNone ? completeness : error; |
|
216 } |
|
217 |
|
218 /** |
|
219 Set the Device Name in the EIR buffer from a TPtrC8. This may be a complete or partial name. |
|
220 @param aName is the Device Name converted into Unicode format |
|
221 @param aIsComplete is marking if the name is complete or partial |
|
222 @return TInt an error code |
|
223 @internalTechnology |
|
224 */ |
|
225 EXPORT_C TInt TExtendedInquiryResponseDataCodec::SetDeviceName(const TPtrC8& aName, TBool aIsComplete) |
|
226 { |
|
227 TPtrC8 name; |
|
228 TInt error = KErrNotFound; |
|
229 TInt offset = NextDataType(0); |
|
230 TInt nameTag = (aIsComplete ? EEirLocalNameComplete : EEirLocalNamePartial); |
|
231 TBool replaceCurrentName = ETrue; |
|
232 |
|
233 while(offset >= KErrNone) |
|
234 { |
|
235 if(iEir[offset] == EEirLocalNameComplete || iEir[offset] == EEirLocalNamePartial) |
|
236 { |
|
237 name.Set(iEir.Mid(offset + KEIRTagToDataOffset, iEir[offset-KEIRLengthToTagOffset] - KEIRLengthToTagOffset)); |
|
238 if(iEir[offset] == EEirLocalNameComplete && name.Compare(aName) == 0) |
|
239 { |
|
240 // The only scenario we don't want to replace the existing name is when |
|
241 // it's complete and same as the new one (aName) |
|
242 replaceCurrentName = EFalse; |
|
243 LOG(_L("We won't replace the current name")); |
|
244 } |
|
245 error = KErrNone; |
|
246 break; |
|
247 } |
|
248 else |
|
249 { |
|
250 offset = NextDataType(offset); |
|
251 } |
|
252 } |
|
253 |
|
254 if(error == KErrNotFound) |
|
255 // no device name present, we will add the name to the end of the eir data |
|
256 { |
|
257 if(iEir.MaxLength() < (iEir.Length() + KEIRTagToDataOffset + KEIRLengthToTagOffset + aName.Length())) |
|
258 { |
|
259 // Not enough space to store this name and its length & tag |
|
260 return KErrNoMemory; |
|
261 } |
|
262 // Add length |
|
263 iEir.Append(aName.Length() + KEIRTagToDataOffset); |
|
264 // Append Tag |
|
265 iEir.Append(nameTag); |
|
266 // Append value |
|
267 iEir.Append(aName); |
|
268 LOG1(_L("EIR data with name appended: %d bytes of data"), iEir.Size()); |
|
269 LOGHEXDESC(iEir); |
|
270 error = KErrNone; |
|
271 } |
|
272 else if(replaceCurrentName) |
|
273 // device name exists in current eir and it's either partial or different from aName, we'll update it with aName |
|
274 { |
|
275 // move all the data on the right of device name to the left and then append new name after it |
|
276 TPtr8 rightPtr = iEir.RightTPtr(iEir.Length() - offset - name.Length() - KEIRLengthToTagOffset); |
|
277 iEir.Replace(offset-KEIRLengthToTagOffset, iEir.Length() - offset + KEIRLengthToTagOffset, rightPtr); |
|
278 iEir.SetLength(offset + rightPtr.Length() - KEIRTagToDataOffset); |
|
279 // Check if the new name is too big for iEir |
|
280 if(iEir.MaxLength() < (iEir.Length() + KEIRTagToDataOffset + KEIRLengthToTagOffset + aName.Length())) |
|
281 { |
|
282 // Not enough space to store this name and its length & tag |
|
283 return KErrNoMemory; |
|
284 } |
|
285 // Add length |
|
286 iEir.Append(aName.Length() + KEIRTagToDataOffset); |
|
287 // Append Tag |
|
288 iEir.Append(nameTag); |
|
289 // Append value |
|
290 iEir.Append(aName); |
|
291 LOG(_L("Reshuffled EIR data:")); |
|
292 LOGHEXDESC(iEir); |
|
293 error = KErrNone; |
|
294 } |
|
295 // otherwise we do nothing, as this is the case of an identical complete name is already present. |
|
296 if(iNameRecord) |
|
297 { |
|
298 if(iEir.Length() > 0) |
|
299 { |
|
300 iNameRecord->iName.SetLength(((iEir.Length() + iEir.Length()%2)/2)); |
|
301 } |
|
302 else |
|
303 { |
|
304 iNameRecord->iName.SetLength(0); |
|
305 } |
|
306 } |
|
307 return error; |
|
308 } |
|
309 |
|
310 /** |
|
311 Private Setter, used by the constructor and the assignment operator |
|
312 @param aNameRecord TNameRecord to create extended inquiry response from |
|
313 */ |
|
314 EXPORT_C void TExtendedInquiryResponseDataCodec::Set(const TNameRecord& aNameRecord) |
|
315 { |
|
316 LOG_FUNC |
|
317 Set(const_cast<TNameRecord&>(aNameRecord)); |
|
318 } |
|
319 |
|
320 /** |
|
321 Private Setter, used by the constructor and the assignment operator |
|
322 @param aNameRecord TNameRecord to create extended inquiry response from |
|
323 */ |
|
324 EXPORT_C void TExtendedInquiryResponseDataCodec::Set(TNameRecord& aNameRecord) |
|
325 { |
|
326 LOG_FUNC |
|
327 TUint8* name = (TUint8*)(aNameRecord.iName.Ptr()); |
|
328 TPtr8 namePtr(name, aNameRecord.iName.Size(), aNameRecord.iName.MaxSize()); |
|
329 iNameRecord = &aNameRecord; |
|
330 Set(namePtr); |
|
331 } |
|
332 |
|
333 /** |
|
334 Private Setter, used by the constructor and the assignment operator |
|
335 @param aDes Data buffer to create extended inquiry response from |
|
336 */ |
|
337 EXPORT_C void TExtendedInquiryResponseDataCodec::Set(const TDesC8& aDes) |
|
338 { |
|
339 LOG_FUNC |
|
340 TPtr8 ptr(const_cast<TUint8*>(aDes.Ptr()), aDes.Length(), aDes.Length()); |
|
341 Set(ptr); |
|
342 } |
|
343 /** |
|
344 Private Setter, used by the constructor and the assignment operator |
|
345 @param aDes Data buffer to create extended inquiry response from |
|
346 */ |
|
347 EXPORT_C void TExtendedInquiryResponseDataCodec::Set(TDes8& aDes) |
|
348 { |
|
349 LOG_FUNC |
|
350 // Check length consistency |
|
351 TUint16 length = ComputeSignificantLength(aDes); |
|
352 if(length > KNameRecord8BitMaxLength) |
|
353 { |
|
354 // Reset the EIR and discard any data it may contain, since it was malformed anyway |
|
355 iEir.Set(NULL, 0, 0); |
|
356 } |
|
357 else |
|
358 { |
|
359 iEir.Set(const_cast<TUint8*>(aDes.Ptr()), length, aDes.MaxLength()); |
|
360 } |
|
361 } |
|
362 |
|
363 EXPORT_C void TExtendedInquiryResponseDataCodec::Copy(TDesC8& aDes) |
|
364 { |
|
365 __ASSERT_DEBUG(iEir.MaxLength() >= aDes.Length(), Panic(EEirCodecDataTooLarge)); |
|
366 TUint16 length = ComputeSignificantLength(aDes); |
|
367 iEir.Copy(aDes); |
|
368 iEir.SetLength(length); |
|
369 iNameRecord->iName.SetLength((iEir.Length()/2)+1); |
|
370 } |
|
371 |
|
372 EXPORT_C TInt TExtendedInquiryResponseDataCodec::DoSanityCheck(TDes8& aDes) |
|
373 { |
|
374 TInt error = KErrNone; |
|
375 TInt offset = NextDataType(0); |
|
376 TUint32 eirTagPresentFlag = 0; // this is a bit mask for existing eir types |
|
377 TInt tag = 0; |
|
378 |
|
379 while(offset >= KErrNone) |
|
380 { |
|
381 if(!IsValideDataType(iEir[offset])) |
|
382 { |
|
383 // the next data type isn't a known(valid) eir tag |
|
384 error = KErrNotSupported; |
|
385 break; |
|
386 } |
|
387 if(static_cast<TInt>(iEir[offset - KEIRLengthToTagOffset]) < 0) |
|
388 { |
|
389 // length is less than 0 |
|
390 error = KErrUnderflow; |
|
391 break; |
|
392 } |
|
393 tag = (iEir[offset] == EEirVendorSpecific ? KSizeOfEIRTagBitMask : iEir[offset]); |
|
394 // check if the tag is already present from previous parsing |
|
395 if(eirTagPresentFlag & (1 << tag)) |
|
396 { |
|
397 error = KErrAlreadyExists; |
|
398 break; |
|
399 } |
|
400 else |
|
401 { |
|
402 // set flag for this data tag |
|
403 eirTagPresentFlag |= (1 << tag); |
|
404 offset = NextDataType(offset); |
|
405 } |
|
406 } |
|
407 error = offset == KErrCorrupt ? offset : error; |
|
408 if(error != KErrNone) |
|
409 { |
|
410 // remove all the data after this LTV unit |
|
411 iEir.SetLength(offset - KEIRLengthToTagOffset); |
|
412 aDes.SetLength(offset - KEIRLengthToTagOffset); |
|
413 } |
|
414 return error; |
|
415 } |
|
416 |
|
417 /** |
|
418 Computes the length of Significant part in an Extended Inquiry Response data. |
|
419 |
|
420 @param aDes Extended Inquiry Response data |
|
421 */ |
|
422 TUint16 TExtendedInquiryResponseDataCodec::ComputeSignificantLength(const TDesC8 &aDes) |
|
423 { |
|
424 LOG_FUNC |
|
425 TUint desLen = aDes.Length(); |
|
426 const TUint8 *ptr = aDes.Ptr(); |
|
427 TUint16 i = 0; |
|
428 while(i < desLen && ptr[i] != 0x00) |
|
429 { |
|
430 i += ptr[i] + KEIRLengthToTagOffset; |
|
431 } |
|
432 return i; |
|
433 } |
|
434 |
|
435 /** |
|
436 @param aOffset Offset to current data type (0 to start traversing) |
|
437 @return an error code or Offset to the next EIR data type |
|
438 */ |
|
439 TInt TExtendedInquiryResponseDataCodec::NextDataType(TInt aOffset) const |
|
440 { |
|
441 LOG_FUNC |
|
442 // Check bounds |
|
443 TInt length = iEir.Length(); |
|
444 TInt ret = KErrNotFound; |
|
445 |
|
446 // Check the values of aOffset and length are correct, and aOffset is not bigger than the length |
|
447 if(aOffset > 0 && aOffset <= length) |
|
448 { |
|
449 // Get the Offset for next data type, aDes[aOffset-1] holds the length for each EIR structure |
|
450 TInt newOffset = iEir[aOffset - KEIRLengthToTagOffset] + aOffset + KEIRLengthToTagOffset; |
|
451 if(newOffset > length) |
|
452 { |
|
453 if(newOffset != length + KEIRLengthToTagOffset) |
|
454 { |
|
455 ret = KErrCorrupt; |
|
456 } |
|
457 } |
|
458 else |
|
459 { |
|
460 ret = newOffset; |
|
461 } |
|
462 } |
|
463 |
|
464 // Offset for the first data type |
|
465 else if(aOffset == 0 && length > 0) |
|
466 { |
|
467 ret = KEIRLengthToTagOffset; |
|
468 } |
|
469 return ret; |
|
470 } |
|
471 |
|
472 TBool TExtendedInquiryResponseDataCodec::IsValideDataType(TInt aDataType) |
|
473 { |
|
474 TBool ret = EFalse; |
|
475 if((aDataType >= EEirFlags && aDataType <= EEirOobSimplePairingRandomizerR) || aDataType == EEirVendorSpecific) |
|
476 { |
|
477 ret = ETrue; |
|
478 } |
|
479 return ret; |
|
480 } |
|
481 |