// 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