// 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 thestack gets a new connection it tries to solicit information about theremote. 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 aremote name request if the remote device provides entries in the remotehost 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'sinterested 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 thename 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 ProtocolTBool 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 validbaseband parameters.Setting the valid bit in clock offset allows us to tell btman that the clockoffset value, and by inferrence the other juice values are valid -i.e. have been supplied by the HCI, as opposed to having been suppliedas 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; (void)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 fromCPhysicalLink 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 MHCICommandQueueClientvoid 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 besynchronously 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