|
1 // Copyright (c) 1999-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 // L2CAP Host resolver. |
|
15 // Implements Inquiry and Name lookup |
|
16 // |
|
17 // |
|
18 |
|
19 #include "hostresolver.h" |
|
20 #include <bluetooth/logger.h> |
|
21 #include <bluetooth/hci/event.h> |
|
22 #include <bluetooth/hci/hciutil.h> |
|
23 #include <bluetooth/hci/hciconsts.h> |
|
24 #include <bluetooth/hci/inquirycommand.h> |
|
25 #include <bluetooth/hci/inquirycancelcommand.h> |
|
26 #include <bluetooth/hci/writeinquirymodecommand.h> |
|
27 #include <bluetooth/hci/writeextendedinquiryresponsecommand.h> |
|
28 #include <bluetooth/hci/readextendedinquiryresponsecommand.h> |
|
29 #include <bluetooth/hci/remotenamerequestcommand.h> |
|
30 #include <bluetooth/hci/remotenamereqcompleteevent.h> |
|
31 #include <bluetooth/hci/remotenamerequestcancelcommand.h> |
|
32 #include <bluetooth/hci/remotehostsupportedfeaturesnotificationevent.h> |
|
33 #include <bluetooth/hci/readlocalnamecommand.h> |
|
34 |
|
35 // Command Events |
|
36 #include <bluetooth/hci/inquiryresultevent.h> |
|
37 #include <bluetooth/hci/inquiryresultwithrssievent.h> |
|
38 #include <bluetooth/hci/extendedinquiryresultevent.h> |
|
39 |
|
40 // Command Complete Events |
|
41 #include <bluetooth/hci/readlocalnamecompleteevent.h> |
|
42 #include <bluetooth/hci/commandcompleteevent.h> |
|
43 #include <bluetooth/hci/commandstatusevent.h> |
|
44 |
|
45 #include <e32std.h> |
|
46 #include <s32mem.h> |
|
47 #include <bt_sock.h> |
|
48 #include <utf.h> |
|
49 #include "debug.h" |
|
50 #include "linkutil.h" |
|
51 #include "physicallinksmanager.h" // to get baseband freed up for inquiries |
|
52 #include "BTSec.h" |
|
53 |
|
54 //Diagnostic string for security check failures, in builds without platsec |
|
55 //diagnostics this will be NULL. |
|
56 const char* const KBT_HOSTRESOLVER_NAME_DIAG = __PLATSEC_DIAGNOSTIC_STRING("Bluetooth Host Resolver"); |
|
57 |
|
58 const static TUint KHostResDontBlock = 4; |
|
59 |
|
60 #ifdef __FLOG_ACTIVE |
|
61 _LIT8(KLogComponent, LOG_COMPONENT_HOSTRESOLVER); |
|
62 #endif |
|
63 |
|
64 |
|
65 |
|
66 // |
|
67 // BT Inquiry Result record. |
|
68 // |
|
69 |
|
70 CBTInqResultRecord::CBTInqResultRecord(const TBTDevAddr& aAddr) |
|
71 : iCodec(iEntry.iExtendedInquiryResponse) |
|
72 { |
|
73 LOG_FUNC |
|
74 iEntry.iBdaddr = aAddr; |
|
75 // Default to page scan repetition mode R2 as recommended by Bray to give best |
|
76 // chance of success for host resolver needs. |
|
77 // Also allow other default values to be entered here |
|
78 iEntry.iPageScanRepetitionMode = KDefaultBluetoothPageScanRepMode; |
|
79 iEntry.iPageScanMode = KDefaultBluetoothPageScanMode; |
|
80 iEntry.iClockOffset = KDefaultBluetoothClockOffset; |
|
81 iEntry.iCoD = KDefaultBluetoothClassOfDevice; |
|
82 |
|
83 if(aAddr!=TBTDevAddr()) |
|
84 { |
|
85 iJuiceFromHCIMask = EBluetoothAddr; |
|
86 } |
|
87 //else 0 cos 'C' class |
|
88 |
|
89 SetNameValid(EFalse); |
|
90 } |
|
91 |
|
92 CBTInqResultRecord::~CBTInqResultRecord() |
|
93 { |
|
94 LOG_FUNC |
|
95 iIACs.Close(); |
|
96 } |
|
97 |
|
98 inline void CBTInqResultRecord::Open() |
|
99 /** |
|
100 Some reference is now pointing at us. |
|
101 Inc the ref count. |
|
102 **/ |
|
103 { |
|
104 LOG_FUNC |
|
105 ++iRefCount; |
|
106 } |
|
107 |
|
108 void CBTInqResultRecord::Close() |
|
109 /** |
|
110 Reference closing. |
|
111 Auto delete if this was the last reference pointing to us. |
|
112 **/ |
|
113 { |
|
114 LOG_FUNC |
|
115 --iRefCount; |
|
116 if (iRefCount <= 0) |
|
117 delete this; |
|
118 } |
|
119 |
|
120 TInquiryLogEntry& CBTInqResultRecord::LogEntry() |
|
121 /** |
|
122 The inquiry result. |
|
123 This is stored as it was returned from the HCI. |
|
124 The host resolver will convert it into an InquirySockAddr |
|
125 when a client actually wants to retreve it. |
|
126 **/ |
|
127 { |
|
128 LOG_FUNC |
|
129 return iEntry; |
|
130 } |
|
131 |
|
132 inline void CBTInqResultRecord::SetName(const TDesC8& aName) |
|
133 { |
|
134 LOG_FUNC |
|
135 iName = aName; |
|
136 } |
|
137 |
|
138 inline const TDesC8& CBTInqResultRecord::Name() const |
|
139 /** |
|
140 The UTF name of the remote device. |
|
141 This is stored as it comes in off the wire, the host resolver |
|
142 converts to Unicode if & when a client actually requires it. |
|
143 **/ |
|
144 { |
|
145 LOG_FUNC |
|
146 return iName; |
|
147 } |
|
148 |
|
149 inline TInt CBTInqResultRecord::IncFlushes() |
|
150 /** |
|
151 Age the result record |
|
152 Returns it's new age. |
|
153 **/ |
|
154 { |
|
155 LOG_FUNC |
|
156 return ++iFlushes; |
|
157 } |
|
158 |
|
159 inline TBool CBTInqResultRecord::IsNameValid() const |
|
160 { |
|
161 LOG_FUNC |
|
162 return iNameLookupResultCode == KErrNone; |
|
163 } |
|
164 |
|
165 inline void CBTInqResultRecord::SetNameValid(TBool aBool) |
|
166 { |
|
167 LOG_FUNC |
|
168 if(aBool) |
|
169 iNameLookupResultCode = KErrNone; |
|
170 else |
|
171 iNameLookupResultCode = 1; |
|
172 } |
|
173 |
|
174 void CBTInqResultRecord::SetNameLookupResultCode(TInt aResultCode) |
|
175 {// Only record error, if we don't have a valid name already |
|
176 LOG_FUNC |
|
177 if(!IsNameValid()) |
|
178 iNameLookupResultCode = aResultCode; |
|
179 } |
|
180 |
|
181 inline TBool CBTInqResultRecord::IsNameRequestPending() const |
|
182 { |
|
183 LOG_FUNC |
|
184 return iNameStatus & ENamePending; |
|
185 } |
|
186 |
|
187 inline void CBTInqResultRecord::SetNamePending(TBool aBool) |
|
188 { |
|
189 LOG_FUNC |
|
190 if(aBool) |
|
191 iNameStatus |= ENamePending; |
|
192 else |
|
193 iNameStatus &= ~ENamePending; |
|
194 } |
|
195 |
|
196 inline TBool CBTInqResultRecord::IsNameRefreshRequested() const |
|
197 { |
|
198 LOG_FUNC |
|
199 return iNameStatus & ENameRefreshRequested; |
|
200 } |
|
201 |
|
202 inline void CBTInqResultRecord::SetNameRefreshRequested(TBool aBool) |
|
203 { |
|
204 LOG_FUNC |
|
205 if(aBool) |
|
206 iNameStatus |= ENameRefreshRequested; |
|
207 else |
|
208 iNameStatus &= ~ENameRefreshRequested; |
|
209 } |
|
210 |
|
211 inline TBool CBTInqResultRecord::IsNameComplete() const |
|
212 { |
|
213 LOG_FUNC |
|
214 return iNameStatus & ENameComplete; |
|
215 } |
|
216 |
|
217 inline void CBTInqResultRecord::SetNameComplete(TBool aBool) |
|
218 { |
|
219 LOG_FUNC |
|
220 if(aBool) |
|
221 iNameStatus |= ENameComplete; |
|
222 else |
|
223 iNameStatus &= ~ENameComplete; |
|
224 } |
|
225 |
|
226 inline TBool CBTInqResultRecord::IsExplicitNameRequest() const |
|
227 { |
|
228 LOG_FUNC |
|
229 return iNameStatus & ENameExplicitRequest; |
|
230 } |
|
231 |
|
232 inline void CBTInqResultRecord::SetExplicitNameRequest(TBool aBool) |
|
233 { |
|
234 LOG_FUNC |
|
235 if(aBool) |
|
236 iNameStatus |= ENameExplicitRequest; |
|
237 else |
|
238 iNameStatus &= ~ENameExplicitRequest; |
|
239 } |
|
240 |
|
241 inline TBool CBTInqResultRecord::HaveNameLookupResult() const |
|
242 { |
|
243 LOG_FUNC |
|
244 return iNameLookupResultCode <= KErrNone; |
|
245 } |
|
246 |
|
247 inline TInt CBTInqResultRecord::NameLookupResultCode() const |
|
248 {// Only valid if HaveNameLookupResult == ETrue |
|
249 LOG_FUNC |
|
250 __ASSERT_DEBUG(HaveNameLookupResult(), Panic(EBTNameLookupResultNotFound)); |
|
251 return iNameLookupResultCode; |
|
252 } |
|
253 |
|
254 inline TInt CBTInqResultRecord::NameLookupAttempts() const |
|
255 { |
|
256 LOG_FUNC |
|
257 return iNameLookupAttempts; |
|
258 } |
|
259 |
|
260 void CBTInqResultRecord::GetInquirySockAddr(TInquirySockAddr& aAddr) |
|
261 /** |
|
262 Read this result record out into a TInquirySockAddr. |
|
263 **/ |
|
264 { |
|
265 LOG_FUNC |
|
266 aAddr.SetBTAddr(LogEntry().iBdaddr); |
|
267 |
|
268 TBTDeviceClass cod(LogEntry().iCoD); |
|
269 aAddr.SetMajorServiceClass(cod.MajorServiceClass()); |
|
270 aAddr.SetMajorClassOfDevice(cod.MajorDeviceClass()); |
|
271 aAddr.SetMinorClassOfDevice(cod.MinorDeviceClass()); |
|
272 |
|
273 TUint8 resultFlags = 0; |
|
274 |
|
275 if(iJuiceFromHCIMask & EBluetoothRssi) |
|
276 { |
|
277 TInquiryLogEntry* logEntry = &(LogEntry()); |
|
278 resultFlags |= TInquirySockAddr::ERssiValid; |
|
279 aAddr.SetRssi(static_cast<TInquiryLogEntryWithRssi*>(logEntry)->iRssi.RSSI()); |
|
280 } |
|
281 |
|
282 aAddr.SetResultFlags(resultFlags); |
|
283 } |
|
284 |
|
285 TInt CBTInqResultRecord::GetEir(TNameRecord& aNameRec, TBool aIgnoreCachedName) |
|
286 { |
|
287 // Reformat the EIR (i.e. remove 0s from EIR) for the constructor |
|
288 LOG_FUNC |
|
289 TInt error = KErrNone; |
|
290 |
|
291 aNameRec.iName.Zero(); |
|
292 iCodec.Set(aNameRec); |
|
293 if(IsEirPresent()) |
|
294 { |
|
295 TInquiryLogEntry* logEntry = &(LogEntry()); |
|
296 iCodec.Copy(static_cast<TInquiryLogEntryWithEir*>(logEntry)->iExtendedInquiryResponse); |
|
297 } |
|
298 // replace device name in eir with iName if there is a complete name present |
|
299 if(IsNameValid() && IsNameComplete() && !aIgnoreCachedName) |
|
300 { |
|
301 iCodec.SetDeviceName(iName, ETrue); |
|
302 } |
|
303 return error; |
|
304 } |
|
305 |
|
306 TInt CBTInqResultRecord::GetName(TNameRecord& aNameRec) |
|
307 { |
|
308 LOG_FUNC |
|
309 TInt err = KErrNotFound; |
|
310 if(HaveNameLookupResult()) |
|
311 { |
|
312 err = CnvUtfConverter::ConvertToUnicodeFromUtf8(aNameRec.iName, iName); |
|
313 if (err >= KErrNone) |
|
314 { |
|
315 err = KErrNone; |
|
316 if(!IsNameComplete()) |
|
317 { |
|
318 aNameRec.iFlags |= TNameRecord::EPartial; |
|
319 } |
|
320 else |
|
321 { |
|
322 aNameRec.iFlags &= ~(TNameRecord::EPartial); |
|
323 } |
|
324 } |
|
325 } |
|
326 else |
|
327 { |
|
328 err = KErrNone; // just following the old way of working. |
|
329 aNameRec.iName.Zero(); |
|
330 } |
|
331 return err; |
|
332 } |
|
333 |
|
334 TInt CBTInqResultRecord::AddIAC(TUint aIAC) |
|
335 /** |
|
336 Adds IAC into list of those this device has responded to |
|
337 @return KErrNone IAC added without any bother |
|
338 @return KErrAlreadyExists IAC already in list |
|
339 **/ |
|
340 { |
|
341 LOG_FUNC |
|
342 return iIACs.InsertInOrder(aIAC); |
|
343 } |
|
344 |
|
345 inline TBool CBTInqResultRecord::HasRespondedToIAC(TUint aIAC) |
|
346 { |
|
347 LOG_FUNC |
|
348 return (iIACs.FindInOrder(aIAC) >= 0); |
|
349 } |
|
350 |
|
351 inline TInt CBTInqResultRecord::NumberOfIACsRespondedTo() |
|
352 { |
|
353 LOG_FUNC |
|
354 return iIACs.Count(); |
|
355 } |
|
356 |
|
357 inline void CBTInqResultRecord::ClearIACs() |
|
358 { |
|
359 LOG_FUNC |
|
360 iIACs.Reset(); |
|
361 } |
|
362 |
|
363 // |
|
364 // Inquiry result reference. |
|
365 // |
|
366 CBTInqResultRef::CBTInqResultRef(CBTInqResultRef& aRef) |
|
367 : iRecord(aRef.iRecord) |
|
368 { |
|
369 LOG_FUNC |
|
370 iRecord.Open(); |
|
371 } |
|
372 |
|
373 CBTInqResultRef::~CBTInqResultRef() |
|
374 { |
|
375 LOG_FUNC |
|
376 iRecord.Close(); |
|
377 iLink.Deque(); |
|
378 } |
|
379 |
|
380 CBTInqResultRef::CBTInqResultRef(CBTInqResultRecord& aRec) |
|
381 : iRecord(aRec) |
|
382 { |
|
383 LOG_FUNC |
|
384 iRecord.Open(); |
|
385 } |
|
386 |
|
387 inline CBTInqResultRecord& CBTInqResultRef::Result() const |
|
388 { |
|
389 LOG_FUNC |
|
390 return iRecord; |
|
391 } |
|
392 |
|
393 |
|
394 |
|
395 // |
|
396 // BT Inquiry Result Set. |
|
397 // |
|
398 |
|
399 CBTInqResultSet::CBTInqResultSet() |
|
400 : iResultRefs(_FOFF(CBTInqResultRef, iLink)), |
|
401 iNextRefIter(iResultRefs) |
|
402 { |
|
403 LOG_FUNC |
|
404 } |
|
405 |
|
406 CBTInqResultSet::~CBTInqResultSet() |
|
407 { |
|
408 LOG_FUNC |
|
409 Reset(); |
|
410 } |
|
411 |
|
412 void CBTInqResultSet::Reset() |
|
413 { |
|
414 LOG_FUNC |
|
415 while (!iResultRefs.IsEmpty()) |
|
416 delete iResultRefs.First(); |
|
417 ReturnToFirstResult(); |
|
418 } |
|
419 |
|
420 CBTInqResultRef* CBTInqResultSet::Add(CBTInqResultRecord& aRec) |
|
421 { |
|
422 LOG_FUNC |
|
423 CBTInqResultRef* ref = new CBTInqResultRef(aRec); |
|
424 if (!ref) |
|
425 { |
|
426 return 0; |
|
427 } |
|
428 iResultRefs.AddLast(*ref); |
|
429 if (!iNextRefIter) |
|
430 { |
|
431 iNextRefIter.SetToLast(); |
|
432 } |
|
433 return ref; |
|
434 } |
|
435 |
|
436 CBTInqResultRef* CBTInqResultSet::FindEntry(const TBTDevAddr& aAddr) |
|
437 { |
|
438 LOG_FUNC |
|
439 TResultQueIter iter(iResultRefs); |
|
440 while (iter) |
|
441 { |
|
442 CBTInqResultRef* ref = iter++; |
|
443 if (ref->Result().LogEntry().iBdaddr == aAddr) |
|
444 { |
|
445 return ref; |
|
446 } |
|
447 } |
|
448 return 0; |
|
449 } |
|
450 |
|
451 CBTInqResultRef* CBTInqResultSet::NextResult() |
|
452 { |
|
453 LOG_FUNC |
|
454 return iCurrentResult = (iNextRefIter++); |
|
455 } |
|
456 |
|
457 inline CBTInqResultRef* CBTInqResultSet::CurrentResult() |
|
458 { |
|
459 LOG_FUNC |
|
460 return iCurrentResult; |
|
461 } |
|
462 |
|
463 void CBTInqResultSet::ReturnToFirstResult() |
|
464 { |
|
465 LOG_FUNC |
|
466 iNextRefIter.SetToFirst(); |
|
467 iCurrentResult = 0; |
|
468 } |
|
469 |
|
470 inline TBool CBTInqResultSet::IsEmpty() |
|
471 { |
|
472 LOG_FUNC |
|
473 return iResultRefs.IsEmpty(); |
|
474 } |
|
475 |
|
476 void CBTInqResultSet::MoveToback(CBTInqResultRef& aRef) |
|
477 { |
|
478 LOG_FUNC |
|
479 if(iNextRefIter == &aRef) |
|
480 NextResult(); |
|
481 aRef.iLink.Deque(); |
|
482 iResultRefs.AddLast(aRef); |
|
483 if (!iNextRefIter) |
|
484 { |
|
485 iNextRefIter.SetToLast(); |
|
486 } |
|
487 } |
|
488 |
|
489 // |
|
490 // BT Host resolver. |
|
491 // |
|
492 |
|
493 |
|
494 CBTHostResolver::CBTHostResolver(CBTInquiryMgr& aInquiryMgr) |
|
495 : iInquiryMgr(aInquiryMgr), |
|
496 iRequestState(EIdle), |
|
497 iInquiryStatus(EInquiryReady) |
|
498 { |
|
499 LOG_FUNC |
|
500 #ifdef _DEBUG |
|
501 iInquiryMgr.IncrementHRCount(); |
|
502 #endif |
|
503 } |
|
504 |
|
505 CBTHostResolver::~CBTHostResolver() |
|
506 { |
|
507 LOG_FUNC |
|
508 iLink.Deque(); |
|
509 iInquiryMgr.DeletingHostResolver(); |
|
510 #ifdef _DEBUG |
|
511 iInquiryMgr.DecrementHRCount(); |
|
512 #endif |
|
513 } |
|
514 |
|
515 void CBTHostResolver::GetByName(TNameRecord& /*aName*/) |
|
516 { |
|
517 LOG_FUNC |
|
518 iNotify->QueryComplete(KErrNotSupported); |
|
519 } |
|
520 |
|
521 void CBTHostResolver::GetByAddress(TNameRecord& aName) |
|
522 { |
|
523 LOG_FUNC |
|
524 __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests)); |
|
525 iNameRecord = &aName; |
|
526 iNameRecord->iName.Zero(); |
|
527 TInquirySockAddr& sa = TInquirySockAddr::Cast(aName.iAddr); |
|
528 |
|
529 LOG1(_L("Host Resolver\tAction = %d"),sa.Action()); |
|
530 |
|
531 if(sa.Action() & KHostResCache) |
|
532 //Complete immediately with info if available! |
|
533 { |
|
534 TInt err = KErrNotFound; |
|
535 CBTInqResultRef* ref = iInquiryMgr.FindExistingCacheEntry(sa.BTAddr()); |
|
536 if (ref) |
|
537 {// Got a result to send up |
|
538 CBTInqResultRecord& rec = ref->Result(); |
|
539 rec.GetInquirySockAddr(sa); //Put BT address, CoD etc into 'aName' |
|
540 // Check whether client wants EIRs instead of names |
|
541 if(sa.Action() & KHostResEir) |
|
542 { |
|
543 // Client knows about EIR, we'll fill the TNameRecord with EIR |
|
544 err = rec.GetEir(aName, EFalse); |
|
545 } |
|
546 else |
|
547 { |
|
548 // Client don't knows about EIR, we'll fill the TNameRecord with just encoded device name if present |
|
549 err = rec.GetName(aName); |
|
550 } |
|
551 if (err != KErrNone) |
|
552 {// error getting name - but other stuff fine - |
|
553 // so ensure name is not gobbledigook |
|
554 aName.iName.Zero(); |
|
555 } |
|
556 err = KErrNone; //err has new meaning now, vis "record exists" |
|
557 } |
|
558 else |
|
559 { |
|
560 //err is KErrNotFound |
|
561 } |
|
562 |
|
563 iNotify->QueryComplete(err); |
|
564 return; |
|
565 } |
|
566 |
|
567 // Must request at least one of inquiry or name lookup |
|
568 if (!(sa.Action() & (KHostResInquiry | KHostResName))) |
|
569 { |
|
570 iRequestState = EError; |
|
571 CompleteRequest(KErrArgument); |
|
572 return; |
|
573 } |
|
574 |
|
575 // Check the hw state - can't do anything if its switched off |
|
576 // |
|
577 if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff) |
|
578 { |
|
579 iRequestState = EError; |
|
580 CompleteRequest(KErrHardwareNotAvailable); |
|
581 return; |
|
582 } |
|
583 |
|
584 iNameLookupMode = (sa.Action()) & KHostResName ? EDoGetNames : EDontGetNames; |
|
585 TBool ignoreCache = (sa.Action() & KHostResIgnoreCache) ? ETrue : EFalse; |
|
586 |
|
587 if (sa.Action() & KHostResInquiry) |
|
588 { |
|
589 // We only support GIAC and LIAC |
|
590 if (sa.IAC() != KLIAC && sa.IAC() != KGIAC) |
|
591 { |
|
592 iRequestState = EError; |
|
593 CompleteRequest(KErrNotSupported); |
|
594 return; |
|
595 } |
|
596 sa.SetBTAddr(TBTDevAddr()); |
|
597 iRequestState = EInquiry; |
|
598 if (iInquiryStatus == EInquiryReady) |
|
599 {// Need to start the inquiry process |
|
600 iInquiryStatus = EInquiring; |
|
601 // We set this before calling StartInquiry, as StartInquiry can call InquiryComplete synchronously. |
|
602 iResults.Reset(); |
|
603 iInquiryMgr.StartInquiry(*this, sa.IAC(), ignoreCache); |
|
604 } |
|
605 } |
|
606 else // Not an inquiry - just a name lookup |
|
607 { |
|
608 // Just name lookup -- add it to the result set, and request a lookup of it |
|
609 iResults.Reset(); |
|
610 |
|
611 CBTInqResultRef* ref = iInquiryMgr.AddEntryToCache(sa.BTAddr()); |
|
612 if(ref) |
|
613 {// This ref is in cache -- need to make our own ref |
|
614 ref = iResults.Add(ref->Result()); |
|
615 } |
|
616 if (!ref) |
|
617 { |
|
618 CompleteRequest(KErrNoMemory); |
|
619 return; |
|
620 } |
|
621 iResults.NextResult(); // Move iter onto first (only) result |
|
622 iRequestState = ENameLookup; |
|
623 iInquiryMgr.LookupName(*this, sa.BTAddr(), ignoreCache, ETrue); |
|
624 } |
|
625 TryToCompleteRequest(); |
|
626 } |
|
627 |
|
628 TInt CBTHostResolver::SetOption(TUint /*aLevel*/, TUint /*aName*/, const TDesC8& /*aOption*/) |
|
629 { |
|
630 LOG_FUNC |
|
631 return KErrNone; |
|
632 } |
|
633 |
|
634 void CBTHostResolver::SetHostName(TDes& aNameBuf) |
|
635 { |
|
636 LOG_FUNC |
|
637 __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests)); |
|
638 |
|
639 TBuf8<KHCILocalDeviceNameMaxLength> utf8Name; |
|
640 TInt ret = CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Name, aNameBuf); |
|
641 iRequestState=ESetLocalName; |
|
642 if (ret > KErrNone) |
|
643 {// Unconverted characters exist! Error the client |
|
644 ret = KErrHostResNameTooLong; |
|
645 } |
|
646 |
|
647 // Check the hw state - can't do anything if its switched off |
|
648 // |
|
649 if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff) |
|
650 { |
|
651 ret = KErrHardwareNotAvailable; |
|
652 } |
|
653 |
|
654 if (ret == KErrNone) |
|
655 ret = iInquiryMgr.SetLocalName(utf8Name); |
|
656 |
|
657 if (ret != KErrNone) |
|
658 { |
|
659 CompleteRequest(ret); |
|
660 } |
|
661 } |
|
662 |
|
663 void CBTHostResolver::GetHostName(TDes& aNameBuf) |
|
664 { |
|
665 LOG_FUNC |
|
666 __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests)); |
|
667 |
|
668 iHostNameBuf = &aNameBuf; |
|
669 iRequestState=EGetLocalName; |
|
670 TInt ret = KErrNone; |
|
671 |
|
672 // Check the hw state - can't do anything if its switched off |
|
673 // |
|
674 if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff) |
|
675 { |
|
676 ret = KErrHardwareNotAvailable; |
|
677 } |
|
678 |
|
679 if(ret==KErrNone) |
|
680 iInquiryMgr.GetLocalName(); |
|
681 else |
|
682 CompleteRequest(ret); |
|
683 } |
|
684 |
|
685 |
|
686 void CBTHostResolver::CancelCurrentOperation() |
|
687 /** |
|
688 Current operation cancel by user app. |
|
689 Reset, so that we don't bother picking up any future results. |
|
690 We don't actually bother to cancel the async. operations, as |
|
691 if they succeed, the InquiryMgr may as well cache them anyway. |
|
692 The next operation after this (if there is any), must be a fresh |
|
693 one -- i.e. it can not be a next. |
|
694 **/ |
|
695 { |
|
696 LOG_FUNC |
|
697 iRequestState = EIdle; |
|
698 CompleteCurrentOperation(); |
|
699 } |
|
700 |
|
701 void CBTHostResolver::InquiryResult(CBTInqResultRecord& aResult) |
|
702 { |
|
703 LOG_FUNC |
|
704 if (iInquiryStatus != EInquiring) |
|
705 return; |
|
706 |
|
707 // Check if we've already seen this result |
|
708 CBTInqResultRef* ref = iResults.FindEntry(aResult.LogEntry().iBdaddr); |
|
709 if (ref) |
|
710 return; |
|
711 |
|
712 TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr); |
|
713 if(!aResult.HasRespondedToIAC(sa.IAC())) |
|
714 return; // It's never responded to our IAC, so ignore it |
|
715 |
|
716 ref = iResults.Add(aResult); |
|
717 if (!ref) |
|
718 { |
|
719 InquiryComplete(KErrNoMemory); |
|
720 return; |
|
721 } |
|
722 |
|
723 if(iNameLookupMode == EDoGetNames) |
|
724 { |
|
725 // Find out whether cache was supposed to be ignored from TNameRecord that was passed into GetByAddress() previously |
|
726 TBool ignoreCache = (sa.Action() & KHostResIgnoreCache) ? ETrue : EFalse; |
|
727 // Handle a "special" case: Cache is to be ignored, but we got the _complete_ name during the inquiry process inside an EIR |
|
728 // (which doesn't count as cached data, since it's freshly arrived from the radio). In this case we don't really want to |
|
729 // launch a remote name request |
|
730 if(ignoreCache && ref->Result().IsEirPresent() && ref->Result().Codec().IsDataTypePresent(EEirLocalNameComplete)) |
|
731 { |
|
732 // Do nothing, the entry already contains the name |
|
733 } |
|
734 else |
|
735 { |
|
736 iInquiryMgr.LookupName(*this, aResult.LogEntry().iBdaddr, ignoreCache, EFalse); |
|
737 } |
|
738 } |
|
739 |
|
740 if (iRequestState == EInquiry) |
|
741 { |
|
742 TryToCompleteRequest(); |
|
743 } |
|
744 } |
|
745 |
|
746 void CBTHostResolver::NameLookupResult(TInt aErr, const TBTDevAddr& aAddr, const TDesC8& aName) |
|
747 { |
|
748 LOG_FUNC |
|
749 if (iNameLookupMode == EDontGetNames) |
|
750 return; // We're not actually after names |
|
751 |
|
752 // Check we've got this result recorded (mostly for error cases) |
|
753 CBTInqResultRef* ref = iResults.FindEntry(aAddr); |
|
754 if (ref && !(ref->Result().HaveNameLookupResult() && ref->Result().IsNameComplete())) |
|
755 { |
|
756 // we have successfully created a reference and there isn't a complete name available. |
|
757 ref->Result().SetNameLookupResultCode(aErr); |
|
758 ref->Result().SetNameComplete(ETrue); |
|
759 ref->Result().SetName(aName); |
|
760 } |
|
761 |
|
762 TryToCompleteRequest(); |
|
763 } |
|
764 |
|
765 void CBTHostResolver::InquiryComplete(TInt aErr) |
|
766 { |
|
767 LOG_FUNC |
|
768 // LC changed - if we are just doing a name lookup, iInquiryStatus |
|
769 // doesn't get set so need additional check here. |
|
770 // |
|
771 if ((iInquiryStatus != EInquiring) && (iRequestState != ENameLookup)) |
|
772 return; |
|
773 iInquiryStatus = EInquiryComplete; |
|
774 iInqCompletionCode = aErr; |
|
775 if(aErr==KErrHardwareNotAvailable) |
|
776 iRequestState=EError; |
|
777 TryToCompleteRequest(); |
|
778 } |
|
779 |
|
780 void CBTHostResolver::SetLocalNameComplete(TInt aErr) |
|
781 { |
|
782 LOG_FUNC |
|
783 if (iRequestState != ESetLocalName) |
|
784 return; |
|
785 CompleteRequest(aErr); |
|
786 } |
|
787 |
|
788 void CBTHostResolver::GetLocalNameComplete(TInt aErr, const TDesC8& aName) |
|
789 { |
|
790 LOG_FUNC |
|
791 if (iRequestState != EGetLocalName) |
|
792 return; |
|
793 if (aErr == KErrNone) |
|
794 { |
|
795 aErr = CnvUtfConverter::ConvertToUnicodeFromUtf8(*iHostNameBuf, aName); |
|
796 // Just allow names to be truncated... |
|
797 } |
|
798 CompleteRequest(aErr >= 0 ? KErrNone : aErr); |
|
799 } |
|
800 |
|
801 TUint CBTHostResolver::GetIAC() const |
|
802 { |
|
803 LOG_FUNC |
|
804 if (!iNameRecord) |
|
805 { |
|
806 return 0; // avoid dereferencing a null ptr |
|
807 } |
|
808 |
|
809 TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr); |
|
810 return sa.IAC(); |
|
811 } |
|
812 |
|
813 TInt CBTHostResolver::SecurityCheck(MProvdSecurityChecker *aSecurityChecker) |
|
814 { |
|
815 LOG_FUNC |
|
816 __ASSERT_ALWAYS(aSecurityChecker, User::Panic(KSECURITY_PANIC, EBTPanicNullSecurityChecker)); |
|
817 |
|
818 iSecurityChecker = aSecurityChecker; |
|
819 return iSecurityChecker->CheckPolicy(KLOCAL_SERVICES, KBT_HOSTRESOLVER_NAME_DIAG); |
|
820 } |
|
821 |
|
822 void CBTHostResolver::TryToCompleteRequest() |
|
823 /** |
|
824 This is where the work is done. |
|
825 A big old state machine that tries to get us one step |
|
826 closer to completing the query. |
|
827 Gets an inquiry result out of the result set if appropriate and |
|
828 there is one available. |
|
829 Gets the name for the current result if required. |
|
830 Completes the request if there is then nothing more to do. |
|
831 Must only break out (i.e. return) if the request is completed, or |
|
832 non-blocking mode requested |
|
833 **/ |
|
834 { |
|
835 LOG_FUNC |
|
836 TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr); |
|
837 if ((sa.Action() & KHostResInquiry) && iRequestState == EInquiry) |
|
838 { |
|
839 CBTInqResultRef* refNextRes = iResults.NextResult(); |
|
840 if (refNextRes) |
|
841 {// Got a result to send up |
|
842 if (iNameLookupMode == EDoGetNames) |
|
843 {// Need to get the name of this baby... |
|
844 iRequestState = ENameLookup; |
|
845 } |
|
846 else |
|
847 {//Complete! |
|
848 refNextRes->Result().GetInquirySockAddr(sa); |
|
849 // If EIR was requested and we got one, stick it in the name field |
|
850 TInt err = KErrNone; |
|
851 if(sa.Action() & KHostResEir) |
|
852 { |
|
853 err = refNextRes->Result().GetEir(*iNameRecord, (sa.Action() & KHostResIgnoreCache) && !(sa.Action() & KHostResName)); |
|
854 } |
|
855 else |
|
856 { |
|
857 err = refNextRes->Result().GetName(*iNameRecord); |
|
858 } |
|
859 if(err != KErrNone) |
|
860 { |
|
861 // Set name back to zero length |
|
862 iNameRecord->iName.Zero(); |
|
863 } |
|
864 CompleteRequest(err); |
|
865 return; |
|
866 } |
|
867 } |
|
868 else if (iInquiryStatus == EInquiryComplete) |
|
869 {// Bad luck. No more results availble! |
|
870 if (iInqCompletionCode == KErrNone) |
|
871 {// Inquiry completed fine, so report end of file. |
|
872 CompleteRequest(KErrHostResNoMoreResults); |
|
873 } |
|
874 else |
|
875 {// Some error in the inquiry, so report that. |
|
876 CompleteRequest(iInqCompletionCode); |
|
877 } |
|
878 return; |
|
879 } |
|
880 } |
|
881 |
|
882 if ((sa.Action() & KHostResName) && iRequestState == ENameLookup) |
|
883 { |
|
884 CBTInqResultRef* refNextRes = iResults.CurrentResult(); |
|
885 if(!refNextRes) |
|
886 {// Can't get the name... no result to lookup |
|
887 CompleteRequest(KErrHostResNoMoreResults); |
|
888 return; |
|
889 } |
|
890 // Walk through results and check if we have a name to complete |
|
891 CBTInqResultRef* startResult = refNextRes; |
|
892 do |
|
893 { |
|
894 CBTInqResultRecord& rec = refNextRes->Result(); |
|
895 if(rec.HaveNameLookupResult() && !rec.IsNameRequestPending() && rec.IsNameComplete()) |
|
896 {// Got a name! Complete the request |
|
897 rec.GetInquirySockAddr(sa); |
|
898 TInt err = rec.NameLookupResultCode(); |
|
899 if (err == KErrNone) |
|
900 {// Copy & convert the UTF-8 name over into the result record. |
|
901 if(sa.Action() & KHostResEir) |
|
902 { |
|
903 err = rec.GetEir(*iNameRecord, EFalse); |
|
904 } |
|
905 else |
|
906 { |
|
907 err = rec.GetName(*iNameRecord); |
|
908 } |
|
909 } |
|
910 if (err != KErrNone) |
|
911 { |
|
912 iNameRecord->iName.Zero(); |
|
913 } |
|
914 // Name lookup complete (poss. with errors) |
|
915 if(sa.Action() & KHostResInquiry) |
|
916 {// Ignore name failures if we're also doing inquiry |
|
917 CompleteRequest(KErrNone); |
|
918 } |
|
919 else |
|
920 {// Report any error encountered (or none) |
|
921 CompleteRequest(err); |
|
922 } |
|
923 return; |
|
924 } |
|
925 |
|
926 iResults.MoveToback(*refNextRes); |
|
927 refNextRes = iResults.NextResult(); |
|
928 } while(refNextRes != startResult); |
|
929 } |
|
930 else if(iRequestState==EError) |
|
931 { |
|
932 CompleteRequest(KErrHardwareNotAvailable); |
|
933 } |
|
934 if(sa.Action() & KHostResDontBlock) |
|
935 {// Complete any outstanding non-blocking operation |
|
936 CompleteRequest(KErrWouldBlock); |
|
937 } |
|
938 |
|
939 } |
|
940 |
|
941 void CBTHostResolver::CompleteRequest(TInt aErr) |
|
942 { |
|
943 LOG_FUNC |
|
944 if (iRequestState == EIdle && aErr == KErrNone) |
|
945 return; |
|
946 iRequestState = EIdle; |
|
947 if (aErr != KErrNone) |
|
948 {// End of query -- tidy up |
|
949 CompleteCurrentOperation(); |
|
950 } |
|
951 iNotify->QueryComplete(aErr); |
|
952 } |
|
953 |
|
954 void CBTHostResolver::CompleteCurrentOperation() |
|
955 /** |
|
956 Operation known to be complete. |
|
957 This happens through a CancelCurrentOperation, or when |
|
958 the current operation has read all the results out. |
|
959 **/ |
|
960 { |
|
961 LOG_FUNC |
|
962 if (iInquiryStatus == EInquiryReady) |
|
963 return; |
|
964 iInquiryStatus = EInquiryReady; |
|
965 iResults.Reset(); |
|
966 } |
|
967 |
|
968 |
|
969 // |
|
970 // BT Inquiry Manager. |
|
971 // |
|
972 CBTInquiryMgr::CBTInquiryMgr(CLinkMgrProtocol& aProtocol) |
|
973 : iLinkMgrProtocol(aProtocol), |
|
974 iNamePageTimeout((KMaxNamePageTimeout + KMinNamePageTimeout)/2), // start with mid-point timeout |
|
975 iInquiryMode(EStandardInquiryFormat), iHRs(CBTHostResolver::LinkOffset()) |
|
976 { |
|
977 LOG_FUNC |
|
978 } |
|
979 |
|
980 CBTInquiryMgr::~CBTInquiryMgr() |
|
981 { |
|
982 LOG_FUNC |
|
983 __ASSERT_DEBUG(iHRs.IsEmpty(), Panic(EBTHostControllerQueueNotEmpty)); |
|
984 SetHWState(EOff); |
|
985 PublishStatus();// we're not doing anything more so publish status |
|
986 ClearHCICommandQueue(); |
|
987 delete iFlusher; |
|
988 delete iConnectingStatus; |
|
989 } |
|
990 |
|
991 void CBTInquiryMgr::ClearHCICommandQueue() |
|
992 { |
|
993 LOG_FUNC |
|
994 if (iCommandQueue) |
|
995 { |
|
996 iCommandQueue->MhcqRemoveAllCommands(*this); |
|
997 } |
|
998 iCommandQueue = NULL; |
|
999 } |
|
1000 |
|
1001 void CBTInquiryMgr::SetHCICommandQueue(MHCICommandQueue& aCommandQueue) |
|
1002 { |
|
1003 LOG_FUNC |
|
1004 __ASSERT_DEBUG(!iCommandQueue, Panic(EBTCommandQueueAlreadyExist)); |
|
1005 iCommandQueue = &aCommandQueue; |
|
1006 } |
|
1007 |
|
1008 MHCICommandQueue& CBTInquiryMgr::CommandQueue() const |
|
1009 { |
|
1010 LOG_FUNC |
|
1011 __ASSERT_DEBUG(iCommandQueue, Panic(EBTCommandQueueNotFound)); |
|
1012 return *iCommandQueue; |
|
1013 } |
|
1014 |
|
1015 CBTInquiryMgr* CBTInquiryMgr::NewL(CLinkMgrProtocol& aProtocol) |
|
1016 { |
|
1017 LOG_STATIC_FUNC |
|
1018 CBTInquiryMgr* self = new(ELeave) CBTInquiryMgr(aProtocol); |
|
1019 CleanupStack::PushL(self); |
|
1020 self->ConstructL(); |
|
1021 CleanupStack::Pop(); |
|
1022 return self; |
|
1023 } |
|
1024 |
|
1025 void CBTInquiryMgr::ConstructL() |
|
1026 { |
|
1027 LOG_FUNC |
|
1028 iFlusher = CPeriodic::NewL(CActive::EPriorityLow); |
|
1029 #ifdef CONNECTION_PREEMPTS_INQUIRY |
|
1030 iConnectingStatus = CConnectingStatusSubscriber::NewL(*this); |
|
1031 #endif |
|
1032 } |
|
1033 |
|
1034 CBTHostResolver* CBTInquiryMgr::NewHostResolverL() |
|
1035 { |
|
1036 LOG_FUNC |
|
1037 // Leave if HW switched off |
|
1038 // |
|
1039 if(iHWState==EOff) |
|
1040 User::Leave(KErrHardwareNotAvailable); |
|
1041 |
|
1042 CBTHostResolver* r = new(ELeave) CBTHostResolver(*this); |
|
1043 iHRs.AddFirst(*r); |
|
1044 return r; |
|
1045 } |
|
1046 |
|
1047 void CBTInquiryMgr::DeletingHostResolver() |
|
1048 { |
|
1049 LOG_FUNC |
|
1050 // A host resolver is being deleted - we need to check if any other host resolvers want inquiry results from the current IAC. |
|
1051 if(!iRequestedInquiryIAC) |
|
1052 { |
|
1053 //No 'current' IAC exists |
|
1054 return; |
|
1055 } |
|
1056 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1057 TBool iacFound=EFalse; |
|
1058 while (iter) |
|
1059 { |
|
1060 if ((iter++)->GetIAC() == iRequestedInquiryIAC) |
|
1061 { |
|
1062 iacFound = ETrue; |
|
1063 } |
|
1064 } |
|
1065 if (!iacFound) |
|
1066 { |
|
1067 iRequestedInquiryIAC = 0; |
|
1068 } |
|
1069 } |
|
1070 |
|
1071 void CBTInquiryMgr::InquiryResult(TInt aErr,const TInquiryLogEntry& aEntry) |
|
1072 /** |
|
1073 Single Inquiry result received. |
|
1074 Called via HCI for each incoming inquiry result. Need to demultiplex these |
|
1075 out to all CBTHostResolver's interested in them. |
|
1076 |
|
1077 @param aErr An EPOC error code. Any HCI mapping should be done before |
|
1078 calling this function. |
|
1079 @param aEntry The log entry returned through HCI. |
|
1080 **/ |
|
1081 { |
|
1082 LOG_FUNC |
|
1083 LOG5(_L("err: %d,ScanRepM: %d ScanPerM: %d ScanM: %d Type: %d"), |
|
1084 aErr, |
|
1085 aEntry.iPageScanRepetitionMode, |
|
1086 aEntry.iPageScanPeriodMode, |
|
1087 aEntry.iPageScanMode, |
|
1088 aEntry.iSpare ); |
|
1089 |
|
1090 CBTInqResultRef* ref = 0; |
|
1091 |
|
1092 //Revise our cache record regardless - at least need to make sure |
|
1093 //it knows we have received juice from the HCI |
|
1094 if(aErr == KErrNone) |
|
1095 { |
|
1096 ref = AddEntryWithJuiceToCache(aEntry); |
|
1097 if (!ref) |
|
1098 { |
|
1099 aErr = KErrNoMemory; |
|
1100 } |
|
1101 else |
|
1102 { |
|
1103 ref->Result().iFlushes = 0; |
|
1104 ref->Result().iFoundDuringCurrentInquiry = ETrue; |
|
1105 |
|
1106 TInt ret = ref->Result().AddIAC(iCurrentInquiryIAC); |
|
1107 switch(ret) |
|
1108 { |
|
1109 case KErrNone: |
|
1110 ++iResultCount; |
|
1111 // drop through |
|
1112 case KErrAlreadyExists: |
|
1113 break; |
|
1114 default: // Got an error adding the IAC |
|
1115 aErr = ret; |
|
1116 } // switch |
|
1117 ref->Result().AddIAC(KGIAC); // This would come back on a GIAC, so try adding GIAC. |
|
1118 // If this doesn't work, it doesn't really matter, so ignore the error. |
|
1119 } // else |
|
1120 } // if (aErr == KErrNone) |
|
1121 |
|
1122 if (aErr != KErrNone) |
|
1123 { |
|
1124 InquiryComplete(aErr, 0); |
|
1125 return; |
|
1126 } |
|
1127 |
|
1128 // Tell host resolvers |
|
1129 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1130 CBTHostResolver* hr; |
|
1131 while ((hr = iter ++) != NULL) |
|
1132 { |
|
1133 if (hr->GetIAC() == iCurrentInquiryIAC || hr->GetIAC() == KGIAC) |
|
1134 { |
|
1135 hr->InquiryResult(ref->Result()); |
|
1136 } |
|
1137 } |
|
1138 } |
|
1139 |
|
1140 void CBTInquiryMgr::CoDResult(const TBTDevAddr& aAddr, TUint aCoD) |
|
1141 /** |
|
1142 We have been donated a CoD from an incoming connection |
|
1143 This is useful to keep in the registry for remotely |
|
1144 initiated pairings - otherwise we will never have found |
|
1145 the CoD of the device bonding with us |
|
1146 **/ |
|
1147 { |
|
1148 // create record if it doesn't already exist in cache. |
|
1149 // If this fails, we'll just not be able to store the CoD for this device. |
|
1150 |
|
1151 LOG_FUNC |
|
1152 AddEntryWithCoDToCache(aAddr, aCoD); |
|
1153 } |
|
1154 |
|
1155 void CBTInquiryMgr::ClockOffsetResult(const TBTDevAddr& aAddr, TBasebandTime aClockOffset) |
|
1156 /** |
|
1157 We have been donated a Clock Offset - maybe an update |
|
1158 **/ |
|
1159 { |
|
1160 LOG_FUNC |
|
1161 AddEntryWithClockOffsetToCache(aAddr, aClockOffset); |
|
1162 } |
|
1163 |
|
1164 void CBTInquiryMgr::InquiryComplete(TInt aErr, TUint8 /*aNumResponses*/) |
|
1165 /** |
|
1166 Current Inquiry has completed. |
|
1167 Must tell all CBTHostResolver's who may be waiting for inquiry results. |
|
1168 |
|
1169 @param aErr An EPOC error code. Any HCI mapping should be done before |
|
1170 calling this function. |
|
1171 @param aNumResponses The number of calls to InquiryResult that should |
|
1172 have been generated. We don't use this info atm, though. |
|
1173 **/ |
|
1174 { |
|
1175 LOG_FUNC |
|
1176 SetCacheAge(iCurrentInquiryIAC, 0); |
|
1177 TUint iacToComplete = iRequestedInquiryIAC; |
|
1178 iRequestedInquiryIAC = 0; |
|
1179 iFlusher->Cancel(); // Stop watchdog, start flusher |
|
1180 SetHWState(EIdle); |
|
1181 // don't publish status here, might be doing a name lookup |
|
1182 EnableFlusher(); |
|
1183 |
|
1184 // The inquiry has completed so clear any results that were marked found during |
|
1185 // the just finished inquiry |
|
1186 ClearCurrentInquiryResults(); |
|
1187 |
|
1188 if(aErr!=KErrHardwareNotAvailable) |
|
1189 { |
|
1190 LOG(_L("CBTInquiryMgr::InquiryComplete asking for another name lookup")); |
|
1191 DoNameLookup(ETrue); // Get any pending name lookups going |
|
1192 } |
|
1193 else |
|
1194 { |
|
1195 SetHWState(EOff); |
|
1196 LOG(_L("CBTInquiryMgr::InquiryComplete PublishState Idle")); |
|
1197 PublishStatus();// we're not doing name lookup so publish status |
|
1198 } |
|
1199 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1200 CBTHostResolver* hr; |
|
1201 while ((hr = iter++) != NULL) |
|
1202 { |
|
1203 if (hr->GetIAC() == iacToComplete) |
|
1204 { |
|
1205 hr->InquiryComplete(aErr); |
|
1206 } |
|
1207 hr++; |
|
1208 } |
|
1209 // Only queue the inquiry if we have completed all the name requests from this one. |
|
1210 // Otherwise, it will be issued after all the remote name requests have completed. |
|
1211 if (iQueuedInquiryIAC != 0 && iPendingNameRequests == 0) |
|
1212 { |
|
1213 iRequestedInquiryIAC = iQueuedInquiryIAC; |
|
1214 iQueuedInquiryIAC = 0; |
|
1215 DoInquiry(); |
|
1216 } |
|
1217 } |
|
1218 |
|
1219 /** |
|
1220 Name lookup result received. |
|
1221 Called via HCI for each incoming name result. |
|
1222 |
|
1223 The name lookup may not have been requested by the inquiry mgr as when the |
|
1224 stack gets a new connection it tries to solicit information about the |
|
1225 remote. This sort of result is just to add to our cache. |
|
1226 |
|
1227 Otherwise it's from a request on us, pass it on for handling. |
|
1228 |
|
1229 @param aErr An EPOC error code. Any HCI mapping should be done before |
|
1230 calling this function. |
|
1231 @param aAddr The address of the Bluetooth device that the name is associated |
|
1232 with. |
|
1233 @param aBuf 8 bit Descriptor up to KHCIRemoteDeviceNameMaxLength |
|
1234 bytes long, containing the name. |
|
1235 */ |
|
1236 void CBTInquiryMgr::RemoteNameResult(TInt aErr, const TBTDevAddr& aAddr, const TBTDeviceName8& aBuf) |
|
1237 { |
|
1238 LOG_FUNC |
|
1239 LOG1(_L("aErr = %d"), aErr); |
|
1240 LOG6(_L(", aAddr: %02x %02x %02x %02x %02x %02x"), |
|
1241 TUint8(aAddr[0]), TUint8(aAddr[1]), TUint8(aAddr[2]), TUint8(aAddr[3]), TUint8(aAddr[4]), |
|
1242 TUint8(aAddr[5])); |
|
1243 |
|
1244 CBTInqResultRef* ref = FindExistingCacheEntry(aAddr); |
|
1245 if(ref) |
|
1246 { |
|
1247 CBTInqResultRecord& rec = ref->Result(); |
|
1248 if (rec.IsNameRequestPending()) |
|
1249 { |
|
1250 // We asked for this |
|
1251 HandleRemoteNameResult(aErr, *ref, aBuf); |
|
1252 return; |
|
1253 } |
|
1254 } |
|
1255 |
|
1256 // Unsolicited, just wham in cache if it's good |
|
1257 if (aErr == KErrNone) |
|
1258 { |
|
1259 CBTInqResultRef* ref = AddEntryToCache(aAddr); |
|
1260 if(ref) |
|
1261 { |
|
1262 CBTInqResultRecord& rec = ref->Result(); |
|
1263 |
|
1264 rec.SetName(aBuf); |
|
1265 rec.SetNameComplete(ETrue); |
|
1266 rec.iFlushes = 0; |
|
1267 rec.SetNameValid(ETrue); |
|
1268 rec.SetNameRefreshRequested(EFalse); |
|
1269 } |
|
1270 } |
|
1271 } |
|
1272 |
|
1273 /** |
|
1274 Remote host supported features notification received. |
|
1275 |
|
1276 The remote host supported feature notification may be received during a |
|
1277 remote name request if the remote device provides entries in the remote |
|
1278 host supported feature page. |
|
1279 |
|
1280 @param aErr An EPOC error code. Any HCI mapping should be done before |
|
1281 calling this function. |
|
1282 @param aAddr The address of the Bluetooth device that the name is associated |
|
1283 with. |
|
1284 @param aHostSupportedFeatures 64 bit field holding the remote host supported |
|
1285 features page. |
|
1286 */ |
|
1287 void CBTInquiryMgr::RemoteHostSupportedFeatures(TInt /*aErr*/, const TBTDevAddr& /*aAddr*/, const TUint64& /*aHostSupportedFeatures*/) |
|
1288 { |
|
1289 LOG_FUNC |
|
1290 // This is currently not used by the stack since the optimisation it could provide would be |
|
1291 // difficult to take advantage of and of minimal use. |
|
1292 |
|
1293 // If to be used the result should be stored in the inquiry result record along with whether |
|
1294 // a name request has been made (with EIR it is possible to have retrieved the name without |
|
1295 // a name request) |
|
1296 } |
|
1297 |
|
1298 /** |
|
1299 Name lookup result received. |
|
1300 |
|
1301 We need to demultiplex these out to all CBTHostResolver's |
|
1302 interested in them. |
|
1303 |
|
1304 @param aErr An EPOC error code. Any HCI mapping should be done before |
|
1305 calling this function. |
|
1306 @param aRef The inquiry result for this info. |
|
1307 @param aBuf 8 bit Descriptor up to KHCIRemoteDeviceNameMaxLength |
|
1308 bytes long, containing the name. |
|
1309 */ |
|
1310 void CBTInquiryMgr::HandleRemoteNameResult(TInt aErr, CBTInqResultRef& aRef, const TBTDeviceName8& aBuf) |
|
1311 { |
|
1312 LOG_FUNC |
|
1313 |
|
1314 if(iHWState != EConnecting) |
|
1315 { |
|
1316 SetHWState(EIdle); |
|
1317 // don't set publish status here, doinquiry will do that |
|
1318 } |
|
1319 |
|
1320 if(aErr == KErrNone) |
|
1321 { |
|
1322 if(iNamePageTimeout > KMinNamePageTimeout) |
|
1323 {// got name OK, dec the page timeout |
|
1324 iNamePageTimeout -= KNamePageTimeoutIncrement; |
|
1325 } |
|
1326 } |
|
1327 else if(aErr == KHCIErrorBase - EPageTimedOut) |
|
1328 { |
|
1329 if(iNamePageTimeout < KMaxNamePageTimeout) |
|
1330 {// Got pagetime, inc. pagetime a bit |
|
1331 iNamePageTimeout += KNamePageTimeoutIncrement; |
|
1332 } |
|
1333 } |
|
1334 |
|
1335 |
|
1336 CBTInqResultRecord& rec = aRef.Result(); |
|
1337 |
|
1338 --iPendingNameRequests; |
|
1339 rec.SetNamePending(EFalse); |
|
1340 ++(rec.iNameLookupAttempts); |
|
1341 if(iPendingNameRequests <= 0) |
|
1342 { |
|
1343 iNewPageRequestsPending = EFalse; |
|
1344 } |
|
1345 |
|
1346 // Update our cached version. |
|
1347 if (aErr == KErrNone) |
|
1348 { |
|
1349 rec.SetName(aBuf); |
|
1350 rec.SetNameComplete(ETrue); |
|
1351 rec.iFlushes = 0; |
|
1352 rec.SetNameValid(ETrue); |
|
1353 rec.SetNameRefreshRequested(EFalse); |
|
1354 rec.SetExplicitNameRequest(EFalse); // If we had an explicit name request on this, we've completed it now |
|
1355 } |
|
1356 else |
|
1357 { |
|
1358 // got an error |
|
1359 if(rec.NameLookupAttempts() < KMaxNameLookupAttempts) |
|
1360 {// try to get later - put to back of Que for getting |
|
1361 ++iPendingNameRequests; |
|
1362 rec.SetNamePending(ETrue); |
|
1363 iCurrentResults.MoveToback(aRef); |
|
1364 } |
|
1365 else |
|
1366 {// No more chances -- record lookup error code |
|
1367 rec.SetNameLookupResultCode(aErr); |
|
1368 rec.SetExplicitNameRequest(EFalse); // If we had an explicit name request on this, we've completed it now |
|
1369 } |
|
1370 } |
|
1371 |
|
1372 // Try to get more names, if needed |
|
1373 LOG(_L("CBTInquiryMgr::HandleRemoteNameResult asking for another name lookup")); |
|
1374 DoNameLookup(EFalse); |
|
1375 |
|
1376 if(!aRef.Result().IsNameRequestPending()) |
|
1377 {// Don't propogate if name request is still pending |
|
1378 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1379 while (iter) |
|
1380 { |
|
1381 (iter++)->NameLookupResult(aErr, rec.LogEntry().iBdaddr, aBuf); |
|
1382 } |
|
1383 } |
|
1384 |
|
1385 // In case we're now free, do inquiry |
|
1386 LOG(_L("CBTInquiryMgr::HandleRemoteNameResult asking for another inquiry")); |
|
1387 if (iPendingNameRequests == 0 && iRequestedInquiryIAC == 0) // If we've completed the current inquiry, see if we've got another one queued. |
|
1388 { |
|
1389 iRequestedInquiryIAC = iQueuedInquiryIAC; |
|
1390 iQueuedInquiryIAC = 0; |
|
1391 } |
|
1392 DoInquiry(); |
|
1393 } |
|
1394 |
|
1395 const TDesC8* CBTInquiryMgr::DeviceNameFromCache(const TBTDevAddr& aAddr) |
|
1396 /** |
|
1397 Used by other parts of the stack to get a name synchronously from cache if |
|
1398 there is one |
|
1399 |
|
1400 @return NULL if device is unknwon |
|
1401 **/ |
|
1402 { |
|
1403 LOG_FUNC |
|
1404 CBTInqResultRef* entry = FindExistingCacheEntry(aAddr); |
|
1405 if (!entry) |
|
1406 return NULL; |
|
1407 else |
|
1408 { |
|
1409 return &entry->Result().Name(); |
|
1410 } |
|
1411 } |
|
1412 |
|
1413 CBTInqResultRecord* CBTInquiryMgr::BasebandParametersFromCache(const TBTDevAddr& aAddr) |
|
1414 { |
|
1415 LOG_FUNC |
|
1416 CBTInqResultRef* entry = FindExistingCacheEntry(aAddr); |
|
1417 if (!entry) |
|
1418 return NULL; |
|
1419 else |
|
1420 { |
|
1421 return &(entry->Result()); |
|
1422 } |
|
1423 } |
|
1424 |
|
1425 void CBTInquiryMgr::SetHWState(THWState aState) |
|
1426 { |
|
1427 LOG_FUNC |
|
1428 iHWState = aState; |
|
1429 } |
|
1430 |
|
1431 void CBTInquiryMgr::PublishStatus() |
|
1432 { |
|
1433 LOG_FUNC |
|
1434 // sets the inquiry/discovery status to bool true or false |
|
1435 TBool inquiryState = EFalse; |
|
1436 |
|
1437 switch (iHWState) |
|
1438 { |
|
1439 /* deliberate fall throughs */ |
|
1440 case EInquiry: |
|
1441 case ENameLookup: |
|
1442 { |
|
1443 inquiryState = ETrue; |
|
1444 } |
|
1445 break; |
|
1446 case EIdle: |
|
1447 case EConnecting: |
|
1448 case EOff: |
|
1449 default: |
|
1450 { |
|
1451 // defaulted above to EFalse; |
|
1452 } |
|
1453 break; |
|
1454 } |
|
1455 /* only publish status if we've changed |
|
1456 it is however the case that if the state changes sufficiently quickly |
|
1457 the subscriber will miss some of the state changes |
|
1458 this is particularly the case while doing name searches where we toggle into |
|
1459 idle and back to namelookup quickly and regularly, the subscriber |
|
1460 sees a stream of etrues coming through, the efalses are missing |
|
1461 this is probably ok as long as they know because this means they are told |
|
1462 more than once they are inquiring, and indeed they are, its certainly better |
|
1463 than being told they're not if they are */ |
|
1464 if (inquiryState != iReportedInquiryState) |
|
1465 { |
|
1466 iLinkMgrProtocol.SetUIDiscovering(inquiryState); |
|
1467 iReportedInquiryState = inquiryState; |
|
1468 if (inquiryState) |
|
1469 { |
|
1470 LOG(_L("CBTInquiryMgr::PublishState setting status TRUE")); |
|
1471 } |
|
1472 else |
|
1473 { |
|
1474 LOG(_L("CBTInquiryMgr::PublishState setting status FALSE")); |
|
1475 } |
|
1476 } |
|
1477 else |
|
1478 { |
|
1479 LOG(_L("CBTInquiryMgr::PublishState not updating status")); |
|
1480 } |
|
1481 } |
|
1482 |
|
1483 void CBTInquiryMgr::SetLocalNameComplete(TInt aErr) |
|
1484 { |
|
1485 LOG_FUNC |
|
1486 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1487 while (iter) |
|
1488 { |
|
1489 (iter++)->SetLocalNameComplete(aErr); |
|
1490 } |
|
1491 } |
|
1492 |
|
1493 void CBTInquiryMgr::GetLocalNameComplete(TInt aErr, const TDesC8& aName) |
|
1494 { |
|
1495 LOG_FUNC |
|
1496 TDblQueIter<CBTHostResolver> iter(iHRs); |
|
1497 while (iter) |
|
1498 { |
|
1499 (iter++)->GetLocalNameComplete(aErr, aName); |
|
1500 } |
|
1501 } |
|
1502 |
|
1503 void CBTInquiryMgr::StartInquiry(CBTHostResolver& aResolver, TUint aIAC, TBool aIgnoreCache) |
|
1504 { |
|
1505 LOG_FUNC |
|
1506 // Pre-load any existing results into the CBTHostResolver |
|
1507 if (!aIgnoreCache) |
|
1508 { |
|
1509 // Pre-load any existing results into the CBTHostResolver |
|
1510 iCurrentResults.ReturnToFirstResult(); |
|
1511 while (CBTInqResultRef* ref = iCurrentResults.NextResult()) |
|
1512 { |
|
1513 // If GIAC is being requested, we return all results |
|
1514 if (aIAC == KGIAC || ref->Result().HasRespondedToIAC(aIAC)) |
|
1515 { |
|
1516 aResolver.InquiryResult(ref->Result()); |
|
1517 } |
|
1518 } |
|
1519 } |
|
1520 |
|
1521 if ( !aIgnoreCache && !iCurrentResults.IsEmpty() && CacheAge(aIAC) <= KCacheStaleAge) |
|
1522 {// Cache not yet stale, so just give these results back and dont start |
|
1523 LOG(_L("CBTInquiryMgr::StartInquiry giving result from cache")); |
|
1524 aResolver.InquiryComplete(KErrNone); |
|
1525 return; |
|
1526 } |
|
1527 |
|
1528 if (iRequestedInquiryIAC || iQueuedInquiryIAC) |
|
1529 { |
|
1530 LOG(_L("CBTInquiryMgr::StartInquiry iRequestedInquiryIAC")); |
|
1531 if(iRequestedInquiryIAC == aIAC) |
|
1532 { |
|
1533 // an Inquiry is ongoing, return any results already found during the |
|
1534 // current Inquiry if not already done so as part of the complete cache |
|
1535 if (aIgnoreCache) |
|
1536 { |
|
1537 iCurrentResults.ReturnToFirstResult(); |
|
1538 while (CBTInqResultRef* ref = iCurrentResults.NextResult()) |
|
1539 { |
|
1540 if (ref->Result().iFoundDuringCurrentInquiry) |
|
1541 { |
|
1542 aResolver.InquiryResult(ref->Result()); |
|
1543 } |
|
1544 } |
|
1545 } |
|
1546 // the current Inquiry will just continue |
|
1547 return; |
|
1548 } |
|
1549 if (aIAC == KGIAC) |
|
1550 { |
|
1551 // If the current IAC is GIAC, and the requested inquiry IAC isn't, it must be LIAC |
|
1552 __ASSERT_DEBUG(iRequestedInquiryIAC == KLIAC, Panic(EBTUnexpectedIAC)); |
|
1553 // Queue a general inquiry for when the current limited inquiry has finished |
|
1554 iQueuedInquiryIAC = aIAC; |
|
1555 return; |
|
1556 } |
|
1557 else if(iHWState == EInquiry) |
|
1558 { |
|
1559 // The host resolver should only allow through GIAC and LIAC, and we handle GIAC above |
|
1560 __ASSERT_DEBUG(aIAC == KLIAC, Panic(EBTUnexpectedIAC)); |
|
1561 // We favour a Limited inqiury, so interrupt the current general inquiry |
|
1562 TInt err = CancelHardwareInquiry(); |
|
1563 if(err!=KErrNone) |
|
1564 { |
|
1565 LOG(_L("CBTInquiryMgr::StartInquiry cancel didn't work")); |
|
1566 aResolver.InquiryComplete(err); // cancel went wrong |
|
1567 return; |
|
1568 } |
|
1569 // Queue a general inquiry for when the limited inquiry is complete |
|
1570 iRequestedInquiryIAC = KLIAC; |
|
1571 iQueuedInquiryIAC = KGIAC; |
|
1572 SetHWState(ECancellingForNewIAC); |
|
1573 iFlusher->Cancel(); // Stop watchdog, start flusher. |
|
1574 return; |
|
1575 } |
|
1576 else |
|
1577 { |
|
1578 // The host resolver should only allow through GIAC and LIAC, and we handle GIAC above |
|
1579 __ASSERT_DEBUG(aIAC == KLIAC, Panic(EBTUnexpectedIAC)); |
|
1580 iRequestedInquiryIAC = KLIAC; |
|
1581 iQueuedInquiryIAC = KGIAC; |
|
1582 } |
|
1583 } |
|
1584 |
|
1585 iRequestedInquiryIAC = aIAC; |
|
1586 iInquiryInteruptions = 0; |
|
1587 |
|
1588 DoInquiry(); |
|
1589 } |
|
1590 |
|
1591 void CBTInquiryMgr::Suspend() |
|
1592 /** |
|
1593 Ah - somebody wishes to make a connection - we need to clear the way |
|
1594 If we are doing a namerequest we can cancel. |
|
1595 |
|
1596 WARNING - The caller of this must ensure a call to Resume() ASAP |
|
1597 **/ |
|
1598 { |
|
1599 LOG_FUNC |
|
1600 |
|
1601 if(iHWState == EInquiry) |
|
1602 { |
|
1603 // This is a best effort service, the Connect will be tried even |
|
1604 // if we fail to stop the inquiry, so ignore errors. |
|
1605 static_cast<void>(CancelHardwareInquiry()); |
|
1606 } |
|
1607 else if (iHWState == ENameLookup) |
|
1608 { |
|
1609 |
|
1610 iCurrentResults.ReturnToFirstResult(); |
|
1611 while(CBTInqResultRef *ref = iCurrentResults.NextResult()) |
|
1612 { |
|
1613 |
|
1614 CBTInqResultRecord& rec = ref->Result(); |
|
1615 if(rec.IsNameRequestPending()) |
|
1616 { |
|
1617 // This is a best effort service, the Connect will be tried even |
|
1618 // if we fail to stop the Name request, so ignore errors. |
|
1619 TRAP_IGNORE(CancelRemoteNameL(rec.LogEntry().iBdaddr)); |
|
1620 } |
|
1621 } |
|
1622 |
|
1623 } |
|
1624 |
|
1625 iFlusher->Cancel(); // Stop watchdog, start flusher. |
|
1626 SetHWState(EConnecting); // will stop inquirymanager doing anything else |
|
1627 // don't publish status here, will be cleared up when manager tries to do soemthing else |
|
1628 EnableFlusher(); |
|
1629 } |
|
1630 |
|
1631 void CBTInquiryMgr::Resume() |
|
1632 /** |
|
1633 We can now carry on... |
|
1634 **/ |
|
1635 { |
|
1636 LOG_FUNC |
|
1637 if(iHWState == EConnecting) |
|
1638 { |
|
1639 LOG(_L("InquiryMgr: Hardware connected; resuming operations (when a new inquiry starts")); |
|
1640 SetHWState(EIdle); |
|
1641 // don't publish status here, it will be set by namelookup |
|
1642 LOG(_L("CBTInquiryMgr::Resume asking for another name lookup")); |
|
1643 DoNameLookup(EFalse); |
|
1644 DoInquiry(); |
|
1645 EnableFlusher(); // in case no operations started |
|
1646 } |
|
1647 } |
|
1648 |
|
1649 void CBTInquiryMgr::LookupName(CBTHostResolver& aResolver, const TBTDevAddr& aAddr, TBool aIgnoreCache, TBool aExplicitNameRequest) |
|
1650 /** |
|
1651 This method is called by a CBTHostResolver to look up the name of a device with a given address. If the |
|
1652 name is not immediately available a name lookup request is queued within the inquiry manager. This |
|
1653 request will be actioned by CBTInquiryMgr::DoNameLookup(), and the remote device response will be |
|
1654 sent (by CBTInquiryMgr::RemoteNameResult) to all current host resolvers. |
|
1655 **/ |
|
1656 { |
|
1657 LOG_FUNC |
|
1658 LOG1(_L("CBTHostResolver: 0x%8x"), &aResolver); |
|
1659 LOG6(_L(", aAddr: %02x %02x %02x %02x %02x %02x"), |
|
1660 TUint8(aAddr[0]), TUint8(aAddr[1]), TUint8(aAddr[2]), TUint8(aAddr[3]), TUint8(aAddr[4]), |
|
1661 TUint8(aAddr[5])); |
|
1662 |
|
1663 CBTInqResultRef* ref = AddEntryToCache(aAddr); //N.B. Creates a new entry if one doesn't exist |
|
1664 |
|
1665 if(!ref) |
|
1666 { |
|
1667 aResolver.NameLookupResult(KErrNoMemory, aAddr, KNullDesC8); |
|
1668 return; |
|
1669 } |
|
1670 |
|
1671 // check state of cache entry |
|
1672 CBTInqResultRecord& rec = ref->Result(); |
|
1673 |
|
1674 if(rec.HaveNameLookupResult() && rec.IsNameComplete() && (!aIgnoreCache|| rec.iFoundDuringCurrentInquiry)) |
|
1675 {// Either got a name, or we made an attempt that failed |
|
1676 aResolver.NameLookupResult(rec.NameLookupResultCode(), |
|
1677 rec.LogEntry().iBdaddr, |
|
1678 rec.iName); |
|
1679 return; |
|
1680 } |
|
1681 |
|
1682 if (aExplicitNameRequest) |
|
1683 { |
|
1684 rec.SetExplicitNameRequest(ETrue); |
|
1685 } |
|
1686 |
|
1687 if(rec.IsNameRequestPending()) |
|
1688 { |
|
1689 return; // it's already happening |
|
1690 } |
|
1691 |
|
1692 rec.SetNamePending(ETrue); |
|
1693 rec.iNameLookupAttempts = 0; |
|
1694 ++iPendingNameRequests; |
|
1695 iNewPageRequestsPending = ETrue; |
|
1696 |
|
1697 if(iHWState == EInquiry) |
|
1698 { |
|
1699 if(iPendingNameRequests > iInquiryInteruptions |
|
1700 || iInquiryInteruptions < KImmediateNameFetch) |
|
1701 { |
|
1702 TryToInterruptInquiryForNameLookup(); |
|
1703 } |
|
1704 } |
|
1705 else |
|
1706 { |
|
1707 LOG(_L("CBTInquiryMgr::LookupName asking for another name lookup")); |
|
1708 DoNameLookup(EFalse); |
|
1709 } |
|
1710 } |
|
1711 |
|
1712 void CBTInquiryMgr::ClearCache() |
|
1713 { |
|
1714 LOG_FUNC |
|
1715 if(iHWState == EInquiry) |
|
1716 { |
|
1717 TInt err = CancelHardwareInquiry(); |
|
1718 if(err==KErrNone) |
|
1719 { |
|
1720 // Don't clear cache if we couldn't cancel inquiry. |
|
1721 // We'll be slightly unfaithful in the case of OOM... |
|
1722 iCurrentResults.Reset(); |
|
1723 InquiryComplete(KErrNone, 0); |
|
1724 } |
|
1725 } |
|
1726 else |
|
1727 { |
|
1728 iCurrentResults.Reset(); |
|
1729 } |
|
1730 } |
|
1731 |
|
1732 TInt CBTInquiryMgr::SetLocalName(const TDesC8& aName) |
|
1733 { |
|
1734 LOG_FUNC |
|
1735 return iLinkMgrProtocol.SetLocalDeviceName(aName); |
|
1736 } |
|
1737 |
|
1738 TInt CBTInquiryMgr::GetLocalName() |
|
1739 { |
|
1740 LOG_FUNC |
|
1741 TRAPD(err, ReadLocalNameL()); |
|
1742 return err; |
|
1743 } |
|
1744 |
|
1745 /** |
|
1746 This is called whenever the local HCI version is updated (usually only during startup), |
|
1747 so that the inquiry mode is set accordingly |
|
1748 **/ |
|
1749 void CBTInquiryMgr::SetInquiryMode() |
|
1750 { |
|
1751 LOG_FUNC |
|
1752 |
|
1753 THCIInquiryMode inquiryMode = EStandardInquiryFormat; |
|
1754 |
|
1755 if (iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally()) |
|
1756 { |
|
1757 inquiryMode = EInquiryWithRssiOrEir; |
|
1758 } |
|
1759 else if (iLinkMgrProtocol.IsRssiWithInquiryResultsSupportedLocally()) |
|
1760 { |
|
1761 inquiryMode = EInquiryWithRssi; |
|
1762 } |
|
1763 |
|
1764 if (inquiryMode != EStandardInquiryFormat) |
|
1765 { |
|
1766 TRAPD(err, WriteInquiryModeL(inquiryMode)); |
|
1767 if (err == KErrNone) |
|
1768 { |
|
1769 iPendingInquiryMode = inquiryMode; |
|
1770 } |
|
1771 } |
|
1772 } |
|
1773 /** |
|
1774 This is called whenever the pending WriteInquiryMode command has completed. |
|
1775 **/ |
|
1776 void CBTInquiryMgr::WriteInquiryModeComplete(TBool aSucceeded) |
|
1777 { |
|
1778 LOG_FUNC |
|
1779 if (aSucceeded) |
|
1780 { |
|
1781 iInquiryMode = iPendingInquiryMode; |
|
1782 return; |
|
1783 } |
|
1784 |
|
1785 // should we maybe try to set another mode? Probably worth a try... |
|
1786 // let's downgrade the inquiry mode |
|
1787 if (iPendingInquiryMode == EInquiryWithRssiOrEir) |
|
1788 { |
|
1789 TRAPD(err, WriteInquiryModeL(EInquiryWithRssi)); |
|
1790 if (err == KErrNone) |
|
1791 { |
|
1792 iPendingInquiryMode = EInquiryWithRssi; |
|
1793 } |
|
1794 } |
|
1795 } |
|
1796 |
|
1797 void CBTInquiryMgr::DoInquiry() |
|
1798 /** |
|
1799 Try to start the inquiry. |
|
1800 Only starts it if there isn't already one going. |
|
1801 **/ |
|
1802 { |
|
1803 LOG_FUNC |
|
1804 |
|
1805 if(iRequestedInquiryIAC == 0 || iHWState != EIdle || iHRs.IsEmpty()) |
|
1806 { |
|
1807 #ifdef _DEBUG |
|
1808 LOG3(_L("Not starting inquiry. iRequestedInquiryIAC == %d, iHWState == %d, iHostResolverCount == %d"), iRequestedInquiryIAC, iHWState, iNumHRs); |
|
1809 if (iHRs.IsEmpty()) |
|
1810 { |
|
1811 LOG(_L("No HRs interested in results - Stopping discovery")); |
|
1812 } |
|
1813 #endif |
|
1814 if (iHWState == EIdle) |
|
1815 { |
|
1816 LOG(_L("CBTInquiryMgr::DoInquiry PublishStatus Idle")); |
|
1817 PublishStatus();// make sure the status says we're idle |
|
1818 } |
|
1819 return; |
|
1820 } |
|
1821 |
|
1822 // If pending on name lookup, we'll do it when that finishes |
|
1823 |
|
1824 // Cache stale -- update it. |
|
1825 TInt err = StartHardwareInquiry(); |
|
1826 |
|
1827 if (err == KErrNone) |
|
1828 { |
|
1829 SetHWState(EInquiry); |
|
1830 LOG(_L("CBTInquiryMgr::DoInquiry PublishState Inquiry")); |
|
1831 PublishStatus(); |
|
1832 // Use cache age to estimate time elapsed while inquiring. |
|
1833 // Synchronise it with the inquiry |
|
1834 // Stop flusher, Start inquiry wathcdog |
|
1835 iFlusher->Cancel(); |
|
1836 TCallBack cb(InquiryWatchdog, this); |
|
1837 iFlusher->Start(KInquiryWatchdogPeriod * 1000000, |
|
1838 KInquiryWatchdogPeriod * 1000000, |
|
1839 cb); |
|
1840 SetCacheAge(iCurrentInquiryIAC, 0); |
|
1841 iInquirySilenceCount = 0; |
|
1842 iResultCount = 0; |
|
1843 } |
|
1844 else |
|
1845 { |
|
1846 // Couldn't start inquiry. |
|
1847 // Make sure the request is completed. |
|
1848 iRequestedInquiryIAC = 0; |
|
1849 InquiryComplete(err, 0); |
|
1850 LOG(_L("CBTInquiryMgr::DoInquiry PublishState Idle couldn't start inquiry")); |
|
1851 PublishStatus(); |
|
1852 return; |
|
1853 } |
|
1854 } |
|
1855 |
|
1856 void CBTInquiryMgr::ClearCurrentInquiryResults() |
|
1857 { |
|
1858 LOG_FUNC |
|
1859 iCurrentResults.ReturnToFirstResult(); |
|
1860 while (CBTInqResultRef* ref = iCurrentResults.NextResult()) |
|
1861 { |
|
1862 ref->Result().iFoundDuringCurrentInquiry = EFalse; |
|
1863 } |
|
1864 } |
|
1865 |
|
1866 TInt CBTInquiryMgr::CancelHardwareInquiry() |
|
1867 { |
|
1868 LOG_FUNC |
|
1869 // CancelInquiryL will stop the current inquiry so clear any results that |
|
1870 // were marked found during the inquiry |
|
1871 ClearCurrentInquiryResults(); |
|
1872 |
|
1873 TRAPD(err, CancelInquiryL()); |
|
1874 return err; |
|
1875 } |
|
1876 |
|
1877 TInt CBTInquiryMgr::StartHardwareInquiry() |
|
1878 { |
|
1879 LOG_FUNC |
|
1880 iCurrentInquiryIAC = iRequestedInquiryIAC; |
|
1881 // attempt to free up baseband space for best performance discovery |
|
1882 iLinkMgrProtocol.PhysicalLinksMgr().RequireSlotSpace(); // will *eventually* put connections in hold - we dont wait though |
|
1883 TRAPD(err, StartInquiryL(iCurrentInquiryIAC, KInquiryLength, KInquiryMaxResults)); |
|
1884 |
|
1885 return err; |
|
1886 } |
|
1887 |
|
1888 |
|
1889 |
|
1890 |
|
1891 void CBTInquiryMgr::DoNameLookup(TBool aInquiryComplete) |
|
1892 /** |
|
1893 This method attempts to issue a name lookup to the HCI |
|
1894 **/ |
|
1895 { |
|
1896 LOG_FUNC |
|
1897 |
|
1898 if(iHWState != EIdle || iPendingNameRequests == 0 || iHRs.IsEmpty()) |
|
1899 { |
|
1900 #ifdef _DEBUG |
|
1901 LOG3(_L("Not starting name lookup. iCurrentInquiryIAC == %d, iHWState == %d, iHostResolverCount == %d"), iCurrentInquiryIAC, iHWState, iNumHRs); |
|
1902 #endif |
|
1903 /* if hw is idle and the inquiry is ready we are finished |
|
1904 and will not get another inquiry call so publish status idle |
|
1905 note that if doing 'inquiry with names' |
|
1906 in inquiry the aInquiryComplete flag stops state oscilating |
|
1907 thostres name request for all devices will still cause this to |
|
1908 oscilate because each name request is done individually, at this layer |
|
1909 we have no idea whether the test program will ask for another name */ |
|
1910 if ((iHWState == EIdle) && (aInquiryComplete)) |
|
1911 { |
|
1912 LOG(_L("CBTInquiryMgr::DoNameLookup inquiry finished with")); |
|
1913 PublishStatus(); |
|
1914 } |
|
1915 // else publish status will be sorted by next inquiry |
|
1916 return; |
|
1917 } |
|
1918 |
|
1919 iCurrentResults.ReturnToFirstResult(); |
|
1920 CBTInqResultRef *refToGet = NULL; |
|
1921 while(CBTInqResultRef *ref = iCurrentResults.NextResult()) |
|
1922 { |
|
1923 CBTInqResultRecord& rec = ref->Result(); |
|
1924 if(rec.IsNameRequestPending()) |
|
1925 { |
|
1926 if(!iRequestedInquiryIAC || |
|
1927 rec.NameLookupAttempts() < KMaxNameLookupAttemptsDuringInquiry) |
|
1928 { |
|
1929 // We want the first record for the current IAC or a record for an explicit name request. |
|
1930 // Failing that, we'll just have the first record |
|
1931 if (rec.HasRespondedToIAC(iCurrentInquiryIAC) || rec.IsExplicitNameRequest()) |
|
1932 { |
|
1933 refToGet = ref; |
|
1934 break; |
|
1935 } |
|
1936 if (!refToGet) |
|
1937 { |
|
1938 refToGet = ref; |
|
1939 } |
|
1940 } |
|
1941 } |
|
1942 } |
|
1943 if (refToGet) |
|
1944 { |
|
1945 CBTInqResultRecord& rec = refToGet->Result(); |
|
1946 |
|
1947 // First set the page timeout, it is a best effort attempt |
|
1948 iNamePageTimeout = iLinkMgrProtocol.PhysicalLinksMgr().TryToChangePageTimeout(iNamePageTimeout); |
|
1949 |
|
1950 // Request the name lookup. |
|
1951 TRAPD(err, LookupNameL(rec.LogEntry())); |
|
1952 |
|
1953 if (err == KErrNone) |
|
1954 { |
|
1955 SetHWState(ENameLookup); |
|
1956 return; |
|
1957 } |
|
1958 else |
|
1959 { |
|
1960 // got a synchronous error - put in a blank name |
|
1961 HandleRemoteNameResult(err, *refToGet, KNullDesC8()); |
|
1962 } |
|
1963 } |
|
1964 // Nothing new to do! |
|
1965 iNewPageRequestsPending = EFalse; |
|
1966 } |
|
1967 |
|
1968 void CBTInquiryMgr::TryToInterruptInquiryForNameLookup() |
|
1969 { |
|
1970 LOG_FUNC |
|
1971 TInt err = CancelHardwareInquiry(); |
|
1972 |
|
1973 if(err == KErrNone) |
|
1974 {// Start doing name lookups |
|
1975 iFlusher->Cancel(); //Stop watchdog, start flusher |
|
1976 SetHWState(EIdle); |
|
1977 // don't publish status here, namelookup/inquiry will deal with it |
|
1978 EnableFlusher(); |
|
1979 ++iInquiryInteruptions; |
|
1980 LOG(_L("CBTInquiryMgr::TryToInterruptInquiryForNameLookup asking for another name lookup")); |
|
1981 DoNameLookup(EFalse); |
|
1982 DoInquiry(); // <- In case name lookup fails |
|
1983 } |
|
1984 } |
|
1985 |
|
1986 // An inelegant workaround called from CLinkMgrProtocol::Error to get round |
|
1987 // the fact that the stack doesn't (yet) keep track of commands that it has |
|
1988 // been asked to do. If they aren't completed on a reset due to a |
|
1989 // KErrHardwareNotFound error then the stack hangs as it waits for the responses. |
|
1990 void CBTInquiryMgr::CompleteCommands(TInt aErr) |
|
1991 { |
|
1992 LOG_FUNC |
|
1993 SetLocalNameComplete(aErr); |
|
1994 GetLocalNameComplete(aErr, _L8("")); |
|
1995 |
|
1996 // Reset any inquiry results left over from a previous inquiry |
|
1997 iCurrentResults.Reset(); |
|
1998 InquiryComplete(aErr, 0); |
|
1999 } |
|
2000 |
|
2001 |
|
2002 // Check whether the controller supports Extended Inquiry Response by examing |
|
2003 // the supported features mask using Link Manage Protocol |
|
2004 TBool CBTInquiryMgr::IsExtendedInquiryResponseSupported() |
|
2005 { |
|
2006 LOG_FUNC |
|
2007 return iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally(); |
|
2008 } |
|
2009 |
|
2010 /******************************************************************************/ |
|
2011 /* If UPDATING THE CACHE, please use one of the "AddEntry..." methods below. */ |
|
2012 /******************************************************************************/ |
|
2013 CBTInqResultRef* CBTInquiryMgr::FindExistingCacheEntry(const TBTDevAddr& aAddr) |
|
2014 { |
|
2015 LOG_FUNC |
|
2016 return(iCurrentResults.FindEntry(aAddr)); |
|
2017 } |
|
2018 |
|
2019 CBTInqResultRef* CBTInquiryMgr::AddEntryToCache(const TBTDevAddr& aAddr) |
|
2020 { |
|
2021 LOG_FUNC |
|
2022 CBTInqResultRef* ref = FindExistingCacheEntry(aAddr); |
|
2023 if(ref) |
|
2024 {// Already in there! |
|
2025 return ref; |
|
2026 } |
|
2027 |
|
2028 CBTInqResultRecord* rec = new CBTInqResultRecord(aAddr); |
|
2029 if (!rec) |
|
2030 { |
|
2031 return 0; |
|
2032 } |
|
2033 |
|
2034 ref = iCurrentResults.Add(*rec); |
|
2035 if (!ref) |
|
2036 { |
|
2037 delete rec; |
|
2038 return 0; |
|
2039 } |
|
2040 |
|
2041 //default CBTInqResultRecord values - provided in CBTInqResultRecord constructer (unlike v1) |
|
2042 //maybe overwritten later |
|
2043 |
|
2044 EnableFlusher(); // Make sure we're aging entries. |
|
2045 return ref; |
|
2046 } |
|
2047 |
|
2048 CBTInqResultRef* CBTInquiryMgr::AddEntryWithJuiceToCache(const TInquiryLogEntry& aEntry) |
|
2049 /** |
|
2050 This method should be used if you are supplying cache with all valid |
|
2051 baseband parameters. |
|
2052 Setting the valid bit in clock offset allows us to tell btman that the clock |
|
2053 offset value, and by inferrence the other juice values are valid - |
|
2054 i.e. have been supplied by the HCI, as opposed to having been supplied |
|
2055 as default. |
|
2056 **/ |
|
2057 { |
|
2058 LOG_FUNC |
|
2059 CBTInqResultRef* ref = AddEntryToCache(aEntry.iBdaddr); |
|
2060 |
|
2061 if (ref) |
|
2062 { |
|
2063 CBTInqResultRecord& rec = ref->Result(); |
|
2064 // Update all, and register that values come from HCI |
|
2065 rec.iJuiceFromHCIMask |= EBluetoothJuice; |
|
2066 // First fill in the base class members, common to all inquiry results |
|
2067 *static_cast<TInquiryLogEntry*>(&rec.iEntry) = aEntry; |
|
2068 // Now complete with extra data depending on the inquiry result type |
|
2069 switch(aEntry.iSpare) |
|
2070 { |
|
2071 case KInqLogEntryStandard: |
|
2072 break; |
|
2073 case KInqLogEntryWithEir: |
|
2074 { |
|
2075 const TInquiryLogEntryWithEir& entryEir = static_cast<const TInquiryLogEntryWithEir &>(aEntry); |
|
2076 // Only replace EIR if it contains something, since sometimes EIR transmission fails |
|
2077 if(entryEir.iExtendedInquiryResponse.Length() != 0) |
|
2078 { |
|
2079 rec.iEntry.iExtendedInquiryResponse = entryEir.iExtendedInquiryResponse; |
|
2080 } |
|
2081 rec.iJuiceFromHCIMask |= EBluetoothEir; |
|
2082 // Now check whether we can extract a complete or partial friendly name from the EIR and add it to the cache |
|
2083 TPtrC8 name; |
|
2084 TBool complete = ETrue; |
|
2085 rec.Codec().Set(rec.iEntry.iExtendedInquiryResponse); |
|
2086 // use EirCodec doing sanity check of the eir data |
|
2087 TInt error = rec.Codec().DoSanityCheck(rec.iEntry.iExtendedInquiryResponse); |
|
2088 if(error == KErrNone) |
|
2089 { |
|
2090 error = rec.Codec().GetDeviceName(name); |
|
2091 if(error >= KErrNone) |
|
2092 { |
|
2093 if(error == EDataPartial) |
|
2094 { |
|
2095 complete = EFalse; |
|
2096 } |
|
2097 // Do not overwrite complete names with partial ones |
|
2098 if(complete || !rec.IsNameValid() || (rec.IsNameValid() && !rec.IsNameComplete())) |
|
2099 { |
|
2100 rec.SetName(name); |
|
2101 rec.SetNameComplete(complete); |
|
2102 rec.iFlushes = 0; |
|
2103 rec.SetNameValid(ETrue); |
|
2104 rec.SetNameRefreshRequested(EFalse); |
|
2105 } |
|
2106 } |
|
2107 } |
|
2108 } |
|
2109 // Fall through |
|
2110 case KInqLogEntryWithRssi: |
|
2111 { |
|
2112 const TInquiryLogEntryWithRssi& entryRssi = static_cast<const TInquiryLogEntryWithRssi &>(aEntry); |
|
2113 rec.iEntry.iRssi = entryRssi.iRssi; |
|
2114 rec.iJuiceFromHCIMask |= EBluetoothRssi; |
|
2115 break; |
|
2116 } |
|
2117 default: |
|
2118 __ASSERT_DEBUG(EFalse, Panic(EBTInvalidInquiryLogEntry)); |
|
2119 break; |
|
2120 } |
|
2121 // Mark as valid, HCI does not (necessarily) do this for you |
|
2122 rec.iEntry.iClockOffset |= KHCIClockOffsetValidBit; |
|
2123 } |
|
2124 |
|
2125 return ref; |
|
2126 } |
|
2127 |
|
2128 CBTInqResultRef* CBTInquiryMgr::AddEntryWithCoDToCache(const TBTDevAddr& aAddr, const TUint aCoD) |
|
2129 { |
|
2130 LOG_FUNC |
|
2131 CBTInqResultRef* ref = AddEntryToCache(aAddr); |
|
2132 |
|
2133 if (ref) |
|
2134 { |
|
2135 // Update all, and register that values come from HCI |
|
2136 ref->Result().iJuiceFromHCIMask |= EBluetoothCoD; |
|
2137 ref->Result().iEntry.iCoD = aCoD; |
|
2138 } |
|
2139 |
|
2140 return ref; |
|
2141 } |
|
2142 |
|
2143 CBTInqResultRef* CBTInquiryMgr::AddEntryWithClockOffsetToCache(const TBTDevAddr& aAddr, const TBasebandTime aClockOffset) |
|
2144 { |
|
2145 LOG_FUNC |
|
2146 CBTInqResultRef* ref = AddEntryToCache(aAddr); |
|
2147 |
|
2148 if (ref) |
|
2149 { |
|
2150 //update the clock offset - useful for subsequent connections |
|
2151 // mark as valid |
|
2152 ref->Result().iJuiceFromHCIMask |= EBluetoothClockOffSet; |
|
2153 ref->Result().LogEntry().iClockOffset = static_cast<TUint16>(aClockOffset | KHCIClockOffsetValidBit); |
|
2154 } |
|
2155 |
|
2156 return ref; |
|
2157 } |
|
2158 |
|
2159 |
|
2160 TInt CBTInquiryMgr::InquiryWatchdog(TAny* aPtr) |
|
2161 /* |
|
2162 Called second while doing enquiry. |
|
2163 Considers whether to give up enquiry |
|
2164 */ |
|
2165 { |
|
2166 LOG_STATIC_FUNC |
|
2167 CBTInquiryMgr& self = *static_cast<CBTInquiryMgr*>(aPtr); |
|
2168 __ASSERT_DEBUG(self.iHWState == EInquiry, Panic(EHostResolverBadHWState)); |
|
2169 if(self.iResultCount == 0) |
|
2170 { |
|
2171 ++self.iInquirySilenceCount; |
|
2172 } |
|
2173 |
|
2174 if(self.iNewPageRequestsPending |
|
2175 && (self.iInquirySilenceCount * KInquiryWatchdogPeriod) > self.iInquiryInteruptions) |
|
2176 {// Silence, and names are waiting. Defer the inquiry |
|
2177 self.TryToInterruptInquiryForNameLookup(); |
|
2178 } |
|
2179 |
|
2180 self.iResultCount = 0; |
|
2181 return 0; |
|
2182 } |
|
2183 |
|
2184 TInt CBTInquiryMgr::Flush(TAny* aPtr) |
|
2185 { |
|
2186 LOG_STATIC_FUNC |
|
2187 CBTInquiryMgr& self = *static_cast<CBTInquiryMgr*>(aPtr); |
|
2188 __ASSERT_DEBUG(self.iHWState != EInquiry, Panic(EHostResolverBadHWState)); |
|
2189 self.DoFlush(); |
|
2190 return 0; |
|
2191 } |
|
2192 |
|
2193 void CBTInquiryMgr::DoFlush() |
|
2194 { |
|
2195 LOG_FUNC |
|
2196 for (TInt i = 0; i < iCacheAge.Count(); i++) |
|
2197 { |
|
2198 ++iCacheAge[i].iCacheAge; |
|
2199 } |
|
2200 iCurrentResults.ReturnToFirstResult(); |
|
2201 |
|
2202 while (CBTInqResultRef* ref = iCurrentResults.NextResult()) |
|
2203 { |
|
2204 CBTInqResultRecord& rec = ref->Result(); |
|
2205 TInt age = rec.IncFlushes(); |
|
2206 if(age >= KRecordStaleAge && rec.NumberOfIACsRespondedTo() > 0) |
|
2207 { |
|
2208 // device is around, but not seen for a while |
|
2209 rec.ClearIACs(); |
|
2210 LOG(_L("CBTInquiryMgr::DoFlush - ClearIACs")); |
|
2211 } |
|
2212 |
|
2213 if(age >= KRecordDeathAge) |
|
2214 { |
|
2215 rec.SetNameValid(EFalse); |
|
2216 LOG(_L("CBTInquiryMgr::DoFlush - age >= KRecordDeathAge setting name valid False")); |
|
2217 } |
|
2218 |
|
2219 if(age >= KRecordDeathAge && rec.NumberOfIACsRespondedTo() == 0 && !rec.IsNameValid() |
|
2220 && (!rec.IsNameRequestPending() || iHRs.IsEmpty())) //Don't flush old record if name pending and HRs present |
|
2221 {// Record is dead. Delete it |
|
2222 LOG(_L("CBTInquiryMgr::DoFlush - Deleting reference")); |
|
2223 delete ref; |
|
2224 } |
|
2225 else if(rec.IsNameRefreshRequested() && iHWState == EIdle) |
|
2226 {// Try to get this name now, perhaps? |
|
2227 LOG(_L("CBTInquiryMgr::DoFlush - Try to get name straight away")); |
|
2228 rec.SetNameRefreshRequested(EFalse); |
|
2229 rec.SetNamePending(ETrue); |
|
2230 rec.iNameLookupAttempts = 0; |
|
2231 ++iPendingNameRequests; |
|
2232 LOG(_L("CBTInquiryMgr::DoFlush asking for another name lookup")); |
|
2233 DoNameLookup(EFalse); |
|
2234 } |
|
2235 } |
|
2236 if (iCurrentResults.IsEmpty()) |
|
2237 { |
|
2238 iNewPageRequestsPending = EFalse; |
|
2239 iPendingNameRequests = 0; |
|
2240 iFlusher->Cancel(); |
|
2241 } |
|
2242 } |
|
2243 |
|
2244 void CBTInquiryMgr::EnableFlusher() |
|
2245 { |
|
2246 LOG_FUNC |
|
2247 if (iFlusher->IsActive() || iHWState == EInquiry) |
|
2248 return; |
|
2249 static const TInt usec2sec = 1000000; |
|
2250 TCallBack cb(Flush, this); |
|
2251 iFlusher->Start(usec2sec / 4, // 1/4 sec for initial flush |
|
2252 KFlushTimeoutSecs * usec2sec, |
|
2253 cb); |
|
2254 } |
|
2255 |
|
2256 TInt CBTInquiryMgr::CacheAge(TUint aIAC) const |
|
2257 { |
|
2258 TInt ret = KMaxTInt; // If we haven't set a cache age for this IAC, return KMaxTInt |
|
2259 for (TUint i = 0; i < iCacheAge.Count(); i++) |
|
2260 { |
|
2261 if (iCacheAge[i].iIAC == aIAC) |
|
2262 { |
|
2263 ret = iCacheAge[i].iCacheAge; |
|
2264 break; |
|
2265 } |
|
2266 } |
|
2267 return ret; |
|
2268 } |
|
2269 |
|
2270 void CBTInquiryMgr::SetCacheAge(TUint aIAC, TInt aAge) |
|
2271 { |
|
2272 TBool found = EFalse; |
|
2273 for (TUint i = 0; i < iCacheAge.Count(); i++) |
|
2274 { |
|
2275 if (iCacheAge[i].iIAC == aIAC) |
|
2276 { |
|
2277 iCacheAge[i].iCacheAge = aAge; |
|
2278 found = ETrue; |
|
2279 break; |
|
2280 } |
|
2281 } |
|
2282 if (!found) |
|
2283 { |
|
2284 TInquiryCacheAge ageInfo; |
|
2285 ageInfo.iIAC = aIAC; |
|
2286 ageInfo.iCacheAge = aAge; |
|
2287 iCacheAge.Append(ageInfo); |
|
2288 // If we can't append, there's not a lot we can do - we'll just have |
|
2289 // to return KMaxTInt when someone asks for the age |
|
2290 } |
|
2291 } |
|
2292 |
|
2293 |
|
2294 // ----------------------------------------------------------------------------------------- |
|
2295 |
|
2296 void CBTInquiryMgr::StartInquiryL(TUint aIAC, TUint8 aLength, TUint8 aNumResponses) |
|
2297 { |
|
2298 LOG_FUNC |
|
2299 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2300 CInquiryCommand* cmd = CInquiryCommand::NewL(aIAC, aLength, aNumResponses); |
|
2301 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2302 } |
|
2303 |
|
2304 void CBTInquiryMgr::CancelInquiryL() |
|
2305 { |
|
2306 LOG_FUNC |
|
2307 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2308 CInquiryCancelCommand* cmd = CInquiryCancelCommand::NewL(); |
|
2309 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2310 } |
|
2311 |
|
2312 void CBTInquiryMgr::CancelRemoteNameL(const TBTDevAddr& aAddr) |
|
2313 { |
|
2314 LOG_FUNC |
|
2315 CRemoteNameRequestCancelCommand* cmd = CRemoteNameRequestCancelCommand::NewL(aAddr); |
|
2316 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2317 } |
|
2318 |
|
2319 void CBTInquiryMgr::WriteInquiryModeL(TUint8 aInquiryMode) |
|
2320 { |
|
2321 LOG_FUNC |
|
2322 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2323 CWriteInquiryModeCommand* cmd = CWriteInquiryModeCommand::NewL(aInquiryMode); |
|
2324 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2325 } |
|
2326 |
|
2327 /** |
|
2328 This function should be used by the inquiry manager to retrieve remote names. |
|
2329 */ |
|
2330 void CBTInquiryMgr::LookupNameL(const TInquiryLogEntry& aEntry) |
|
2331 { |
|
2332 LOG_FUNC |
|
2333 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2334 CRemoteNameRequestCommand* cmd = CRemoteNameRequestCommand::NewL(aEntry.iBdaddr, aEntry.iPageScanRepetitionMode, aEntry.iPageScanMode, aEntry.iClockOffset); |
|
2335 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2336 } |
|
2337 |
|
2338 /** |
|
2339 This function should only be used when there is an established physical link (and therefore from |
|
2340 CPhysicalLink only) to retrieve a remote name. |
|
2341 */ |
|
2342 void CBTInquiryMgr::ReadRemoteNameL(const TBTDevAddr& aAddr) |
|
2343 { |
|
2344 LOG_FUNC |
|
2345 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2346 CRemoteNameRequestCommand* cmd = CRemoteNameRequestCommand::NewL(aAddr, 0, 0, 0); |
|
2347 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2348 } |
|
2349 |
|
2350 void CBTInquiryMgr::ReadLocalNameL() |
|
2351 { |
|
2352 LOG_FUNC |
|
2353 // Ownership of cmd transfered even if MhcqAddCommandL leaves |
|
2354 CReadLocalNameCommand* cmd = CReadLocalNameCommand::NewL(); |
|
2355 CommandQueue().MhcqAddCommandL(cmd, *this); |
|
2356 } |
|
2357 |
|
2358 void CBTInquiryMgr::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand) |
|
2359 { |
|
2360 LOG_FUNC |
|
2361 __ASSERT_DEBUG(aCommand, Panic(EBTInquiryMgrUnmatchedEvent)); |
|
2362 LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode()); |
|
2363 |
|
2364 if (aCommand->Opcode() == KInquiryOpcode) |
|
2365 { |
|
2366 // Clear any pending stuff |
|
2367 CompleteCommands(aErrorCode); |
|
2368 // this will also set the state to Idle |
|
2369 } |
|
2370 else if (aCommand->Opcode() == KWriteInquiryModeOpcode) |
|
2371 { |
|
2372 // command had error before response -- assume not supported |
|
2373 WriteInquiryModeComplete(EFalse); |
|
2374 } |
|
2375 else if (aCommand->Opcode() == KRemoteNameRequestOpcode) |
|
2376 { |
|
2377 // command had error before response -- doctor a fake response and process it to restore sanity |
|
2378 TBuf8<256> fakeEventBuffer; |
|
2379 const TBTDevAddr remNameReqAddr = static_cast<const CRemoteNameRequestCommand*>(aCommand)->BDADDR(); |
|
2380 TRemoteNameReqCompleteEvent fakeRemoteNameRequestCompleteEvent(EOK, remNameReqAddr, _L8(""), fakeEventBuffer); |
|
2381 RemoteNameReqCompleteEvent(fakeRemoteNameRequestCompleteEvent); |
|
2382 // we will not change the state so a running inquiry can complete |
|
2383 } |
|
2384 else if (aCommand->Opcode() == KReadLocalNameOpcode) |
|
2385 { |
|
2386 // command had error before response -- doctor a fake response and process it to restore sanity |
|
2387 TBuf8<256> fakeEventBuffer; |
|
2388 TReadLocalNameCompleteEvent fakeLocalNameCompleteEvent(EUnspecifiedError, 0, _L8(""), fakeEventBuffer); |
|
2389 ReadLocalNameOpcode(EUnspecifiedError, fakeLocalNameCompleteEvent); |
|
2390 // this will also set the state to Idle |
|
2391 } |
|
2392 |
|
2393 } |
|
2394 |
|
2395 // From MHCICommandQueueClient |
|
2396 void CBTInquiryMgr::MhcqcCommandEventReceived(const THCIEventBase& aEvent, |
|
2397 const CHCICommandBase* aRelatedCommand) |
|
2398 { |
|
2399 LOG_FUNC |
|
2400 __ASSERT_DEBUG(aRelatedCommand, Panic(EBTInquiryMgrUnmatchedEvent)); |
|
2401 static_cast<void>(aRelatedCommand); |
|
2402 switch(aEvent.EventCode()) |
|
2403 { |
|
2404 case ERemoteNameReqCompleteEvent: |
|
2405 RemoteNameReqCompleteEvent(aEvent); |
|
2406 break; |
|
2407 |
|
2408 case ERemoteHostSupportedFeaturesNotificationEvent: |
|
2409 RemoteHostSupportedFeaturesNotificationEvent(aEvent); |
|
2410 break; |
|
2411 |
|
2412 case EInquiryCompleteEvent: |
|
2413 InquiryCompleteEvent(aEvent); |
|
2414 break; |
|
2415 |
|
2416 case EInquiryResultEvent: |
|
2417 InquiryResultEvent(aEvent); |
|
2418 break; |
|
2419 |
|
2420 case EInquiryResultwithRSSIEvent: |
|
2421 InquiryResultWithRSSIEvent(aEvent); |
|
2422 break; |
|
2423 |
|
2424 case EExtendedInquiryResultEvent: |
|
2425 ExtendedInquiryResultEvent(aEvent); |
|
2426 break; |
|
2427 |
|
2428 case ECommandCompleteEvent: |
|
2429 CommandCompleteEvent(aEvent); |
|
2430 break; |
|
2431 |
|
2432 case ECommandStatusEvent: |
|
2433 CommandStatusEvent(aEvent, *aRelatedCommand); |
|
2434 break; |
|
2435 |
|
2436 default: |
|
2437 LOG1(_L("Warning!! Unknown Command Event Received (event code:%d)"), aEvent.EventCode()); |
|
2438 __ASSERT_DEBUG(EFalse, Panic(EHCIUnknownCommandEvent)); |
|
2439 break; |
|
2440 } |
|
2441 } |
|
2442 |
|
2443 void CBTInquiryMgr::CommandCompleteEvent(const THCIEventBase& aEvent) |
|
2444 { |
|
2445 LOG_FUNC |
|
2446 const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent); |
|
2447 THCIOpcode opcode = completeEvent.CommandOpcode(); |
|
2448 THCIErrorCode hciErr = aEvent.ErrorCode(); |
|
2449 |
|
2450 switch (opcode) |
|
2451 { |
|
2452 case KWriteInquiryModeOpcode: |
|
2453 WriteInquiryModeOpcode(hciErr, aEvent); |
|
2454 break; |
|
2455 |
|
2456 case KReadLocalNameOpcode: |
|
2457 ReadLocalNameOpcode(hciErr, aEvent); |
|
2458 break; |
|
2459 |
|
2460 case KInquiryCancelOpcode: |
|
2461 InquiryCancelOpcode(hciErr, aEvent); |
|
2462 break; |
|
2463 |
|
2464 // These commands are expected to not be handled and so can be safely ignored. |
|
2465 case KRemoteNameRequestCancelOpcode: |
|
2466 LOG1(_L("ignored Command Complete event (opcode: 0x%04x)"), opcode); |
|
2467 break; |
|
2468 |
|
2469 // The commands below would most likely be used by the inquiry manager, however |
|
2470 // they currently are not used. |
|
2471 case KPeriodicInquiryModeOpcode: |
|
2472 case KExitPeriodicInquiryModeOpcode: |
|
2473 case KReadInquiryScanActivityOpcode: |
|
2474 case KWriteInquiryScanActivityOpcode: |
|
2475 case KReadInquiryScanTypeOpcode: |
|
2476 case KWriteInquiryScanTypeOpcode: |
|
2477 case KReadInquiryModeOpcode: |
|
2478 LOG1(_L("Warning!! Unhandled Command Complete Event (opcode:%d)"), opcode); |
|
2479 break; |
|
2480 |
|
2481 default: |
|
2482 LOG2(_L("Error!! Unknown Command complete event! Opcode %d error code %d"), opcode, hciErr); |
|
2483 __ASSERT_DEBUG(EFalse, Panic(EHCIUnknownCommandCompleteOpcode)); |
|
2484 break; |
|
2485 } |
|
2486 } |
|
2487 |
|
2488 void CBTInquiryMgr::CommandStatusEvent(const THCIEventBase& aEvent, const CHCICommandBase& aCommand) |
|
2489 { |
|
2490 LOG_FUNC |
|
2491 |
|
2492 const TCommandStatusEvent& commandStatusEvent = TCommandStatusEvent::Cast(aEvent); |
|
2493 THCIOpcode opcode = commandStatusEvent.CommandOpcode(); |
|
2494 THCIErrorCode hciErr = commandStatusEvent.ErrorCode(); |
|
2495 |
|
2496 __ASSERT_DEBUG(aCommand.Opcode() == opcode, Panic(EBTInquiryMgrMismatchedStatusEvent)); |
|
2497 |
|
2498 if (hciErr != EOK) |
|
2499 { |
|
2500 // got an error |
|
2501 // map onto the event that would have occurred: some things we will have to let the client work out |
|
2502 // e.g. they should check error and connection handle etc. |
|
2503 switch (opcode) |
|
2504 { |
|
2505 case KInquiryOpcode: |
|
2506 InquiryComplete(CHciUtil::SymbianErrorCode(hciErr), 0); |
|
2507 break; |
|
2508 |
|
2509 case KRemoteNameRequestOpcode: |
|
2510 { |
|
2511 const CRemoteNameRequestCommand& remNameReq = static_cast<const CRemoteNameRequestCommand&>(aCommand); |
|
2512 TInt err = CHciUtil::SymbianErrorCode(hciErr); |
|
2513 TBTDevAddr addr = remNameReq.BDADDR(); |
|
2514 TBTDeviceName8 nullDevName(KNullDesC8); |
|
2515 |
|
2516 RemoteNameResult(err, addr, nullDevName); |
|
2517 iLinkMgrProtocol.PhysicalLinksMgr().RemoteName(hciErr, addr, nullDevName); |
|
2518 } |
|
2519 break; |
|
2520 |
|
2521 default: |
|
2522 // Complete any other commands with an error |
|
2523 CommandCompleteEvent(aEvent); |
|
2524 break; |
|
2525 } |
|
2526 } |
|
2527 } |
|
2528 |
|
2529 void CBTInquiryMgr::WriteInquiryModeOpcode(THCIErrorCode aHciErr, const THCIEventBase& /*aEvent*/) |
|
2530 { |
|
2531 LOG_FUNC |
|
2532 WriteInquiryModeComplete(aHciErr == EOK); |
|
2533 } |
|
2534 |
|
2535 void CBTInquiryMgr::ReadLocalNameOpcode(THCIErrorCode aHciErr, const THCIEventBase& aEvent) |
|
2536 { |
|
2537 LOG_FUNC |
|
2538 if (aHciErr == EOK) |
|
2539 { |
|
2540 const TReadLocalNameCompleteEvent& readLocalNameCompleteEvent = TReadLocalNameCompleteEvent::Cast(aEvent); |
|
2541 TPtrC8 localName = readLocalNameCompleteEvent.LocalName(); |
|
2542 |
|
2543 GetLocalNameComplete(CHciUtil::SymbianErrorCode(aHciErr), localName); |
|
2544 iLinkMgrProtocol.UpdateLocalDeviceName(localName); |
|
2545 } |
|
2546 else |
|
2547 { |
|
2548 _LIT8(KNoName, ""); |
|
2549 GetLocalNameComplete(CHciUtil::SymbianErrorCode(aHciErr), KNoName()); |
|
2550 } |
|
2551 } |
|
2552 |
|
2553 void CBTInquiryMgr::InquiryCancelOpcode(THCIErrorCode aHciErr, const THCIEventBase& /* aEvent */) |
|
2554 { |
|
2555 LOG_FUNC |
|
2556 if (aHciErr == EOK) |
|
2557 { |
|
2558 if (iHWState == ECancellingForNewIAC) |
|
2559 { |
|
2560 SetHWState(EIdle); |
|
2561 DoInquiry(); |
|
2562 } |
|
2563 } |
|
2564 // If an error comes in, it may be because the inquiry complete came in first. In that case, everything |
|
2565 // will be handled by the Inquiry complete logic |
|
2566 } |
|
2567 |
|
2568 // ---------------------------------------------------------------------------- |
|
2569 // Event processing functions |
|
2570 // ---------------------------------------------------------------------------- |
|
2571 |
|
2572 void CBTInquiryMgr::InquiryCompleteEvent(const THCIEventBase& aEvent) |
|
2573 { |
|
2574 LOG_FUNC |
|
2575 InquiryComplete(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), 0); |
|
2576 } |
|
2577 |
|
2578 void CBTInquiryMgr::InquiryResultEvent(const THCIEventBase& aEvent) |
|
2579 { |
|
2580 LOG_FUNC |
|
2581 |
|
2582 const TInquiryResultEvent& inquiryResultEvent = TInquiryResultEvent::Cast(aEvent); |
|
2583 TInquiryLogEntry entry; |
|
2584 TUint responses = inquiryResultEvent.NumResponses(); |
|
2585 |
|
2586 for (TUint index = 0; index < responses; index++) |
|
2587 { |
|
2588 entry.iSpare = KInqLogEntryStandard; |
|
2589 entry.iPageScanRepetitionMode = inquiryResultEvent.PageScanRepetitionMode(index); |
|
2590 entry.iPageScanPeriodMode = inquiryResultEvent.Reserved1(index); // v1.1 spec, no meaning in v1.2 |
|
2591 entry.iPageScanMode = inquiryResultEvent.Reserved2(index); // v1.1 spec, no meaning in v1.2 |
|
2592 entry.iClockOffset = inquiryResultEvent.ClockOffset(index); |
|
2593 entry.iBdaddr = inquiryResultEvent.BDADDR(index); |
|
2594 entry.iCoD = inquiryResultEvent.ClassOfDevice(index); |
|
2595 InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry); |
|
2596 } |
|
2597 } |
|
2598 |
|
2599 void CBTInquiryMgr::InquiryResultWithRSSIEvent(const THCIEventBase& aEvent) |
|
2600 { |
|
2601 LOG_FUNC |
|
2602 |
|
2603 const TInquiryResultwithRSSIEvent& inquiryResultwithRSSIEvent = TInquiryResultwithRSSIEvent::Cast(aEvent); |
|
2604 TInquiryLogEntryWithRssi entry; |
|
2605 TUint responses = inquiryResultwithRSSIEvent.NumResponses(); |
|
2606 |
|
2607 for (TUint index = 0; index < responses; index++) |
|
2608 { |
|
2609 entry.iSpare = KInqLogEntryWithRssi; |
|
2610 entry.iPageScanRepetitionMode = inquiryResultwithRSSIEvent.PageScanRepetitionMode(index); |
|
2611 entry.iPageScanPeriodMode = 0x00; |
|
2612 entry.iPageScanMode = 0x00; |
|
2613 entry.iClockOffset = inquiryResultwithRSSIEvent.ClockOffset(index); |
|
2614 entry.iBdaddr = inquiryResultwithRSSIEvent.BDADDR(index); |
|
2615 entry.iCoD = inquiryResultwithRSSIEvent.ClassOfDevice(index); |
|
2616 entry.iRssi = inquiryResultwithRSSIEvent.RSSI(index); |
|
2617 InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry); |
|
2618 } |
|
2619 } |
|
2620 |
|
2621 void CBTInquiryMgr::ExtendedInquiryResultEvent(const THCIEventBase& aEvent) |
|
2622 { |
|
2623 LOG_FUNC |
|
2624 |
|
2625 const TExtendedInquiryResultEvent& extendedInquiryResultEvent = TExtendedInquiryResultEvent::Cast(aEvent); |
|
2626 TInquiryLogEntryWithEir entry; |
|
2627 |
|
2628 entry.iSpare = KInqLogEntryWithEir; |
|
2629 entry.iPageScanRepetitionMode = extendedInquiryResultEvent.PageScanRepetitionMode(); |
|
2630 entry.iPageScanPeriodMode = 0x00; |
|
2631 entry.iPageScanMode = 0x00; |
|
2632 entry.iClockOffset = extendedInquiryResultEvent.ClockOffset(); |
|
2633 entry.iBdaddr = extendedInquiryResultEvent.BDADDR(); |
|
2634 entry.iCoD = extendedInquiryResultEvent.ClassOfDevice(); |
|
2635 entry.iRssi = extendedInquiryResultEvent.RSSI(); |
|
2636 entry.iExtendedInquiryResponse = extendedInquiryResultEvent.ExtendedInquiryResponse(); |
|
2637 InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry); |
|
2638 } |
|
2639 |
|
2640 void CBTInquiryMgr::RemoteNameReqCompleteEvent(const THCIEventBase& aEvent) |
|
2641 { |
|
2642 LOG_FUNC |
|
2643 const TRemoteNameReqCompleteEvent& remNameReqCompleteEvent = TRemoteNameReqCompleteEvent::Cast(aEvent); |
|
2644 THCIErrorCode err = aEvent.ErrorCode(); |
|
2645 const TBTDevAddr addr = remNameReqCompleteEvent.BDADDR(); |
|
2646 TPtrC8 buf = remNameReqCompleteEvent.RemoteName(); |
|
2647 |
|
2648 //shorten the name to the proper length so that we don't waste memory |
|
2649 TInt nullTerminator = buf.Locate(0); |
|
2650 if (nullTerminator == KErrNotFound) |
|
2651 { |
|
2652 nullTerminator = buf.Length(); |
|
2653 } |
|
2654 TPtrC8 remoteName = buf.Left(nullTerminator); |
|
2655 |
|
2656 // name requests have two customers... |
|
2657 // inquiry manager |
|
2658 RemoteNameResult(CHciUtil::SymbianErrorCode(err), addr, remoteName); |
|
2659 // phy manager |
|
2660 iLinkMgrProtocol.PhysicalLinksMgr().RemoteName(err, addr, remoteName); |
|
2661 } |
|
2662 |
|
2663 void CBTInquiryMgr::RemoteHostSupportedFeaturesNotificationEvent(const THCIEventBase& aEvent) |
|
2664 { |
|
2665 LOG_FUNC |
|
2666 const TRemoteHostSupportedFeaturesNotificationEvent& remHostSupFeatEvent = TRemoteHostSupportedFeaturesNotificationEvent::Cast(aEvent); |
|
2667 THCIErrorCode err = remHostSupFeatEvent.ErrorCode(); |
|
2668 TBTDevAddr addr = remHostSupFeatEvent.BDADDR(); |
|
2669 TUint64 hostSupFeats = remHostSupFeatEvent.HostSupportedFeatures(); |
|
2670 |
|
2671 RemoteHostSupportedFeatures(CHciUtil::SymbianErrorCode(err), addr, hostSupFeats); |
|
2672 } |
|
2673 |
|
2674 |
|
2675 #ifdef CONNECTION_PREEMPTS_INQUIRY |
|
2676 // ******************************************************************* |
|
2677 // ACL Connecting status subscriber |
|
2678 // ******************************************************************* |
|
2679 /*static*/ CConnectingStatusSubscriber* CConnectingStatusSubscriber::NewL(CBTInquiryMgr& aInquiryMgr) |
|
2680 { |
|
2681 LOG_STATIC_FUNC |
|
2682 CConnectingStatusSubscriber* self = new(ELeave) CConnectingStatusSubscriber(aInquiryMgr); |
|
2683 CleanupStack::PushL(self); |
|
2684 self->ConstructL(); |
|
2685 CleanupStack::Pop(); |
|
2686 return self; |
|
2687 } |
|
2688 |
|
2689 CConnectingStatusSubscriber::CConnectingStatusSubscriber(CBTInquiryMgr& aInquiryMgr) |
|
2690 : CActive(CActive::EPriorityStandard), iParent(aInquiryMgr) |
|
2691 { |
|
2692 LOG_FUNC |
|
2693 CActiveScheduler::Add(this); |
|
2694 } |
|
2695 |
|
2696 CConnectingStatusSubscriber::~CConnectingStatusSubscriber() |
|
2697 { |
|
2698 LOG_FUNC |
|
2699 Cancel(); |
|
2700 iProperty.Close(); |
|
2701 } |
|
2702 |
|
2703 void CConnectingStatusSubscriber::DoCancel() |
|
2704 { |
|
2705 LOG_FUNC |
|
2706 iProperty.Cancel(); |
|
2707 } |
|
2708 |
|
2709 void CConnectingStatusSubscriber::ConstructL() |
|
2710 { |
|
2711 LOG_FUNC |
|
2712 User::LeaveIfError(iProperty.Attach(KPropertyUidBluetoothCategory, |
|
2713 KPropertyKeyBluetoothGetConnectingStatus)); |
|
2714 Subscribe(); |
|
2715 } |
|
2716 |
|
2717 void CConnectingStatusSubscriber::Subscribe() |
|
2718 { |
|
2719 LOG_FUNC |
|
2720 iProperty.Subscribe(iStatus); |
|
2721 SetActive(); |
|
2722 } |
|
2723 |
|
2724 /** |
|
2725 This is looking for the connection to complete. The inquiry must be |
|
2726 synchronously cancelled before the CreateConnection command is issued, |
|
2727 but we can resume it when we get round to it. |
|
2728 */ |
|
2729 void CConnectingStatusSubscriber::RunL() |
|
2730 { |
|
2731 LOG_FUNC |
|
2732 // Subscribe to the next change of state. |
|
2733 Subscribe(); |
|
2734 |
|
2735 TInt isConnecting; |
|
2736 // If this device is currently connecting an ACL suspend the |
|
2737 // inquiry. |
|
2738 iProperty.Get(isConnecting); |
|
2739 |
|
2740 if(!isConnecting) |
|
2741 { |
|
2742 iParent.Resume(); |
|
2743 } |
|
2744 } |
|
2745 #endif // CONNECTION_PREEMPTS_INQUIRY |