--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/hostresolver.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,2745 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// L2CAP Host resolver.
+// Implements Inquiry and Name lookup
+//
+//
+
+#include "hostresolver.h"
+#include <bluetooth/logger.h>
+#include <bluetooth/hci/event.h>
+#include <bluetooth/hci/hciutil.h>
+#include <bluetooth/hci/hciconsts.h>
+#include <bluetooth/hci/inquirycommand.h>
+#include <bluetooth/hci/inquirycancelcommand.h>
+#include <bluetooth/hci/writeinquirymodecommand.h>
+#include <bluetooth/hci/writeextendedinquiryresponsecommand.h>
+#include <bluetooth/hci/readextendedinquiryresponsecommand.h>
+#include <bluetooth/hci/remotenamerequestcommand.h>
+#include <bluetooth/hci/remotenamereqcompleteevent.h>
+#include <bluetooth/hci/remotenamerequestcancelcommand.h>
+#include <bluetooth/hci/remotehostsupportedfeaturesnotificationevent.h>
+#include <bluetooth/hci/readlocalnamecommand.h>
+
+// Command Events
+#include <bluetooth/hci/inquiryresultevent.h>
+#include <bluetooth/hci/inquiryresultwithrssievent.h>
+#include <bluetooth/hci/extendedinquiryresultevent.h>
+
+// Command Complete Events
+#include <bluetooth/hci/readlocalnamecompleteevent.h>
+#include <bluetooth/hci/commandcompleteevent.h>
+#include <bluetooth/hci/commandstatusevent.h>
+
+#include <e32std.h>
+#include <s32mem.h>
+#include <bt_sock.h>
+#include <utf.h>
+#include "debug.h"
+#include "linkutil.h"
+#include "physicallinksmanager.h" // to get baseband freed up for inquiries
+#include "BTSec.h"
+
+//Diagnostic string for security check failures, in builds without platsec
+//diagnostics this will be NULL.
+const char* const KBT_HOSTRESOLVER_NAME_DIAG = __PLATSEC_DIAGNOSTIC_STRING("Bluetooth Host Resolver");
+
+const static TUint KHostResDontBlock = 4;
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_HOSTRESOLVER);
+#endif
+
+
+
+//
+// BT Inquiry Result record.
+//
+
+CBTInqResultRecord::CBTInqResultRecord(const TBTDevAddr& aAddr)
+ : iCodec(iEntry.iExtendedInquiryResponse)
+ {
+ LOG_FUNC
+ iEntry.iBdaddr = aAddr;
+ // Default to page scan repetition mode R2 as recommended by Bray to give best
+ // chance of success for host resolver needs.
+ // Also allow other default values to be entered here
+ iEntry.iPageScanRepetitionMode = KDefaultBluetoothPageScanRepMode;
+ iEntry.iPageScanMode = KDefaultBluetoothPageScanMode;
+ iEntry.iClockOffset = KDefaultBluetoothClockOffset;
+ iEntry.iCoD = KDefaultBluetoothClassOfDevice;
+
+ if(aAddr!=TBTDevAddr())
+ {
+ iJuiceFromHCIMask = EBluetoothAddr;
+ }
+ //else 0 cos 'C' class
+
+ SetNameValid(EFalse);
+ }
+
+CBTInqResultRecord::~CBTInqResultRecord()
+ {
+ LOG_FUNC
+ iIACs.Close();
+ }
+
+inline void CBTInqResultRecord::Open()
+/**
+ Some reference is now pointing at us.
+ Inc the ref count.
+**/
+ {
+ LOG_FUNC
+ ++iRefCount;
+ }
+
+void CBTInqResultRecord::Close()
+/**
+ Reference closing.
+ Auto delete if this was the last reference pointing to us.
+**/
+ {
+ LOG_FUNC
+ --iRefCount;
+ if (iRefCount <= 0)
+ delete this;
+ }
+
+TInquiryLogEntry& CBTInqResultRecord::LogEntry()
+/**
+ The inquiry result.
+ This is stored as it was returned from the HCI.
+ The host resolver will convert it into an InquirySockAddr
+ when a client actually wants to retreve it.
+**/
+ {
+ LOG_FUNC
+ return iEntry;
+ }
+
+inline void CBTInqResultRecord::SetName(const TDesC8& aName)
+ {
+ LOG_FUNC
+ iName = aName;
+ }
+
+inline const TDesC8& CBTInqResultRecord::Name() const
+/**
+ The UTF name of the remote device.
+ This is stored as it comes in off the wire, the host resolver
+ converts to Unicode if & when a client actually requires it.
+**/
+ {
+ LOG_FUNC
+ return iName;
+ }
+
+inline TInt CBTInqResultRecord::IncFlushes()
+/**
+ Age the result record
+ Returns it's new age.
+**/
+ {
+ LOG_FUNC
+ return ++iFlushes;
+ }
+
+inline TBool CBTInqResultRecord::IsNameValid() const
+ {
+ LOG_FUNC
+ return iNameLookupResultCode == KErrNone;
+ }
+
+inline void CBTInqResultRecord::SetNameValid(TBool aBool)
+ {
+ LOG_FUNC
+ if(aBool)
+ iNameLookupResultCode = KErrNone;
+ else
+ iNameLookupResultCode = 1;
+ }
+
+void CBTInqResultRecord::SetNameLookupResultCode(TInt aResultCode)
+ {// Only record error, if we don't have a valid name already
+ LOG_FUNC
+ if(!IsNameValid())
+ iNameLookupResultCode = aResultCode;
+ }
+
+inline TBool CBTInqResultRecord::IsNameRequestPending() const
+ {
+ LOG_FUNC
+ return iNameStatus & ENamePending;
+ }
+
+inline void CBTInqResultRecord::SetNamePending(TBool aBool)
+ {
+ LOG_FUNC
+ if(aBool)
+ iNameStatus |= ENamePending;
+ else
+ iNameStatus &= ~ENamePending;
+ }
+
+inline TBool CBTInqResultRecord::IsNameRefreshRequested() const
+ {
+ LOG_FUNC
+ return iNameStatus & ENameRefreshRequested;
+ }
+
+inline void CBTInqResultRecord::SetNameRefreshRequested(TBool aBool)
+ {
+ LOG_FUNC
+ if(aBool)
+ iNameStatus |= ENameRefreshRequested;
+ else
+ iNameStatus &= ~ENameRefreshRequested;
+ }
+
+inline TBool CBTInqResultRecord::IsNameComplete() const
+ {
+ LOG_FUNC
+ return iNameStatus & ENameComplete;
+ }
+
+inline void CBTInqResultRecord::SetNameComplete(TBool aBool)
+ {
+ LOG_FUNC
+ if(aBool)
+ iNameStatus |= ENameComplete;
+ else
+ iNameStatus &= ~ENameComplete;
+ }
+
+inline TBool CBTInqResultRecord::IsExplicitNameRequest() const
+ {
+ LOG_FUNC
+ return iNameStatus & ENameExplicitRequest;
+ }
+
+inline void CBTInqResultRecord::SetExplicitNameRequest(TBool aBool)
+ {
+ LOG_FUNC
+ if(aBool)
+ iNameStatus |= ENameExplicitRequest;
+ else
+ iNameStatus &= ~ENameExplicitRequest;
+ }
+
+inline TBool CBTInqResultRecord::HaveNameLookupResult() const
+ {
+ LOG_FUNC
+ return iNameLookupResultCode <= KErrNone;
+ }
+
+inline TInt CBTInqResultRecord::NameLookupResultCode() const
+ {// Only valid if HaveNameLookupResult == ETrue
+ LOG_FUNC
+ __ASSERT_DEBUG(HaveNameLookupResult(), Panic(EBTNameLookupResultNotFound));
+ return iNameLookupResultCode;
+ }
+
+inline TInt CBTInqResultRecord::NameLookupAttempts() const
+ {
+ LOG_FUNC
+ return iNameLookupAttempts;
+ }
+
+void CBTInqResultRecord::GetInquirySockAddr(TInquirySockAddr& aAddr)
+/**
+ Read this result record out into a TInquirySockAddr.
+**/
+ {
+ LOG_FUNC
+ aAddr.SetBTAddr(LogEntry().iBdaddr);
+
+ TBTDeviceClass cod(LogEntry().iCoD);
+ aAddr.SetMajorServiceClass(cod.MajorServiceClass());
+ aAddr.SetMajorClassOfDevice(cod.MajorDeviceClass());
+ aAddr.SetMinorClassOfDevice(cod.MinorDeviceClass());
+
+ TUint8 resultFlags = 0;
+
+ if(iJuiceFromHCIMask & EBluetoothRssi)
+ {
+ TInquiryLogEntry* logEntry = &(LogEntry());
+ resultFlags |= TInquirySockAddr::ERssiValid;
+ aAddr.SetRssi(static_cast<TInquiryLogEntryWithRssi*>(logEntry)->iRssi.RSSI());
+ }
+
+ aAddr.SetResultFlags(resultFlags);
+ }
+
+TInt CBTInqResultRecord::GetEir(TNameRecord& aNameRec, TBool aIgnoreCachedName)
+ {
+ // Reformat the EIR (i.e. remove 0s from EIR) for the constructor
+ LOG_FUNC
+ TInt error = KErrNone;
+
+ aNameRec.iName.Zero();
+ iCodec.Set(aNameRec);
+ if(IsEirPresent())
+ {
+ TInquiryLogEntry* logEntry = &(LogEntry());
+ iCodec.Copy(static_cast<TInquiryLogEntryWithEir*>(logEntry)->iExtendedInquiryResponse);
+ }
+ // replace device name in eir with iName if there is a complete name present
+ if(IsNameValid() && IsNameComplete() && !aIgnoreCachedName)
+ {
+ iCodec.SetDeviceName(iName, ETrue);
+ }
+ return error;
+ }
+
+TInt CBTInqResultRecord::GetName(TNameRecord& aNameRec)
+ {
+ LOG_FUNC
+ TInt err = KErrNotFound;
+ if(HaveNameLookupResult())
+ {
+ err = CnvUtfConverter::ConvertToUnicodeFromUtf8(aNameRec.iName, iName);
+ if (err >= KErrNone)
+ {
+ err = KErrNone;
+ if(!IsNameComplete())
+ {
+ aNameRec.iFlags |= TNameRecord::EPartial;
+ }
+ else
+ {
+ aNameRec.iFlags &= ~(TNameRecord::EPartial);
+ }
+ }
+ }
+ else
+ {
+ err = KErrNone; // just following the old way of working.
+ aNameRec.iName.Zero();
+ }
+ return err;
+ }
+
+TInt CBTInqResultRecord::AddIAC(TUint aIAC)
+/**
+ Adds IAC into list of those this device has responded to
+ @return KErrNone IAC added without any bother
+ @return KErrAlreadyExists IAC already in list
+**/
+ {
+ LOG_FUNC
+ return iIACs.InsertInOrder(aIAC);
+ }
+
+inline TBool CBTInqResultRecord::HasRespondedToIAC(TUint aIAC)
+ {
+ LOG_FUNC
+ return (iIACs.FindInOrder(aIAC) >= 0);
+ }
+
+inline TInt CBTInqResultRecord::NumberOfIACsRespondedTo()
+ {
+ LOG_FUNC
+ return iIACs.Count();
+ }
+
+inline void CBTInqResultRecord::ClearIACs()
+ {
+ LOG_FUNC
+ iIACs.Reset();
+ }
+
+//
+// Inquiry result reference.
+//
+CBTInqResultRef::CBTInqResultRef(CBTInqResultRef& aRef)
+ : iRecord(aRef.iRecord)
+ {
+ LOG_FUNC
+ iRecord.Open();
+ }
+
+CBTInqResultRef::~CBTInqResultRef()
+ {
+ LOG_FUNC
+ iRecord.Close();
+ iLink.Deque();
+ }
+
+CBTInqResultRef::CBTInqResultRef(CBTInqResultRecord& aRec)
+ : iRecord(aRec)
+ {
+ LOG_FUNC
+ iRecord.Open();
+ }
+
+inline CBTInqResultRecord& CBTInqResultRef::Result() const
+ {
+ LOG_FUNC
+ return iRecord;
+ }
+
+
+
+//
+// BT Inquiry Result Set.
+//
+
+CBTInqResultSet::CBTInqResultSet()
+ : iResultRefs(_FOFF(CBTInqResultRef, iLink)),
+ iNextRefIter(iResultRefs)
+ {
+ LOG_FUNC
+ }
+
+CBTInqResultSet::~CBTInqResultSet()
+ {
+ LOG_FUNC
+ Reset();
+ }
+
+void CBTInqResultSet::Reset()
+ {
+ LOG_FUNC
+ while (!iResultRefs.IsEmpty())
+ delete iResultRefs.First();
+ ReturnToFirstResult();
+ }
+
+CBTInqResultRef* CBTInqResultSet::Add(CBTInqResultRecord& aRec)
+ {
+ LOG_FUNC
+ CBTInqResultRef* ref = new CBTInqResultRef(aRec);
+ if (!ref)
+ {
+ return 0;
+ }
+ iResultRefs.AddLast(*ref);
+ if (!iNextRefIter)
+ {
+ iNextRefIter.SetToLast();
+ }
+ return ref;
+ }
+
+CBTInqResultRef* CBTInqResultSet::FindEntry(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ TResultQueIter iter(iResultRefs);
+ while (iter)
+ {
+ CBTInqResultRef* ref = iter++;
+ if (ref->Result().LogEntry().iBdaddr == aAddr)
+ {
+ return ref;
+ }
+ }
+ return 0;
+ }
+
+CBTInqResultRef* CBTInqResultSet::NextResult()
+ {
+ LOG_FUNC
+ return iCurrentResult = (iNextRefIter++);
+ }
+
+inline CBTInqResultRef* CBTInqResultSet::CurrentResult()
+ {
+ LOG_FUNC
+ return iCurrentResult;
+ }
+
+void CBTInqResultSet::ReturnToFirstResult()
+ {
+ LOG_FUNC
+ iNextRefIter.SetToFirst();
+ iCurrentResult = 0;
+ }
+
+inline TBool CBTInqResultSet::IsEmpty()
+ {
+ LOG_FUNC
+ return iResultRefs.IsEmpty();
+ }
+
+void CBTInqResultSet::MoveToback(CBTInqResultRef& aRef)
+ {
+ LOG_FUNC
+ if(iNextRefIter == &aRef)
+ NextResult();
+ aRef.iLink.Deque();
+ iResultRefs.AddLast(aRef);
+ if (!iNextRefIter)
+ {
+ iNextRefIter.SetToLast();
+ }
+ }
+
+//
+// BT Host resolver.
+//
+
+
+CBTHostResolver::CBTHostResolver(CBTInquiryMgr& aInquiryMgr)
+ : iInquiryMgr(aInquiryMgr),
+ iRequestState(EIdle),
+ iInquiryStatus(EInquiryReady)
+ {
+ LOG_FUNC
+#ifdef _DEBUG
+ iInquiryMgr.IncrementHRCount();
+#endif
+ }
+
+CBTHostResolver::~CBTHostResolver()
+ {
+ LOG_FUNC
+ iLink.Deque();
+ iInquiryMgr.DeletingHostResolver();
+#ifdef _DEBUG
+ iInquiryMgr.DecrementHRCount();
+#endif
+ }
+
+void CBTHostResolver::GetByName(TNameRecord& /*aName*/)
+ {
+ LOG_FUNC
+ iNotify->QueryComplete(KErrNotSupported);
+ }
+
+void CBTHostResolver::GetByAddress(TNameRecord& aName)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests));
+ iNameRecord = &aName;
+ iNameRecord->iName.Zero();
+ TInquirySockAddr& sa = TInquirySockAddr::Cast(aName.iAddr);
+
+ LOG1(_L("Host Resolver\tAction = %d"),sa.Action());
+
+ if(sa.Action() & KHostResCache)
+ //Complete immediately with info if available!
+ {
+ TInt err = KErrNotFound;
+ CBTInqResultRef* ref = iInquiryMgr.FindExistingCacheEntry(sa.BTAddr());
+ if (ref)
+ {// Got a result to send up
+ CBTInqResultRecord& rec = ref->Result();
+ rec.GetInquirySockAddr(sa); //Put BT address, CoD etc into 'aName'
+ // Check whether client wants EIRs instead of names
+ if(sa.Action() & KHostResEir)
+ {
+ // Client knows about EIR, we'll fill the TNameRecord with EIR
+ err = rec.GetEir(aName, EFalse);
+ }
+ else
+ {
+ // Client don't knows about EIR, we'll fill the TNameRecord with just encoded device name if present
+ err = rec.GetName(aName);
+ }
+ if (err != KErrNone)
+ {// error getting name - but other stuff fine -
+ // so ensure name is not gobbledigook
+ aName.iName.Zero();
+ }
+ err = KErrNone; //err has new meaning now, vis "record exists"
+ }
+ else
+ {
+ //err is KErrNotFound
+ }
+
+ iNotify->QueryComplete(err);
+ return;
+ }
+
+ // Must request at least one of inquiry or name lookup
+ if (!(sa.Action() & (KHostResInquiry | KHostResName)))
+ {
+ iRequestState = EError;
+ CompleteRequest(KErrArgument);
+ return;
+ }
+
+ // Check the hw state - can't do anything if its switched off
+ //
+ if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff)
+ {
+ iRequestState = EError;
+ CompleteRequest(KErrHardwareNotAvailable);
+ return;
+ }
+
+ iNameLookupMode = (sa.Action()) & KHostResName ? EDoGetNames : EDontGetNames;
+ TBool ignoreCache = (sa.Action() & KHostResIgnoreCache) ? ETrue : EFalse;
+
+ if (sa.Action() & KHostResInquiry)
+ {
+ // We only support GIAC and LIAC
+ if (sa.IAC() != KLIAC && sa.IAC() != KGIAC)
+ {
+ iRequestState = EError;
+ CompleteRequest(KErrNotSupported);
+ return;
+ }
+ sa.SetBTAddr(TBTDevAddr());
+ iRequestState = EInquiry;
+ if (iInquiryStatus == EInquiryReady)
+ {// Need to start the inquiry process
+ iInquiryStatus = EInquiring;
+ // We set this before calling StartInquiry, as StartInquiry can call InquiryComplete synchronously.
+ iResults.Reset();
+ iInquiryMgr.StartInquiry(*this, sa.IAC(), ignoreCache);
+ }
+ }
+ else // Not an inquiry - just a name lookup
+ {
+ // Just name lookup -- add it to the result set, and request a lookup of it
+ iResults.Reset();
+
+ CBTInqResultRef* ref = iInquiryMgr.AddEntryToCache(sa.BTAddr());
+ if(ref)
+ {// This ref is in cache -- need to make our own ref
+ ref = iResults.Add(ref->Result());
+ }
+ if (!ref)
+ {
+ CompleteRequest(KErrNoMemory);
+ return;
+ }
+ iResults.NextResult(); // Move iter onto first (only) result
+ iRequestState = ENameLookup;
+ iInquiryMgr.LookupName(*this, sa.BTAddr(), ignoreCache, ETrue);
+ }
+ TryToCompleteRequest();
+ }
+
+TInt CBTHostResolver::SetOption(TUint /*aLevel*/, TUint /*aName*/, const TDesC8& /*aOption*/)
+ {
+ LOG_FUNC
+ return KErrNone;
+ }
+
+void CBTHostResolver::SetHostName(TDes& aNameBuf)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests));
+
+ TBuf8<KHCILocalDeviceNameMaxLength> utf8Name;
+ TInt ret = CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Name, aNameBuf);
+ iRequestState=ESetLocalName;
+ if (ret > KErrNone)
+ {// Unconverted characters exist! Error the client
+ ret = KErrHostResNameTooLong;
+ }
+
+ // Check the hw state - can't do anything if its switched off
+ //
+ if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff)
+ {
+ ret = KErrHardwareNotAvailable;
+ }
+
+ if (ret == KErrNone)
+ ret = iInquiryMgr.SetLocalName(utf8Name);
+
+ if (ret != KErrNone)
+ {
+ CompleteRequest(ret);
+ }
+ }
+
+void CBTHostResolver::GetHostName(TDes& aNameBuf)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iRequestState == EIdle, Panic(EHostResolverTwoRequests));
+
+ iHostNameBuf = &aNameBuf;
+ iRequestState=EGetLocalName;
+ TInt ret = KErrNone;
+
+ // Check the hw state - can't do anything if its switched off
+ //
+ if(iInquiryMgr.HWState()==CBTInquiryMgr::EOff)
+ {
+ ret = KErrHardwareNotAvailable;
+ }
+
+ if(ret==KErrNone)
+ iInquiryMgr.GetLocalName();
+ else
+ CompleteRequest(ret);
+ }
+
+
+void CBTHostResolver::CancelCurrentOperation()
+/**
+ Current operation cancel by user app.
+ Reset, so that we don't bother picking up any future results.
+ We don't actually bother to cancel the async. operations, as
+ if they succeed, the InquiryMgr may as well cache them anyway.
+ The next operation after this (if there is any), must be a fresh
+ one -- i.e. it can not be a next.
+**/
+ {
+ LOG_FUNC
+ iRequestState = EIdle;
+ CompleteCurrentOperation();
+ }
+
+void CBTHostResolver::InquiryResult(CBTInqResultRecord& aResult)
+ {
+ LOG_FUNC
+ if (iInquiryStatus != EInquiring)
+ return;
+
+ // Check if we've already seen this result
+ CBTInqResultRef* ref = iResults.FindEntry(aResult.LogEntry().iBdaddr);
+ if (ref)
+ return;
+
+ TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr);
+ if(!aResult.HasRespondedToIAC(sa.IAC()))
+ return; // It's never responded to our IAC, so ignore it
+
+ ref = iResults.Add(aResult);
+ if (!ref)
+ {
+ InquiryComplete(KErrNoMemory);
+ return;
+ }
+
+ if(iNameLookupMode == EDoGetNames)
+ {
+ // Find out whether cache was supposed to be ignored from TNameRecord that was passed into GetByAddress() previously
+ TBool ignoreCache = (sa.Action() & KHostResIgnoreCache) ? ETrue : EFalse;
+ // Handle a "special" case: Cache is to be ignored, but we got the _complete_ name during the inquiry process inside an EIR
+ // (which doesn't count as cached data, since it's freshly arrived from the radio). In this case we don't really want to
+ // launch a remote name request
+ if(ignoreCache && ref->Result().IsEirPresent() && ref->Result().Codec().IsDataTypePresent(EEirLocalNameComplete))
+ {
+ // Do nothing, the entry already contains the name
+ }
+ else
+ {
+ iInquiryMgr.LookupName(*this, aResult.LogEntry().iBdaddr, ignoreCache, EFalse);
+ }
+ }
+
+ if (iRequestState == EInquiry)
+ {
+ TryToCompleteRequest();
+ }
+ }
+
+void CBTHostResolver::NameLookupResult(TInt aErr, const TBTDevAddr& aAddr, const TDesC8& aName)
+ {
+ LOG_FUNC
+ if (iNameLookupMode == EDontGetNames)
+ return; // We're not actually after names
+
+ // Check we've got this result recorded (mostly for error cases)
+ CBTInqResultRef* ref = iResults.FindEntry(aAddr);
+ if (ref && !(ref->Result().HaveNameLookupResult() && ref->Result().IsNameComplete()))
+ {
+ // we have successfully created a reference and there isn't a complete name available.
+ ref->Result().SetNameLookupResultCode(aErr);
+ ref->Result().SetNameComplete(ETrue);
+ ref->Result().SetName(aName);
+ }
+
+ TryToCompleteRequest();
+ }
+
+void CBTHostResolver::InquiryComplete(TInt aErr)
+ {
+ LOG_FUNC
+ // LC changed - if we are just doing a name lookup, iInquiryStatus
+ // doesn't get set so need additional check here.
+ //
+ if ((iInquiryStatus != EInquiring) && (iRequestState != ENameLookup))
+ return;
+ iInquiryStatus = EInquiryComplete;
+ iInqCompletionCode = aErr;
+ if(aErr==KErrHardwareNotAvailable)
+ iRequestState=EError;
+ TryToCompleteRequest();
+ }
+
+void CBTHostResolver::SetLocalNameComplete(TInt aErr)
+ {
+ LOG_FUNC
+ if (iRequestState != ESetLocalName)
+ return;
+ CompleteRequest(aErr);
+ }
+
+void CBTHostResolver::GetLocalNameComplete(TInt aErr, const TDesC8& aName)
+ {
+ LOG_FUNC
+ if (iRequestState != EGetLocalName)
+ return;
+ if (aErr == KErrNone)
+ {
+ aErr = CnvUtfConverter::ConvertToUnicodeFromUtf8(*iHostNameBuf, aName);
+ // Just allow names to be truncated...
+ }
+ CompleteRequest(aErr >= 0 ? KErrNone : aErr);
+ }
+
+TUint CBTHostResolver::GetIAC() const
+ {
+ LOG_FUNC
+ if (!iNameRecord)
+ {
+ return 0; // avoid dereferencing a null ptr
+ }
+
+ TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr);
+ return sa.IAC();
+ }
+
+TInt CBTHostResolver::SecurityCheck(MProvdSecurityChecker *aSecurityChecker)
+ {
+ LOG_FUNC
+ __ASSERT_ALWAYS(aSecurityChecker, User::Panic(KSECURITY_PANIC, EBTPanicNullSecurityChecker));
+
+ iSecurityChecker = aSecurityChecker;
+ return iSecurityChecker->CheckPolicy(KLOCAL_SERVICES, KBT_HOSTRESOLVER_NAME_DIAG);
+ }
+
+void CBTHostResolver::TryToCompleteRequest()
+/**
+ This is where the work is done.
+ A big old state machine that tries to get us one step
+ closer to completing the query.
+ Gets an inquiry result out of the result set if appropriate and
+ there is one available.
+ Gets the name for the current result if required.
+ Completes the request if there is then nothing more to do.
+ Must only break out (i.e. return) if the request is completed, or
+ non-blocking mode requested
+**/
+ {
+ LOG_FUNC
+ TInquirySockAddr& sa = TInquirySockAddr::Cast(iNameRecord->iAddr);
+ if ((sa.Action() & KHostResInquiry) && iRequestState == EInquiry)
+ {
+ CBTInqResultRef* refNextRes = iResults.NextResult();
+ if (refNextRes)
+ {// Got a result to send up
+ if (iNameLookupMode == EDoGetNames)
+ {// Need to get the name of this baby...
+ iRequestState = ENameLookup;
+ }
+ else
+ {//Complete!
+ refNextRes->Result().GetInquirySockAddr(sa);
+ // If EIR was requested and we got one, stick it in the name field
+ TInt err = KErrNone;
+ if(sa.Action() & KHostResEir)
+ {
+ err = refNextRes->Result().GetEir(*iNameRecord, (sa.Action() & KHostResIgnoreCache) && !(sa.Action() & KHostResName));
+ }
+ else
+ {
+ err = refNextRes->Result().GetName(*iNameRecord);
+ }
+ if(err != KErrNone)
+ {
+ // Set name back to zero length
+ iNameRecord->iName.Zero();
+ }
+ CompleteRequest(err);
+ return;
+ }
+ }
+ else if (iInquiryStatus == EInquiryComplete)
+ {// Bad luck. No more results availble!
+ if (iInqCompletionCode == KErrNone)
+ {// Inquiry completed fine, so report end of file.
+ CompleteRequest(KErrHostResNoMoreResults);
+ }
+ else
+ {// Some error in the inquiry, so report that.
+ CompleteRequest(iInqCompletionCode);
+ }
+ return;
+ }
+ }
+
+ if ((sa.Action() & KHostResName) && iRequestState == ENameLookup)
+ {
+ CBTInqResultRef* refNextRes = iResults.CurrentResult();
+ if(!refNextRes)
+ {// Can't get the name... no result to lookup
+ CompleteRequest(KErrHostResNoMoreResults);
+ return;
+ }
+ // Walk through results and check if we have a name to complete
+ CBTInqResultRef* startResult = refNextRes;
+ do
+ {
+ CBTInqResultRecord& rec = refNextRes->Result();
+ if(rec.HaveNameLookupResult() && !rec.IsNameRequestPending() && rec.IsNameComplete())
+ {// Got a name! Complete the request
+ rec.GetInquirySockAddr(sa);
+ TInt err = rec.NameLookupResultCode();
+ if (err == KErrNone)
+ {// Copy & convert the UTF-8 name over into the result record.
+ if(sa.Action() & KHostResEir)
+ {
+ err = rec.GetEir(*iNameRecord, EFalse);
+ }
+ else
+ {
+ err = rec.GetName(*iNameRecord);
+ }
+ }
+ if (err != KErrNone)
+ {
+ iNameRecord->iName.Zero();
+ }
+ // Name lookup complete (poss. with errors)
+ if(sa.Action() & KHostResInquiry)
+ {// Ignore name failures if we're also doing inquiry
+ CompleteRequest(KErrNone);
+ }
+ else
+ {// Report any error encountered (or none)
+ CompleteRequest(err);
+ }
+ return;
+ }
+
+ iResults.MoveToback(*refNextRes);
+ refNextRes = iResults.NextResult();
+ } while(refNextRes != startResult);
+ }
+ else if(iRequestState==EError)
+ {
+ CompleteRequest(KErrHardwareNotAvailable);
+ }
+ if(sa.Action() & KHostResDontBlock)
+ {// Complete any outstanding non-blocking operation
+ CompleteRequest(KErrWouldBlock);
+ }
+
+ }
+
+void CBTHostResolver::CompleteRequest(TInt aErr)
+ {
+ LOG_FUNC
+ if (iRequestState == EIdle && aErr == KErrNone)
+ return;
+ iRequestState = EIdle;
+ if (aErr != KErrNone)
+ {// End of query -- tidy up
+ CompleteCurrentOperation();
+ }
+ iNotify->QueryComplete(aErr);
+ }
+
+void CBTHostResolver::CompleteCurrentOperation()
+/**
+ Operation known to be complete.
+ This happens through a CancelCurrentOperation, or when
+ the current operation has read all the results out.
+**/
+ {
+ LOG_FUNC
+ if (iInquiryStatus == EInquiryReady)
+ return;
+ iInquiryStatus = EInquiryReady;
+ iResults.Reset();
+ }
+
+
+//
+// BT Inquiry Manager.
+//
+CBTInquiryMgr::CBTInquiryMgr(CLinkMgrProtocol& aProtocol)
+ : iLinkMgrProtocol(aProtocol),
+ iNamePageTimeout((KMaxNamePageTimeout + KMinNamePageTimeout)/2), // start with mid-point timeout
+ iInquiryMode(EStandardInquiryFormat), iHRs(CBTHostResolver::LinkOffset())
+ {
+ LOG_FUNC
+ }
+
+CBTInquiryMgr::~CBTInquiryMgr()
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iHRs.IsEmpty(), Panic(EBTHostControllerQueueNotEmpty));
+ SetHWState(EOff);
+ PublishStatus();// we're not doing anything more so publish status
+ ClearHCICommandQueue();
+ delete iFlusher;
+ delete iConnectingStatus;
+ }
+
+void CBTInquiryMgr::ClearHCICommandQueue()
+ {
+ LOG_FUNC
+ if (iCommandQueue)
+ {
+ iCommandQueue->MhcqRemoveAllCommands(*this);
+ }
+ iCommandQueue = NULL;
+ }
+
+void CBTInquiryMgr::SetHCICommandQueue(MHCICommandQueue& aCommandQueue)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(!iCommandQueue, Panic(EBTCommandQueueAlreadyExist));
+ iCommandQueue = &aCommandQueue;
+ }
+
+MHCICommandQueue& CBTInquiryMgr::CommandQueue() const
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(iCommandQueue, Panic(EBTCommandQueueNotFound));
+ return *iCommandQueue;
+ }
+
+CBTInquiryMgr* CBTInquiryMgr::NewL(CLinkMgrProtocol& aProtocol)
+ {
+ LOG_STATIC_FUNC
+ CBTInquiryMgr* self = new(ELeave) CBTInquiryMgr(aProtocol);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CBTInquiryMgr::ConstructL()
+ {
+ LOG_FUNC
+ iFlusher = CPeriodic::NewL(CActive::EPriorityLow);
+#ifdef CONNECTION_PREEMPTS_INQUIRY
+ iConnectingStatus = CConnectingStatusSubscriber::NewL(*this);
+#endif
+ }
+
+CBTHostResolver* CBTInquiryMgr::NewHostResolverL()
+ {
+ LOG_FUNC
+ // Leave if HW switched off
+ //
+ if(iHWState==EOff)
+ User::Leave(KErrHardwareNotAvailable);
+
+ CBTHostResolver* r = new(ELeave) CBTHostResolver(*this);
+ iHRs.AddFirst(*r);
+ return r;
+ }
+
+void CBTInquiryMgr::DeletingHostResolver()
+ {
+ LOG_FUNC
+ // A host resolver is being deleted - we need to check if any other host resolvers want inquiry results from the current IAC.
+ if(!iRequestedInquiryIAC)
+ {
+ //No 'current' IAC exists
+ return;
+ }
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ TBool iacFound=EFalse;
+ while (iter)
+ {
+ if ((iter++)->GetIAC() == iRequestedInquiryIAC)
+ {
+ iacFound = ETrue;
+ }
+ }
+ if (!iacFound)
+ {
+ iRequestedInquiryIAC = 0;
+ }
+ }
+
+void CBTInquiryMgr::InquiryResult(TInt aErr,const TInquiryLogEntry& aEntry)
+/**
+ Single Inquiry result received.
+ Called via HCI for each incoming inquiry result. Need to demultiplex these
+ out to all CBTHostResolver's interested in them.
+
+ @param aErr An EPOC error code. Any HCI mapping should be done before
+ calling this function.
+ @param aEntry The log entry returned through HCI.
+**/
+ {
+ LOG_FUNC
+ LOG5(_L("err: %d,ScanRepM: %d ScanPerM: %d ScanM: %d Type: %d"),
+ aErr,
+ aEntry.iPageScanRepetitionMode,
+ aEntry.iPageScanPeriodMode,
+ aEntry.iPageScanMode,
+ aEntry.iSpare );
+
+ CBTInqResultRef* ref = 0;
+
+ //Revise our cache record regardless - at least need to make sure
+ //it knows we have received juice from the HCI
+ if(aErr == KErrNone)
+ {
+ ref = AddEntryWithJuiceToCache(aEntry);
+ if (!ref)
+ {
+ aErr = KErrNoMemory;
+ }
+ else
+ {
+ ref->Result().iFlushes = 0;
+ ref->Result().iFoundDuringCurrentInquiry = ETrue;
+
+ TInt ret = ref->Result().AddIAC(iCurrentInquiryIAC);
+ switch(ret)
+ {
+ case KErrNone:
+ ++iResultCount;
+ // drop through
+ case KErrAlreadyExists:
+ break;
+ default: // Got an error adding the IAC
+ aErr = ret;
+ } // switch
+ ref->Result().AddIAC(KGIAC); // This would come back on a GIAC, so try adding GIAC.
+ // If this doesn't work, it doesn't really matter, so ignore the error.
+ } // else
+ } // if (aErr == KErrNone)
+
+ if (aErr != KErrNone)
+ {
+ InquiryComplete(aErr, 0);
+ return;
+ }
+
+ // Tell host resolvers
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ CBTHostResolver* hr;
+ while ((hr = iter ++) != NULL)
+ {
+ if (hr->GetIAC() == iCurrentInquiryIAC || hr->GetIAC() == KGIAC)
+ {
+ hr->InquiryResult(ref->Result());
+ }
+ }
+ }
+
+void CBTInquiryMgr::CoDResult(const TBTDevAddr& aAddr, TUint aCoD)
+/**
+ We have been donated a CoD from an incoming connection
+ This is useful to keep in the registry for remotely
+ initiated pairings - otherwise we will never have found
+ the CoD of the device bonding with us
+**/
+ {
+ // create record if it doesn't already exist in cache.
+ // If this fails, we'll just not be able to store the CoD for this device.
+
+ LOG_FUNC
+ AddEntryWithCoDToCache(aAddr, aCoD);
+ }
+
+void CBTInquiryMgr::ClockOffsetResult(const TBTDevAddr& aAddr, TBasebandTime aClockOffset)
+/**
+ We have been donated a Clock Offset - maybe an update
+**/
+ {
+ LOG_FUNC
+ AddEntryWithClockOffsetToCache(aAddr, aClockOffset);
+ }
+
+void CBTInquiryMgr::InquiryComplete(TInt aErr, TUint8 /*aNumResponses*/)
+/**
+ Current Inquiry has completed.
+ Must tell all CBTHostResolver's who may be waiting for inquiry results.
+
+ @param aErr An EPOC error code. Any HCI mapping should be done before
+ calling this function.
+ @param aNumResponses The number of calls to InquiryResult that should
+ have been generated. We don't use this info atm, though.
+**/
+ {
+ LOG_FUNC
+ SetCacheAge(iCurrentInquiryIAC, 0);
+ TUint iacToComplete = iRequestedInquiryIAC;
+ iRequestedInquiryIAC = 0;
+ iFlusher->Cancel(); // Stop watchdog, start flusher
+ SetHWState(EIdle);
+ // don't publish status here, might be doing a name lookup
+ EnableFlusher();
+
+ // The inquiry has completed so clear any results that were marked found during
+ // the just finished inquiry
+ ClearCurrentInquiryResults();
+
+ if(aErr!=KErrHardwareNotAvailable)
+ {
+ LOG(_L("CBTInquiryMgr::InquiryComplete asking for another name lookup"));
+ DoNameLookup(ETrue); // Get any pending name lookups going
+ }
+ else
+ {
+ SetHWState(EOff);
+ LOG(_L("CBTInquiryMgr::InquiryComplete PublishState Idle"));
+ PublishStatus();// we're not doing name lookup so publish status
+ }
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ CBTHostResolver* hr;
+ while ((hr = iter++) != NULL)
+ {
+ if (hr->GetIAC() == iacToComplete)
+ {
+ hr->InquiryComplete(aErr);
+ }
+ hr++;
+ }
+ // Only queue the inquiry if we have completed all the name requests from this one.
+ // Otherwise, it will be issued after all the remote name requests have completed.
+ if (iQueuedInquiryIAC != 0 && iPendingNameRequests == 0)
+ {
+ iRequestedInquiryIAC = iQueuedInquiryIAC;
+ iQueuedInquiryIAC = 0;
+ DoInquiry();
+ }
+ }
+
+/**
+Name lookup result received.
+Called via HCI for each incoming name result.
+
+The name lookup may not have been requested by the inquiry mgr as when the
+stack gets a new connection it tries to solicit information about the
+remote. This sort of result is just to add to our cache.
+
+Otherwise it's from a request on us, pass it on for handling.
+
+@param aErr An EPOC error code. Any HCI mapping should be done before
+ calling this function.
+@param aAddr The address of the Bluetooth device that the name is associated
+ with.
+@param aBuf 8 bit Descriptor up to KHCIRemoteDeviceNameMaxLength
+ bytes long, containing the name.
+*/
+void CBTInquiryMgr::RemoteNameResult(TInt aErr, const TBTDevAddr& aAddr, const TBTDeviceName8& aBuf)
+ {
+ LOG_FUNC
+ LOG1(_L("aErr = %d"), aErr);
+ LOG6(_L(", aAddr: %02x %02x %02x %02x %02x %02x"),
+ TUint8(aAddr[0]), TUint8(aAddr[1]), TUint8(aAddr[2]), TUint8(aAddr[3]), TUint8(aAddr[4]),
+ TUint8(aAddr[5]));
+
+ CBTInqResultRef* ref = FindExistingCacheEntry(aAddr);
+ if(ref)
+ {
+ CBTInqResultRecord& rec = ref->Result();
+ if (rec.IsNameRequestPending())
+ {
+ // We asked for this
+ HandleRemoteNameResult(aErr, *ref, aBuf);
+ return;
+ }
+ }
+
+ // Unsolicited, just wham in cache if it's good
+ if (aErr == KErrNone)
+ {
+ CBTInqResultRef* ref = AddEntryToCache(aAddr);
+ if(ref)
+ {
+ CBTInqResultRecord& rec = ref->Result();
+
+ rec.SetName(aBuf);
+ rec.SetNameComplete(ETrue);
+ rec.iFlushes = 0;
+ rec.SetNameValid(ETrue);
+ rec.SetNameRefreshRequested(EFalse);
+ }
+ }
+ }
+
+/**
+Remote host supported features notification received.
+
+The remote host supported feature notification may be received during a
+remote name request if the remote device provides entries in the remote
+host supported feature page.
+
+@param aErr An EPOC error code. Any HCI mapping should be done before
+ calling this function.
+@param aAddr The address of the Bluetooth device that the name is associated
+ with.
+@param aHostSupportedFeatures 64 bit field holding the remote host supported
+ features page.
+*/
+void CBTInquiryMgr::RemoteHostSupportedFeatures(TInt /*aErr*/, const TBTDevAddr& /*aAddr*/, const TUint64& /*aHostSupportedFeatures*/)
+ {
+ LOG_FUNC
+ // This is currently not used by the stack since the optimisation it could provide would be
+ // difficult to take advantage of and of minimal use.
+
+ // If to be used the result should be stored in the inquiry result record along with whether
+ // a name request has been made (with EIR it is possible to have retrieved the name without
+ // a name request)
+ }
+
+/**
+Name lookup result received.
+
+We need to demultiplex these out to all CBTHostResolver's
+interested in them.
+
+@param aErr An EPOC error code. Any HCI mapping should be done before
+ calling this function.
+@param aRef The inquiry result for this info.
+@param aBuf 8 bit Descriptor up to KHCIRemoteDeviceNameMaxLength
+ bytes long, containing the name.
+*/
+void CBTInquiryMgr::HandleRemoteNameResult(TInt aErr, CBTInqResultRef& aRef, const TBTDeviceName8& aBuf)
+ {
+ LOG_FUNC
+
+ if(iHWState != EConnecting)
+ {
+ SetHWState(EIdle);
+ // don't set publish status here, doinquiry will do that
+ }
+
+ if(aErr == KErrNone)
+ {
+ if(iNamePageTimeout > KMinNamePageTimeout)
+ {// got name OK, dec the page timeout
+ iNamePageTimeout -= KNamePageTimeoutIncrement;
+ }
+ }
+ else if(aErr == KHCIErrorBase - EPageTimedOut)
+ {
+ if(iNamePageTimeout < KMaxNamePageTimeout)
+ {// Got pagetime, inc. pagetime a bit
+ iNamePageTimeout += KNamePageTimeoutIncrement;
+ }
+ }
+
+
+ CBTInqResultRecord& rec = aRef.Result();
+
+ --iPendingNameRequests;
+ rec.SetNamePending(EFalse);
+ ++(rec.iNameLookupAttempts);
+ if(iPendingNameRequests <= 0)
+ {
+ iNewPageRequestsPending = EFalse;
+ }
+
+ // Update our cached version.
+ if (aErr == KErrNone)
+ {
+ rec.SetName(aBuf);
+ rec.SetNameComplete(ETrue);
+ rec.iFlushes = 0;
+ rec.SetNameValid(ETrue);
+ rec.SetNameRefreshRequested(EFalse);
+ rec.SetExplicitNameRequest(EFalse); // If we had an explicit name request on this, we've completed it now
+ }
+ else
+ {
+ // got an error
+ if(rec.NameLookupAttempts() < KMaxNameLookupAttempts)
+ {// try to get later - put to back of Que for getting
+ ++iPendingNameRequests;
+ rec.SetNamePending(ETrue);
+ iCurrentResults.MoveToback(aRef);
+ }
+ else
+ {// No more chances -- record lookup error code
+ rec.SetNameLookupResultCode(aErr);
+ rec.SetExplicitNameRequest(EFalse); // If we had an explicit name request on this, we've completed it now
+ }
+ }
+
+ // Try to get more names, if needed
+ LOG(_L("CBTInquiryMgr::HandleRemoteNameResult asking for another name lookup"));
+ DoNameLookup(EFalse);
+
+ if(!aRef.Result().IsNameRequestPending())
+ {// Don't propogate if name request is still pending
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ while (iter)
+ {
+ (iter++)->NameLookupResult(aErr, rec.LogEntry().iBdaddr, aBuf);
+ }
+ }
+
+ // In case we're now free, do inquiry
+ LOG(_L("CBTInquiryMgr::HandleRemoteNameResult asking for another inquiry"));
+ if (iPendingNameRequests == 0 && iRequestedInquiryIAC == 0) // If we've completed the current inquiry, see if we've got another one queued.
+ {
+ iRequestedInquiryIAC = iQueuedInquiryIAC;
+ iQueuedInquiryIAC = 0;
+ }
+ DoInquiry();
+ }
+
+const TDesC8* CBTInquiryMgr::DeviceNameFromCache(const TBTDevAddr& aAddr)
+/**
+ Used by other parts of the stack to get a name synchronously from cache if
+ there is one
+
+ @return NULL if device is unknwon
+**/
+ {
+ LOG_FUNC
+ CBTInqResultRef* entry = FindExistingCacheEntry(aAddr);
+ if (!entry)
+ return NULL;
+ else
+ {
+ return &entry->Result().Name();
+ }
+ }
+
+CBTInqResultRecord* CBTInquiryMgr::BasebandParametersFromCache(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ CBTInqResultRef* entry = FindExistingCacheEntry(aAddr);
+ if (!entry)
+ return NULL;
+ else
+ {
+ return &(entry->Result());
+ }
+ }
+
+void CBTInquiryMgr::SetHWState(THWState aState)
+ {
+ LOG_FUNC
+ iHWState = aState;
+ }
+
+void CBTInquiryMgr::PublishStatus()
+ {
+ LOG_FUNC
+ // sets the inquiry/discovery status to bool true or false
+ TBool inquiryState = EFalse;
+
+ switch (iHWState)
+ {
+ /* deliberate fall throughs */
+ case EInquiry:
+ case ENameLookup:
+ {
+ inquiryState = ETrue;
+ }
+ break;
+ case EIdle:
+ case EConnecting:
+ case EOff:
+ default:
+ {
+ // defaulted above to EFalse;
+ }
+ break;
+ }
+ /* only publish status if we've changed
+ it is however the case that if the state changes sufficiently quickly
+ the subscriber will miss some of the state changes
+ this is particularly the case while doing name searches where we toggle into
+ idle and back to namelookup quickly and regularly, the subscriber
+ sees a stream of etrues coming through, the efalses are missing
+ this is probably ok as long as they know because this means they are told
+ more than once they are inquiring, and indeed they are, its certainly better
+ than being told they're not if they are */
+ if (inquiryState != iReportedInquiryState)
+ {
+ iLinkMgrProtocol.SetUIDiscovering(inquiryState);
+ iReportedInquiryState = inquiryState;
+ if (inquiryState)
+ {
+ LOG(_L("CBTInquiryMgr::PublishState setting status TRUE"));
+ }
+ else
+ {
+ LOG(_L("CBTInquiryMgr::PublishState setting status FALSE"));
+ }
+ }
+ else
+ {
+ LOG(_L("CBTInquiryMgr::PublishState not updating status"));
+ }
+ }
+
+void CBTInquiryMgr::SetLocalNameComplete(TInt aErr)
+ {
+ LOG_FUNC
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ while (iter)
+ {
+ (iter++)->SetLocalNameComplete(aErr);
+ }
+ }
+
+void CBTInquiryMgr::GetLocalNameComplete(TInt aErr, const TDesC8& aName)
+ {
+ LOG_FUNC
+ TDblQueIter<CBTHostResolver> iter(iHRs);
+ while (iter)
+ {
+ (iter++)->GetLocalNameComplete(aErr, aName);
+ }
+ }
+
+void CBTInquiryMgr::StartInquiry(CBTHostResolver& aResolver, TUint aIAC, TBool aIgnoreCache)
+ {
+ LOG_FUNC
+ // Pre-load any existing results into the CBTHostResolver
+ if (!aIgnoreCache)
+ {
+ // Pre-load any existing results into the CBTHostResolver
+ iCurrentResults.ReturnToFirstResult();
+ while (CBTInqResultRef* ref = iCurrentResults.NextResult())
+ {
+ // If GIAC is being requested, we return all results
+ if (aIAC == KGIAC || ref->Result().HasRespondedToIAC(aIAC))
+ {
+ aResolver.InquiryResult(ref->Result());
+ }
+ }
+ }
+
+ if ( !aIgnoreCache && !iCurrentResults.IsEmpty() && CacheAge(aIAC) <= KCacheStaleAge)
+ {// Cache not yet stale, so just give these results back and dont start
+ LOG(_L("CBTInquiryMgr::StartInquiry giving result from cache"));
+ aResolver.InquiryComplete(KErrNone);
+ return;
+ }
+
+ if (iRequestedInquiryIAC || iQueuedInquiryIAC)
+ {
+ LOG(_L("CBTInquiryMgr::StartInquiry iRequestedInquiryIAC"));
+ if(iRequestedInquiryIAC == aIAC)
+ {
+ // an Inquiry is ongoing, return any results already found during the
+ // current Inquiry if not already done so as part of the complete cache
+ if (aIgnoreCache)
+ {
+ iCurrentResults.ReturnToFirstResult();
+ while (CBTInqResultRef* ref = iCurrentResults.NextResult())
+ {
+ if (ref->Result().iFoundDuringCurrentInquiry)
+ {
+ aResolver.InquiryResult(ref->Result());
+ }
+ }
+ }
+ // the current Inquiry will just continue
+ return;
+ }
+ if (aIAC == KGIAC)
+ {
+ // If the current IAC is GIAC, and the requested inquiry IAC isn't, it must be LIAC
+ __ASSERT_DEBUG(iRequestedInquiryIAC == KLIAC, Panic(EBTUnexpectedIAC));
+ // Queue a general inquiry for when the current limited inquiry has finished
+ iQueuedInquiryIAC = aIAC;
+ return;
+ }
+ else if(iHWState == EInquiry)
+ {
+ // The host resolver should only allow through GIAC and LIAC, and we handle GIAC above
+ __ASSERT_DEBUG(aIAC == KLIAC, Panic(EBTUnexpectedIAC));
+ // We favour a Limited inqiury, so interrupt the current general inquiry
+ TInt err = CancelHardwareInquiry();
+ if(err!=KErrNone)
+ {
+ LOG(_L("CBTInquiryMgr::StartInquiry cancel didn't work"));
+ aResolver.InquiryComplete(err); // cancel went wrong
+ return;
+ }
+ // Queue a general inquiry for when the limited inquiry is complete
+ iRequestedInquiryIAC = KLIAC;
+ iQueuedInquiryIAC = KGIAC;
+ SetHWState(ECancellingForNewIAC);
+ iFlusher->Cancel(); // Stop watchdog, start flusher.
+ return;
+ }
+ else
+ {
+ // The host resolver should only allow through GIAC and LIAC, and we handle GIAC above
+ __ASSERT_DEBUG(aIAC == KLIAC, Panic(EBTUnexpectedIAC));
+ iRequestedInquiryIAC = KLIAC;
+ iQueuedInquiryIAC = KGIAC;
+ }
+ }
+
+ iRequestedInquiryIAC = aIAC;
+ iInquiryInteruptions = 0;
+
+ DoInquiry();
+ }
+
+void CBTInquiryMgr::Suspend()
+/**
+ Ah - somebody wishes to make a connection - we need to clear the way
+ If we are doing a namerequest we can cancel.
+
+ WARNING - The caller of this must ensure a call to Resume() ASAP
+**/
+ {
+ LOG_FUNC
+
+ if(iHWState == EInquiry)
+ {
+ // This is a best effort service, the Connect will be tried even
+ // if we fail to stop the inquiry, so ignore errors.
+ static_cast<void>(CancelHardwareInquiry());
+ }
+ else if (iHWState == ENameLookup)
+ {
+
+ iCurrentResults.ReturnToFirstResult();
+ while(CBTInqResultRef *ref = iCurrentResults.NextResult())
+ {
+
+ CBTInqResultRecord& rec = ref->Result();
+ if(rec.IsNameRequestPending())
+ {
+ // This is a best effort service, the Connect will be tried even
+ // if we fail to stop the Name request, so ignore errors.
+ TRAP_IGNORE(CancelRemoteNameL(rec.LogEntry().iBdaddr));
+ }
+ }
+
+ }
+
+ iFlusher->Cancel(); // Stop watchdog, start flusher.
+ SetHWState(EConnecting); // will stop inquirymanager doing anything else
+ // don't publish status here, will be cleared up when manager tries to do soemthing else
+ EnableFlusher();
+ }
+
+void CBTInquiryMgr::Resume()
+/**
+ We can now carry on...
+**/
+ {
+ LOG_FUNC
+ if(iHWState == EConnecting)
+ {
+ LOG(_L("InquiryMgr: Hardware connected; resuming operations (when a new inquiry starts"));
+ SetHWState(EIdle);
+ // don't publish status here, it will be set by namelookup
+ LOG(_L("CBTInquiryMgr::Resume asking for another name lookup"));
+ DoNameLookup(EFalse);
+ DoInquiry();
+ EnableFlusher(); // in case no operations started
+ }
+ }
+
+void CBTInquiryMgr::LookupName(CBTHostResolver& aResolver, const TBTDevAddr& aAddr, TBool aIgnoreCache, TBool aExplicitNameRequest)
+/**
+This method is called by a CBTHostResolver to look up the name of a device with a given address. If the
+name is not immediately available a name lookup request is queued within the inquiry manager. This
+request will be actioned by CBTInquiryMgr::DoNameLookup(), and the remote device response will be
+sent (by CBTInquiryMgr::RemoteNameResult) to all current host resolvers.
+**/
+ {
+ LOG_FUNC
+ LOG1(_L("CBTHostResolver: 0x%8x"), &aResolver);
+ LOG6(_L(", aAddr: %02x %02x %02x %02x %02x %02x"),
+ TUint8(aAddr[0]), TUint8(aAddr[1]), TUint8(aAddr[2]), TUint8(aAddr[3]), TUint8(aAddr[4]),
+ TUint8(aAddr[5]));
+
+ CBTInqResultRef* ref = AddEntryToCache(aAddr); //N.B. Creates a new entry if one doesn't exist
+
+ if(!ref)
+ {
+ aResolver.NameLookupResult(KErrNoMemory, aAddr, KNullDesC8);
+ return;
+ }
+
+ // check state of cache entry
+ CBTInqResultRecord& rec = ref->Result();
+
+ if(rec.HaveNameLookupResult() && rec.IsNameComplete() && (!aIgnoreCache|| rec.iFoundDuringCurrentInquiry))
+ {// Either got a name, or we made an attempt that failed
+ aResolver.NameLookupResult(rec.NameLookupResultCode(),
+ rec.LogEntry().iBdaddr,
+ rec.iName);
+ return;
+ }
+
+ if (aExplicitNameRequest)
+ {
+ rec.SetExplicitNameRequest(ETrue);
+ }
+
+ if(rec.IsNameRequestPending())
+ {
+ return; // it's already happening
+ }
+
+ rec.SetNamePending(ETrue);
+ rec.iNameLookupAttempts = 0;
+ ++iPendingNameRequests;
+ iNewPageRequestsPending = ETrue;
+
+ if(iHWState == EInquiry)
+ {
+ if(iPendingNameRequests > iInquiryInteruptions
+ || iInquiryInteruptions < KImmediateNameFetch)
+ {
+ TryToInterruptInquiryForNameLookup();
+ }
+ }
+ else
+ {
+ LOG(_L("CBTInquiryMgr::LookupName asking for another name lookup"));
+ DoNameLookup(EFalse);
+ }
+ }
+
+void CBTInquiryMgr::ClearCache()
+ {
+ LOG_FUNC
+ if(iHWState == EInquiry)
+ {
+ TInt err = CancelHardwareInquiry();
+ if(err==KErrNone)
+ {
+ // Don't clear cache if we couldn't cancel inquiry.
+ // We'll be slightly unfaithful in the case of OOM...
+ iCurrentResults.Reset();
+ InquiryComplete(KErrNone, 0);
+ }
+ }
+ else
+ {
+ iCurrentResults.Reset();
+ }
+ }
+
+TInt CBTInquiryMgr::SetLocalName(const TDesC8& aName)
+ {
+ LOG_FUNC
+ return iLinkMgrProtocol.SetLocalDeviceName(aName);
+ }
+
+TInt CBTInquiryMgr::GetLocalName()
+ {
+ LOG_FUNC
+ TRAPD(err, ReadLocalNameL());
+ return err;
+ }
+
+/**
+ This is called whenever the local HCI version is updated (usually only during startup),
+ so that the inquiry mode is set accordingly
+**/
+void CBTInquiryMgr::SetInquiryMode()
+ {
+ LOG_FUNC
+
+ THCIInquiryMode inquiryMode = EStandardInquiryFormat;
+
+ if (iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally())
+ {
+ inquiryMode = EInquiryWithRssiOrEir;
+ }
+ else if (iLinkMgrProtocol.IsRssiWithInquiryResultsSupportedLocally())
+ {
+ inquiryMode = EInquiryWithRssi;
+ }
+
+ if (inquiryMode != EStandardInquiryFormat)
+ {
+ TRAPD(err, WriteInquiryModeL(inquiryMode));
+ if (err == KErrNone)
+ {
+ iPendingInquiryMode = inquiryMode;
+ }
+ }
+ }
+/**
+ This is called whenever the pending WriteInquiryMode command has completed.
+**/
+void CBTInquiryMgr::WriteInquiryModeComplete(TBool aSucceeded)
+ {
+ LOG_FUNC
+ if (aSucceeded)
+ {
+ iInquiryMode = iPendingInquiryMode;
+ return;
+ }
+
+ // should we maybe try to set another mode? Probably worth a try...
+ // let's downgrade the inquiry mode
+ if (iPendingInquiryMode == EInquiryWithRssiOrEir)
+ {
+ TRAPD(err, WriteInquiryModeL(EInquiryWithRssi));
+ if (err == KErrNone)
+ {
+ iPendingInquiryMode = EInquiryWithRssi;
+ }
+ }
+ }
+
+void CBTInquiryMgr::DoInquiry()
+/**
+ Try to start the inquiry.
+ Only starts it if there isn't already one going.
+**/
+ {
+ LOG_FUNC
+
+ if(iRequestedInquiryIAC == 0 || iHWState != EIdle || iHRs.IsEmpty())
+ {
+#ifdef _DEBUG
+ LOG3(_L("Not starting inquiry. iRequestedInquiryIAC == %d, iHWState == %d, iHostResolverCount == %d"), iRequestedInquiryIAC, iHWState, iNumHRs);
+ if (iHRs.IsEmpty())
+ {
+ LOG(_L("No HRs interested in results - Stopping discovery"));
+ }
+#endif
+ if (iHWState == EIdle)
+ {
+ LOG(_L("CBTInquiryMgr::DoInquiry PublishStatus Idle"));
+ PublishStatus();// make sure the status says we're idle
+ }
+ return;
+ }
+
+ // If pending on name lookup, we'll do it when that finishes
+
+ // Cache stale -- update it.
+ TInt err = StartHardwareInquiry();
+
+ if (err == KErrNone)
+ {
+ SetHWState(EInquiry);
+ LOG(_L("CBTInquiryMgr::DoInquiry PublishState Inquiry"));
+ PublishStatus();
+ // Use cache age to estimate time elapsed while inquiring.
+ // Synchronise it with the inquiry
+ // Stop flusher, Start inquiry wathcdog
+ iFlusher->Cancel();
+ TCallBack cb(InquiryWatchdog, this);
+ iFlusher->Start(KInquiryWatchdogPeriod * 1000000,
+ KInquiryWatchdogPeriod * 1000000,
+ cb);
+ SetCacheAge(iCurrentInquiryIAC, 0);
+ iInquirySilenceCount = 0;
+ iResultCount = 0;
+ }
+ else
+ {
+ // Couldn't start inquiry.
+ // Make sure the request is completed.
+ iRequestedInquiryIAC = 0;
+ InquiryComplete(err, 0);
+ LOG(_L("CBTInquiryMgr::DoInquiry PublishState Idle couldn't start inquiry"));
+ PublishStatus();
+ return;
+ }
+ }
+
+void CBTInquiryMgr::ClearCurrentInquiryResults()
+ {
+ LOG_FUNC
+ iCurrentResults.ReturnToFirstResult();
+ while (CBTInqResultRef* ref = iCurrentResults.NextResult())
+ {
+ ref->Result().iFoundDuringCurrentInquiry = EFalse;
+ }
+ }
+
+TInt CBTInquiryMgr::CancelHardwareInquiry()
+ {
+ LOG_FUNC
+ // CancelInquiryL will stop the current inquiry so clear any results that
+ // were marked found during the inquiry
+ ClearCurrentInquiryResults();
+
+ TRAPD(err, CancelInquiryL());
+ return err;
+ }
+
+TInt CBTInquiryMgr::StartHardwareInquiry()
+ {
+ LOG_FUNC
+ iCurrentInquiryIAC = iRequestedInquiryIAC;
+ // attempt to free up baseband space for best performance discovery
+ iLinkMgrProtocol.PhysicalLinksMgr().RequireSlotSpace(); // will *eventually* put connections in hold - we dont wait though
+ TRAPD(err, StartInquiryL(iCurrentInquiryIAC, KInquiryLength, KInquiryMaxResults));
+
+ return err;
+ }
+
+
+
+
+void CBTInquiryMgr::DoNameLookup(TBool aInquiryComplete)
+/**
+This method attempts to issue a name lookup to the HCI
+**/
+ {
+ LOG_FUNC
+
+ if(iHWState != EIdle || iPendingNameRequests == 0 || iHRs.IsEmpty())
+ {
+#ifdef _DEBUG
+ LOG3(_L("Not starting name lookup. iCurrentInquiryIAC == %d, iHWState == %d, iHostResolverCount == %d"), iCurrentInquiryIAC, iHWState, iNumHRs);
+#endif
+ /* if hw is idle and the inquiry is ready we are finished
+ and will not get another inquiry call so publish status idle
+ note that if doing 'inquiry with names'
+ in inquiry the aInquiryComplete flag stops state oscilating
+ thostres name request for all devices will still cause this to
+ oscilate because each name request is done individually, at this layer
+ we have no idea whether the test program will ask for another name */
+ if ((iHWState == EIdle) && (aInquiryComplete))
+ {
+ LOG(_L("CBTInquiryMgr::DoNameLookup inquiry finished with"));
+ PublishStatus();
+ }
+ // else publish status will be sorted by next inquiry
+ return;
+ }
+
+ iCurrentResults.ReturnToFirstResult();
+ CBTInqResultRef *refToGet = NULL;
+ while(CBTInqResultRef *ref = iCurrentResults.NextResult())
+ {
+ CBTInqResultRecord& rec = ref->Result();
+ if(rec.IsNameRequestPending())
+ {
+ if(!iRequestedInquiryIAC ||
+ rec.NameLookupAttempts() < KMaxNameLookupAttemptsDuringInquiry)
+ {
+ // We want the first record for the current IAC or a record for an explicit name request.
+ // Failing that, we'll just have the first record
+ if (rec.HasRespondedToIAC(iCurrentInquiryIAC) || rec.IsExplicitNameRequest())
+ {
+ refToGet = ref;
+ break;
+ }
+ if (!refToGet)
+ {
+ refToGet = ref;
+ }
+ }
+ }
+ }
+ if (refToGet)
+ {
+ CBTInqResultRecord& rec = refToGet->Result();
+
+ // First set the page timeout, it is a best effort attempt
+ iNamePageTimeout = iLinkMgrProtocol.PhysicalLinksMgr().TryToChangePageTimeout(iNamePageTimeout);
+
+ // Request the name lookup.
+ TRAPD(err, LookupNameL(rec.LogEntry()));
+
+ if (err == KErrNone)
+ {
+ SetHWState(ENameLookup);
+ return;
+ }
+ else
+ {
+ // got a synchronous error - put in a blank name
+ HandleRemoteNameResult(err, *refToGet, KNullDesC8());
+ }
+ }
+ // Nothing new to do!
+ iNewPageRequestsPending = EFalse;
+ }
+
+void CBTInquiryMgr::TryToInterruptInquiryForNameLookup()
+ {
+ LOG_FUNC
+ TInt err = CancelHardwareInquiry();
+
+ if(err == KErrNone)
+ {// Start doing name lookups
+ iFlusher->Cancel(); //Stop watchdog, start flusher
+ SetHWState(EIdle);
+ // don't publish status here, namelookup/inquiry will deal with it
+ EnableFlusher();
+ ++iInquiryInteruptions;
+ LOG(_L("CBTInquiryMgr::TryToInterruptInquiryForNameLookup asking for another name lookup"));
+ DoNameLookup(EFalse);
+ DoInquiry(); // <- In case name lookup fails
+ }
+ }
+
+// An inelegant workaround called from CLinkMgrProtocol::Error to get round
+// the fact that the stack doesn't (yet) keep track of commands that it has
+// been asked to do. If they aren't completed on a reset due to a
+// KErrHardwareNotFound error then the stack hangs as it waits for the responses.
+void CBTInquiryMgr::CompleteCommands(TInt aErr)
+ {
+ LOG_FUNC
+ SetLocalNameComplete(aErr);
+ GetLocalNameComplete(aErr, _L8(""));
+
+ // Reset any inquiry results left over from a previous inquiry
+ iCurrentResults.Reset();
+ InquiryComplete(aErr, 0);
+ }
+
+
+// Check whether the controller supports Extended Inquiry Response by examing
+// the supported features mask using Link Manage Protocol
+TBool CBTInquiryMgr::IsExtendedInquiryResponseSupported()
+ {
+ LOG_FUNC
+ return iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally();
+ }
+
+/******************************************************************************/
+/* If UPDATING THE CACHE, please use one of the "AddEntry..." methods below. */
+/******************************************************************************/
+CBTInqResultRef* CBTInquiryMgr::FindExistingCacheEntry(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ return(iCurrentResults.FindEntry(aAddr));
+ }
+
+CBTInqResultRef* CBTInquiryMgr::AddEntryToCache(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ CBTInqResultRef* ref = FindExistingCacheEntry(aAddr);
+ if(ref)
+ {// Already in there!
+ return ref;
+ }
+
+ CBTInqResultRecord* rec = new CBTInqResultRecord(aAddr);
+ if (!rec)
+ {
+ return 0;
+ }
+
+ ref = iCurrentResults.Add(*rec);
+ if (!ref)
+ {
+ delete rec;
+ return 0;
+ }
+
+ //default CBTInqResultRecord values - provided in CBTInqResultRecord constructer (unlike v1)
+ //maybe overwritten later
+
+ EnableFlusher(); // Make sure we're aging entries.
+ return ref;
+ }
+
+CBTInqResultRef* CBTInquiryMgr::AddEntryWithJuiceToCache(const TInquiryLogEntry& aEntry)
+/**
+This method should be used if you are supplying cache with all valid
+baseband parameters.
+Setting the valid bit in clock offset allows us to tell btman that the clock
+offset value, and by inferrence the other juice values are valid -
+i.e. have been supplied by the HCI, as opposed to having been supplied
+as default.
+**/
+ {
+ LOG_FUNC
+ CBTInqResultRef* ref = AddEntryToCache(aEntry.iBdaddr);
+
+ if (ref)
+ {
+ CBTInqResultRecord& rec = ref->Result();
+ // Update all, and register that values come from HCI
+ rec.iJuiceFromHCIMask |= EBluetoothJuice;
+ // First fill in the base class members, common to all inquiry results
+ *static_cast<TInquiryLogEntry*>(&rec.iEntry) = aEntry;
+ // Now complete with extra data depending on the inquiry result type
+ switch(aEntry.iSpare)
+ {
+ case KInqLogEntryStandard:
+ break;
+ case KInqLogEntryWithEir:
+ {
+ const TInquiryLogEntryWithEir& entryEir = static_cast<const TInquiryLogEntryWithEir &>(aEntry);
+ // Only replace EIR if it contains something, since sometimes EIR transmission fails
+ if(entryEir.iExtendedInquiryResponse.Length() != 0)
+ {
+ rec.iEntry.iExtendedInquiryResponse = entryEir.iExtendedInquiryResponse;
+ }
+ rec.iJuiceFromHCIMask |= EBluetoothEir;
+ // Now check whether we can extract a complete or partial friendly name from the EIR and add it to the cache
+ TPtrC8 name;
+ TBool complete = ETrue;
+ rec.Codec().Set(rec.iEntry.iExtendedInquiryResponse);
+ // use EirCodec doing sanity check of the eir data
+ TInt error = rec.Codec().DoSanityCheck(rec.iEntry.iExtendedInquiryResponse);
+ if(error == KErrNone)
+ {
+ error = rec.Codec().GetDeviceName(name);
+ if(error >= KErrNone)
+ {
+ if(error == EDataPartial)
+ {
+ complete = EFalse;
+ }
+ // Do not overwrite complete names with partial ones
+ if(complete || !rec.IsNameValid() || (rec.IsNameValid() && !rec.IsNameComplete()))
+ {
+ rec.SetName(name);
+ rec.SetNameComplete(complete);
+ rec.iFlushes = 0;
+ rec.SetNameValid(ETrue);
+ rec.SetNameRefreshRequested(EFalse);
+ }
+ }
+ }
+ }
+ // Fall through
+ case KInqLogEntryWithRssi:
+ {
+ const TInquiryLogEntryWithRssi& entryRssi = static_cast<const TInquiryLogEntryWithRssi &>(aEntry);
+ rec.iEntry.iRssi = entryRssi.iRssi;
+ rec.iJuiceFromHCIMask |= EBluetoothRssi;
+ break;
+ }
+ default:
+ __ASSERT_DEBUG(EFalse, Panic(EBTInvalidInquiryLogEntry));
+ break;
+ }
+ // Mark as valid, HCI does not (necessarily) do this for you
+ rec.iEntry.iClockOffset |= KHCIClockOffsetValidBit;
+ }
+
+ return ref;
+ }
+
+CBTInqResultRef* CBTInquiryMgr::AddEntryWithCoDToCache(const TBTDevAddr& aAddr, const TUint aCoD)
+ {
+ LOG_FUNC
+ CBTInqResultRef* ref = AddEntryToCache(aAddr);
+
+ if (ref)
+ {
+ // Update all, and register that values come from HCI
+ ref->Result().iJuiceFromHCIMask |= EBluetoothCoD;
+ ref->Result().iEntry.iCoD = aCoD;
+ }
+
+ return ref;
+ }
+
+CBTInqResultRef* CBTInquiryMgr::AddEntryWithClockOffsetToCache(const TBTDevAddr& aAddr, const TBasebandTime aClockOffset)
+ {
+ LOG_FUNC
+ CBTInqResultRef* ref = AddEntryToCache(aAddr);
+
+ if (ref)
+ {
+ //update the clock offset - useful for subsequent connections
+ // mark as valid
+ ref->Result().iJuiceFromHCIMask |= EBluetoothClockOffSet;
+ ref->Result().LogEntry().iClockOffset = static_cast<TUint16>(aClockOffset | KHCIClockOffsetValidBit);
+ }
+
+ return ref;
+ }
+
+
+TInt CBTInquiryMgr::InquiryWatchdog(TAny* aPtr)
+/*
+Called second while doing enquiry.
+Considers whether to give up enquiry
+*/
+ {
+ LOG_STATIC_FUNC
+ CBTInquiryMgr& self = *static_cast<CBTInquiryMgr*>(aPtr);
+ __ASSERT_DEBUG(self.iHWState == EInquiry, Panic(EHostResolverBadHWState));
+ if(self.iResultCount == 0)
+ {
+ ++self.iInquirySilenceCount;
+ }
+
+ if(self.iNewPageRequestsPending
+ && (self.iInquirySilenceCount * KInquiryWatchdogPeriod) > self.iInquiryInteruptions)
+ {// Silence, and names are waiting. Defer the inquiry
+ self.TryToInterruptInquiryForNameLookup();
+ }
+
+ self.iResultCount = 0;
+ return 0;
+ }
+
+TInt CBTInquiryMgr::Flush(TAny* aPtr)
+ {
+ LOG_STATIC_FUNC
+ CBTInquiryMgr& self = *static_cast<CBTInquiryMgr*>(aPtr);
+ __ASSERT_DEBUG(self.iHWState != EInquiry, Panic(EHostResolverBadHWState));
+ self.DoFlush();
+ return 0;
+ }
+
+void CBTInquiryMgr::DoFlush()
+ {
+ LOG_FUNC
+ for (TInt i = 0; i < iCacheAge.Count(); i++)
+ {
+ ++iCacheAge[i].iCacheAge;
+ }
+ iCurrentResults.ReturnToFirstResult();
+
+ while (CBTInqResultRef* ref = iCurrentResults.NextResult())
+ {
+ CBTInqResultRecord& rec = ref->Result();
+ TInt age = rec.IncFlushes();
+ if(age >= KRecordStaleAge && rec.NumberOfIACsRespondedTo() > 0)
+ {
+ // device is around, but not seen for a while
+ rec.ClearIACs();
+ LOG(_L("CBTInquiryMgr::DoFlush - ClearIACs"));
+ }
+
+ if(age >= KRecordDeathAge)
+ {
+ rec.SetNameValid(EFalse);
+ LOG(_L("CBTInquiryMgr::DoFlush - age >= KRecordDeathAge setting name valid False"));
+ }
+
+ if(age >= KRecordDeathAge && rec.NumberOfIACsRespondedTo() == 0 && !rec.IsNameValid()
+ && (!rec.IsNameRequestPending() || iHRs.IsEmpty())) //Don't flush old record if name pending and HRs present
+ {// Record is dead. Delete it
+ LOG(_L("CBTInquiryMgr::DoFlush - Deleting reference"));
+ delete ref;
+ }
+ else if(rec.IsNameRefreshRequested() && iHWState == EIdle)
+ {// Try to get this name now, perhaps?
+ LOG(_L("CBTInquiryMgr::DoFlush - Try to get name straight away"));
+ rec.SetNameRefreshRequested(EFalse);
+ rec.SetNamePending(ETrue);
+ rec.iNameLookupAttempts = 0;
+ ++iPendingNameRequests;
+ LOG(_L("CBTInquiryMgr::DoFlush asking for another name lookup"));
+ DoNameLookup(EFalse);
+ }
+ }
+ if (iCurrentResults.IsEmpty())
+ {
+ iNewPageRequestsPending = EFalse;
+ iPendingNameRequests = 0;
+ iFlusher->Cancel();
+ }
+ }
+
+void CBTInquiryMgr::EnableFlusher()
+ {
+ LOG_FUNC
+ if (iFlusher->IsActive() || iHWState == EInquiry)
+ return;
+ static const TInt usec2sec = 1000000;
+ TCallBack cb(Flush, this);
+ iFlusher->Start(usec2sec / 4, // 1/4 sec for initial flush
+ KFlushTimeoutSecs * usec2sec,
+ cb);
+ }
+
+TInt CBTInquiryMgr::CacheAge(TUint aIAC) const
+ {
+ TInt ret = KMaxTInt; // If we haven't set a cache age for this IAC, return KMaxTInt
+ for (TUint i = 0; i < iCacheAge.Count(); i++)
+ {
+ if (iCacheAge[i].iIAC == aIAC)
+ {
+ ret = iCacheAge[i].iCacheAge;
+ break;
+ }
+ }
+ return ret;
+ }
+
+void CBTInquiryMgr::SetCacheAge(TUint aIAC, TInt aAge)
+ {
+ TBool found = EFalse;
+ for (TUint i = 0; i < iCacheAge.Count(); i++)
+ {
+ if (iCacheAge[i].iIAC == aIAC)
+ {
+ iCacheAge[i].iCacheAge = aAge;
+ found = ETrue;
+ break;
+ }
+ }
+ if (!found)
+ {
+ TInquiryCacheAge ageInfo;
+ ageInfo.iIAC = aIAC;
+ ageInfo.iCacheAge = aAge;
+ iCacheAge.Append(ageInfo);
+ // If we can't append, there's not a lot we can do - we'll just have
+ // to return KMaxTInt when someone asks for the age
+ }
+ }
+
+
+// -----------------------------------------------------------------------------------------
+
+void CBTInquiryMgr::StartInquiryL(TUint aIAC, TUint8 aLength, TUint8 aNumResponses)
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CInquiryCommand* cmd = CInquiryCommand::NewL(aIAC, aLength, aNumResponses);
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+void CBTInquiryMgr::CancelInquiryL()
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CInquiryCancelCommand* cmd = CInquiryCancelCommand::NewL();
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+void CBTInquiryMgr::CancelRemoteNameL(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ CRemoteNameRequestCancelCommand* cmd = CRemoteNameRequestCancelCommand::NewL(aAddr);
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+void CBTInquiryMgr::WriteInquiryModeL(TUint8 aInquiryMode)
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CWriteInquiryModeCommand* cmd = CWriteInquiryModeCommand::NewL(aInquiryMode);
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+/**
+This function should be used by the inquiry manager to retrieve remote names.
+*/
+void CBTInquiryMgr::LookupNameL(const TInquiryLogEntry& aEntry)
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CRemoteNameRequestCommand* cmd = CRemoteNameRequestCommand::NewL(aEntry.iBdaddr, aEntry.iPageScanRepetitionMode, aEntry.iPageScanMode, aEntry.iClockOffset);
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+/**
+This function should only be used when there is an established physical link (and therefore from
+CPhysicalLink only) to retrieve a remote name.
+*/
+void CBTInquiryMgr::ReadRemoteNameL(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CRemoteNameRequestCommand* cmd = CRemoteNameRequestCommand::NewL(aAddr, 0, 0, 0);
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+void CBTInquiryMgr::ReadLocalNameL()
+ {
+ LOG_FUNC
+ // Ownership of cmd transfered even if MhcqAddCommandL leaves
+ CReadLocalNameCommand* cmd = CReadLocalNameCommand::NewL();
+ CommandQueue().MhcqAddCommandL(cmd, *this);
+ }
+
+void CBTInquiryMgr::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(aCommand, Panic(EBTInquiryMgrUnmatchedEvent));
+ LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
+
+ if (aCommand->Opcode() == KInquiryOpcode)
+ {
+ // Clear any pending stuff
+ CompleteCommands(aErrorCode);
+ // this will also set the state to Idle
+ }
+ else if (aCommand->Opcode() == KWriteInquiryModeOpcode)
+ {
+ // command had error before response -- assume not supported
+ WriteInquiryModeComplete(EFalse);
+ }
+ else if (aCommand->Opcode() == KRemoteNameRequestOpcode)
+ {
+ // command had error before response -- doctor a fake response and process it to restore sanity
+ TBuf8<256> fakeEventBuffer;
+ const TBTDevAddr remNameReqAddr = static_cast<const CRemoteNameRequestCommand*>(aCommand)->BDADDR();
+ TRemoteNameReqCompleteEvent fakeRemoteNameRequestCompleteEvent(EOK, remNameReqAddr, _L8(""), fakeEventBuffer);
+ RemoteNameReqCompleteEvent(fakeRemoteNameRequestCompleteEvent);
+ // we will not change the state so a running inquiry can complete
+ }
+ else if (aCommand->Opcode() == KReadLocalNameOpcode)
+ {
+ // command had error before response -- doctor a fake response and process it to restore sanity
+ TBuf8<256> fakeEventBuffer;
+ TReadLocalNameCompleteEvent fakeLocalNameCompleteEvent(EUnspecifiedError, 0, _L8(""), fakeEventBuffer);
+ ReadLocalNameOpcode(EUnspecifiedError, fakeLocalNameCompleteEvent);
+ // this will also set the state to Idle
+ }
+
+ }
+
+// From MHCICommandQueueClient
+void CBTInquiryMgr::MhcqcCommandEventReceived(const THCIEventBase& aEvent,
+ const CHCICommandBase* aRelatedCommand)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(aRelatedCommand, Panic(EBTInquiryMgrUnmatchedEvent));
+ static_cast<void>(aRelatedCommand);
+ switch(aEvent.EventCode())
+ {
+ case ERemoteNameReqCompleteEvent:
+ RemoteNameReqCompleteEvent(aEvent);
+ break;
+
+ case ERemoteHostSupportedFeaturesNotificationEvent:
+ RemoteHostSupportedFeaturesNotificationEvent(aEvent);
+ break;
+
+ case EInquiryCompleteEvent:
+ InquiryCompleteEvent(aEvent);
+ break;
+
+ case EInquiryResultEvent:
+ InquiryResultEvent(aEvent);
+ break;
+
+ case EInquiryResultwithRSSIEvent:
+ InquiryResultWithRSSIEvent(aEvent);
+ break;
+
+ case EExtendedInquiryResultEvent:
+ ExtendedInquiryResultEvent(aEvent);
+ break;
+
+ case ECommandCompleteEvent:
+ CommandCompleteEvent(aEvent);
+ break;
+
+ case ECommandStatusEvent:
+ CommandStatusEvent(aEvent, *aRelatedCommand);
+ break;
+
+ default:
+ LOG1(_L("Warning!! Unknown Command Event Received (event code:%d)"), aEvent.EventCode());
+ __ASSERT_DEBUG(EFalse, Panic(EHCIUnknownCommandEvent));
+ break;
+ }
+ }
+
+void CBTInquiryMgr::CommandCompleteEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+ const THCICommandCompleteEvent& completeEvent = THCICommandCompleteEvent::Cast(aEvent);
+ THCIOpcode opcode = completeEvent.CommandOpcode();
+ THCIErrorCode hciErr = aEvent.ErrorCode();
+
+ switch (opcode)
+ {
+ case KWriteInquiryModeOpcode:
+ WriteInquiryModeOpcode(hciErr, aEvent);
+ break;
+
+ case KReadLocalNameOpcode:
+ ReadLocalNameOpcode(hciErr, aEvent);
+ break;
+
+ case KInquiryCancelOpcode:
+ InquiryCancelOpcode(hciErr, aEvent);
+ break;
+
+ // These commands are expected to not be handled and so can be safely ignored.
+ case KRemoteNameRequestCancelOpcode:
+ LOG1(_L("ignored Command Complete event (opcode: 0x%04x)"), opcode);
+ break;
+
+ // The commands below would most likely be used by the inquiry manager, however
+ // they currently are not used.
+ case KPeriodicInquiryModeOpcode:
+ case KExitPeriodicInquiryModeOpcode:
+ case KReadInquiryScanActivityOpcode:
+ case KWriteInquiryScanActivityOpcode:
+ case KReadInquiryScanTypeOpcode:
+ case KWriteInquiryScanTypeOpcode:
+ case KReadInquiryModeOpcode:
+ LOG1(_L("Warning!! Unhandled Command Complete Event (opcode:%d)"), opcode);
+ break;
+
+ default:
+ LOG2(_L("Error!! Unknown Command complete event! Opcode %d error code %d"), opcode, hciErr);
+ __ASSERT_DEBUG(EFalse, Panic(EHCIUnknownCommandCompleteOpcode));
+ break;
+ }
+ }
+
+void CBTInquiryMgr::CommandStatusEvent(const THCIEventBase& aEvent, const CHCICommandBase& aCommand)
+ {
+ LOG_FUNC
+
+ const TCommandStatusEvent& commandStatusEvent = TCommandStatusEvent::Cast(aEvent);
+ THCIOpcode opcode = commandStatusEvent.CommandOpcode();
+ THCIErrorCode hciErr = commandStatusEvent.ErrorCode();
+
+ __ASSERT_DEBUG(aCommand.Opcode() == opcode, Panic(EBTInquiryMgrMismatchedStatusEvent));
+
+ if (hciErr != EOK)
+ {
+ // got an error
+ // map onto the event that would have occurred: some things we will have to let the client work out
+ // e.g. they should check error and connection handle etc.
+ switch (opcode)
+ {
+ case KInquiryOpcode:
+ InquiryComplete(CHciUtil::SymbianErrorCode(hciErr), 0);
+ break;
+
+ case KRemoteNameRequestOpcode:
+ {
+ const CRemoteNameRequestCommand& remNameReq = static_cast<const CRemoteNameRequestCommand&>(aCommand);
+ TInt err = CHciUtil::SymbianErrorCode(hciErr);
+ TBTDevAddr addr = remNameReq.BDADDR();
+ TBTDeviceName8 nullDevName(KNullDesC8);
+
+ RemoteNameResult(err, addr, nullDevName);
+ iLinkMgrProtocol.PhysicalLinksMgr().RemoteName(hciErr, addr, nullDevName);
+ }
+ break;
+
+ default:
+ // Complete any other commands with an error
+ CommandCompleteEvent(aEvent);
+ break;
+ }
+ }
+ }
+
+void CBTInquiryMgr::WriteInquiryModeOpcode(THCIErrorCode aHciErr, const THCIEventBase& /*aEvent*/)
+ {
+ LOG_FUNC
+ WriteInquiryModeComplete(aHciErr == EOK);
+ }
+
+void CBTInquiryMgr::ReadLocalNameOpcode(THCIErrorCode aHciErr, const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+ if (aHciErr == EOK)
+ {
+ const TReadLocalNameCompleteEvent& readLocalNameCompleteEvent = TReadLocalNameCompleteEvent::Cast(aEvent);
+ TPtrC8 localName = readLocalNameCompleteEvent.LocalName();
+
+ GetLocalNameComplete(CHciUtil::SymbianErrorCode(aHciErr), localName);
+ iLinkMgrProtocol.UpdateLocalDeviceName(localName);
+ }
+ else
+ {
+ _LIT8(KNoName, "");
+ GetLocalNameComplete(CHciUtil::SymbianErrorCode(aHciErr), KNoName());
+ }
+ }
+
+void CBTInquiryMgr::InquiryCancelOpcode(THCIErrorCode aHciErr, const THCIEventBase& /* aEvent */)
+ {
+ LOG_FUNC
+ if (aHciErr == EOK)
+ {
+ if (iHWState == ECancellingForNewIAC)
+ {
+ SetHWState(EIdle);
+ DoInquiry();
+ }
+ }
+ // If an error comes in, it may be because the inquiry complete came in first. In that case, everything
+ // will be handled by the Inquiry complete logic
+ }
+
+// ----------------------------------------------------------------------------
+// Event processing functions
+// ----------------------------------------------------------------------------
+
+void CBTInquiryMgr::InquiryCompleteEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+ InquiryComplete(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), 0);
+ }
+
+void CBTInquiryMgr::InquiryResultEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+
+ const TInquiryResultEvent& inquiryResultEvent = TInquiryResultEvent::Cast(aEvent);
+ TInquiryLogEntry entry;
+ TUint responses = inquiryResultEvent.NumResponses();
+
+ for (TUint index = 0; index < responses; index++)
+ {
+ entry.iSpare = KInqLogEntryStandard;
+ entry.iPageScanRepetitionMode = inquiryResultEvent.PageScanRepetitionMode(index);
+ entry.iPageScanPeriodMode = inquiryResultEvent.Reserved1(index); // v1.1 spec, no meaning in v1.2
+ entry.iPageScanMode = inquiryResultEvent.Reserved2(index); // v1.1 spec, no meaning in v1.2
+ entry.iClockOffset = inquiryResultEvent.ClockOffset(index);
+ entry.iBdaddr = inquiryResultEvent.BDADDR(index);
+ entry.iCoD = inquiryResultEvent.ClassOfDevice(index);
+ InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry);
+ }
+ }
+
+void CBTInquiryMgr::InquiryResultWithRSSIEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+
+ const TInquiryResultwithRSSIEvent& inquiryResultwithRSSIEvent = TInquiryResultwithRSSIEvent::Cast(aEvent);
+ TInquiryLogEntryWithRssi entry;
+ TUint responses = inquiryResultwithRSSIEvent.NumResponses();
+
+ for (TUint index = 0; index < responses; index++)
+ {
+ entry.iSpare = KInqLogEntryWithRssi;
+ entry.iPageScanRepetitionMode = inquiryResultwithRSSIEvent.PageScanRepetitionMode(index);
+ entry.iPageScanPeriodMode = 0x00;
+ entry.iPageScanMode = 0x00;
+ entry.iClockOffset = inquiryResultwithRSSIEvent.ClockOffset(index);
+ entry.iBdaddr = inquiryResultwithRSSIEvent.BDADDR(index);
+ entry.iCoD = inquiryResultwithRSSIEvent.ClassOfDevice(index);
+ entry.iRssi = inquiryResultwithRSSIEvent.RSSI(index);
+ InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry);
+ }
+ }
+
+void CBTInquiryMgr::ExtendedInquiryResultEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+
+ const TExtendedInquiryResultEvent& extendedInquiryResultEvent = TExtendedInquiryResultEvent::Cast(aEvent);
+ TInquiryLogEntryWithEir entry;
+
+ entry.iSpare = KInqLogEntryWithEir;
+ entry.iPageScanRepetitionMode = extendedInquiryResultEvent.PageScanRepetitionMode();
+ entry.iPageScanPeriodMode = 0x00;
+ entry.iPageScanMode = 0x00;
+ entry.iClockOffset = extendedInquiryResultEvent.ClockOffset();
+ entry.iBdaddr = extendedInquiryResultEvent.BDADDR();
+ entry.iCoD = extendedInquiryResultEvent.ClassOfDevice();
+ entry.iRssi = extendedInquiryResultEvent.RSSI();
+ entry.iExtendedInquiryResponse = extendedInquiryResultEvent.ExtendedInquiryResponse();
+ InquiryResult(CHciUtil::SymbianErrorCode(aEvent.ErrorCode()), entry);
+ }
+
+void CBTInquiryMgr::RemoteNameReqCompleteEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+ const TRemoteNameReqCompleteEvent& remNameReqCompleteEvent = TRemoteNameReqCompleteEvent::Cast(aEvent);
+ THCIErrorCode err = aEvent.ErrorCode();
+ const TBTDevAddr addr = remNameReqCompleteEvent.BDADDR();
+ TPtrC8 buf = remNameReqCompleteEvent.RemoteName();
+
+ //shorten the name to the proper length so that we don't waste memory
+ TInt nullTerminator = buf.Locate(0);
+ if (nullTerminator == KErrNotFound)
+ {
+ nullTerminator = buf.Length();
+ }
+ TPtrC8 remoteName = buf.Left(nullTerminator);
+
+ // name requests have two customers...
+ // inquiry manager
+ RemoteNameResult(CHciUtil::SymbianErrorCode(err), addr, remoteName);
+ // phy manager
+ iLinkMgrProtocol.PhysicalLinksMgr().RemoteName(err, addr, remoteName);
+ }
+
+void CBTInquiryMgr::RemoteHostSupportedFeaturesNotificationEvent(const THCIEventBase& aEvent)
+ {
+ LOG_FUNC
+ const TRemoteHostSupportedFeaturesNotificationEvent& remHostSupFeatEvent = TRemoteHostSupportedFeaturesNotificationEvent::Cast(aEvent);
+ THCIErrorCode err = remHostSupFeatEvent.ErrorCode();
+ TBTDevAddr addr = remHostSupFeatEvent.BDADDR();
+ TUint64 hostSupFeats = remHostSupFeatEvent.HostSupportedFeatures();
+
+ RemoteHostSupportedFeatures(CHciUtil::SymbianErrorCode(err), addr, hostSupFeats);
+ }
+
+
+#ifdef CONNECTION_PREEMPTS_INQUIRY
+// *******************************************************************
+// ACL Connecting status subscriber
+// *******************************************************************
+/*static*/ CConnectingStatusSubscriber* CConnectingStatusSubscriber::NewL(CBTInquiryMgr& aInquiryMgr)
+ {
+ LOG_STATIC_FUNC
+ CConnectingStatusSubscriber* self = new(ELeave) CConnectingStatusSubscriber(aInquiryMgr);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+CConnectingStatusSubscriber::CConnectingStatusSubscriber(CBTInquiryMgr& aInquiryMgr)
+: CActive(CActive::EPriorityStandard), iParent(aInquiryMgr)
+ {
+ LOG_FUNC
+ CActiveScheduler::Add(this);
+ }
+
+CConnectingStatusSubscriber::~CConnectingStatusSubscriber()
+ {
+ LOG_FUNC
+ Cancel();
+ iProperty.Close();
+ }
+
+void CConnectingStatusSubscriber::DoCancel()
+ {
+ LOG_FUNC
+ iProperty.Cancel();
+ }
+
+void CConnectingStatusSubscriber::ConstructL()
+ {
+ LOG_FUNC
+ User::LeaveIfError(iProperty.Attach(KPropertyUidBluetoothCategory,
+ KPropertyKeyBluetoothGetConnectingStatus));
+ Subscribe();
+ }
+
+void CConnectingStatusSubscriber::Subscribe()
+ {
+ LOG_FUNC
+ iProperty.Subscribe(iStatus);
+ SetActive();
+ }
+
+/**
+This is looking for the connection to complete. The inquiry must be
+synchronously cancelled before the CreateConnection command is issued,
+but we can resume it when we get round to it.
+*/
+void CConnectingStatusSubscriber::RunL()
+ {
+ LOG_FUNC
+ // Subscribe to the next change of state.
+ Subscribe();
+
+ TInt isConnecting;
+ // If this device is currently connecting an ACL suspend the
+ // inquiry.
+ iProperty.Get(isConnecting);
+
+ if(!isConnecting)
+ {
+ iParent.Resume();
+ }
+ }
+#endif // CONNECTION_PREEMPTS_INQUIRY