bluetooth/btstack/linkmgr/physicallinksmanager.cpp
changeset 0 29b1cd4cb562
child 18 f8503e232b0c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/physicallinksmanager.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1390 @@
+// Copyright (c) 2006-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:
+// Implementation of physical links manager
+// 
+//
+
+#include <bluetooth/logger.h>
+#include "physicallinksmanager.h"
+#include "physicallinks.h"
+#include "hcifacade.h"
+#include "Basebandmodel.h"
+#include "ProxySAP.h"
+
+#include <bt_sock.h>
+#include "PhysicalLinkHelper.h"
+#include "hostresolver.h"
+
+#include <bluetooth/hci/writepagetimeoutcommand.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("plinkmgr");
+#endif
+
+// macro to dump invalid commands from hardware
+#define	RETURN_IF_NULL_CONNECTION(found) {if (!found) {LOG(_L("CPhysicalLinksManager: Bad event received - dropping")); return;}}
+
+
+CPhysicalLinksManager* CPhysicalLinksManager::NewL(CLinkMgrProtocol& aLinkMgrProtocol,
+												   CHCIFacade& aHCIFacade,
+												   CRegistrySession& aRegSess,
+												   CBTSecMan& aSecMan,
+												   CBTCodServiceMan& aCodMan)
+	{
+	CPhysicalLinksManager* s = new(ELeave) CPhysicalLinksManager(aLinkMgrProtocol,
+																 aHCIFacade,
+																 aRegSess,
+																 aSecMan,
+																 aCodMan);
+	CleanupStack::PushL(s);
+	s->ConstructL();
+	CleanupStack::Pop(s);
+	return s;
+	}
+
+CPhysicalLinksManager::CPhysicalLinksManager(CLinkMgrProtocol& aLinkMgrProtocol,
+											 CHCIFacade& aHCIFacade,
+											 CRegistrySession& aRegSess,
+											 CBTSecMan& aSecMan,
+											 CBTCodServiceMan& aCodMan)
+	: iPhysicalLinks(KBTBasebandConnectionArrayGranularity)
+	, iHCIRequestHandler(aHCIFacade)
+	, iRegSess(aRegSess)
+	, iSecMan(aSecMan)
+	, iCodMan(aCodMan)
+	, iLinkManagerProtocol(aLinkMgrProtocol)
+	, iTerminatingProxy(NULL)
+	, iTerminatingConnection(NULL)
+	, iRoleSwitchersQ(_FOFF(CRoleSwitcher, iQLink))
+	, iPageTimeoutController(aHCIFacade.CommandQController())
+	{
+	}
+
+void CPhysicalLinksManager::ConstructL()
+	{
+	iPairingsCache = CBTPairingsCache::NewL(*this, iRegSess.RegServ());
+	iBaseband = CBTBasebandModel::NewL(iLinkManagerProtocol);
+	iPrefetchMan = CBluetoothPrefetchManager::NewL();
+	}
+
+CPhysicalLinksManager::~CPhysicalLinksManager()
+	{
+	LOG(_L("sec\tCBTConnectionsManager destructing"));
+
+	iPageTimeoutController.Abort();
+
+	iPhysicalLinks.ResetAndDestroy();
+	iPhysicalLinks.Close();
+	__ASSERT_ALWAYS(iRoleSwitchersQ.IsEmpty(), Panic(ERoleSwitchersNotDeleted));
+	iListeners.Close();
+
+	delete iBaseband;
+	delete iRoleSwitcherStateFactory;
+	delete iPairingsCache;
+	delete iPrefetchMan;
+	}
+
+void CPhysicalLinksManager::RequireSlotSpace()
+/**
+	At present we put all those connections that are holdable into hold
+	To release baseband space for certain activities such
+	as device discovery
+
+	In future other operations may be performed here...
+**/
+	{
+	if (LinkManagerProtocol().IsModeSupportedLocally(EHoldMode))
+		{
+		CPhysicalLink* con = NULL;
+		// locally can support Hold mode - see if we can free up links that do
+		TInt count = iPhysicalLinks.Count();
+
+		for (TInt i=(count-1); i>=0; i--)
+			{
+			con = iPhysicalLinks[i];
+			static_cast<void>(con->RequestHold()); //ignore error - best effort
+			}
+		}
+	}
+
+TBasebandTime CPhysicalLinksManager::TryToChangePageTimeout(TBasebandTime aPageTimeout)
+	{
+	// First check the page timeout is suitable wrt. link supervision timeouts.
+	aPageTimeout = CheckPageTimeoutAgainstLSTO(aPageTimeout);
+	iPageTimeoutController.WritePageTimeout(aPageTimeout);
+	return aPageTimeout; // return the timeout that was tried (not necessarily set).
+	}
+
+CPhysicalLink& CPhysicalLinksManager::NewPhysicalLinkL(const TBTDevAddr& aAddr)
+	{
+	// create a PHY just knowing its address
+	TBTNamelessDevice device;
+	device.SetAddress(aAddr);
+	return NewPhysicalLinkL(device);
+	}
+
+CPhysicalLink& CPhysicalLinksManager::NewPhysicalLinkL(const TBTNamelessDevice& aDevice)
+	{
+	// create a PHY with a full Device - make sure it isn't us!
+	if (aDevice.Address() == iLinkManagerProtocol.LocalBTAddress())
+		{
+		// the app may be at fault - so don't panic
+		User::Leave(KErrReflexiveBluetoothLink);
+		}
+
+	// create role switch SM
+	// delete is in destructor of CPhysicalLinksManager
+	if (!iRoleSwitcherStateFactory)
+		{
+		iRoleSwitcherStateFactory = CRoleSwitcherStateFactory::NewL();
+		}
+	
+	CPhysicalLink* phy = CPhysicalLink::NewLC(*this, iRegSess, aDevice);
+	User::LeaveIfError(iPhysicalLinks.Append(phy)); //transfers ownership
+	
+	CleanupStack::Pop(phy);
+	LOG1(_L("CPhysicalLinksManager: Number of physical links %d"), iPhysicalLinks.Count());
+	return *phy;
+	}
+
+CPhysicalLink* CPhysicalLinksManager::FindPhysicalLink(const TBTDevAddr& aBDAddr) const
+	{
+	CPhysicalLink* con = NULL;
+	CPhysicalLink* found = NULL;
+	
+	
+	TInt count = iPhysicalLinks.Count();
+
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		con = iPhysicalLinks[i];
+
+		if (con->BDAddr() == aBDAddr)
+			{
+#ifndef _DEBUG
+			found = con;
+			break;
+#else
+			if (found) //make sure there's only one
+				{
+				__DEBUGGER()
+				}
+			else
+				{
+				found = con;
+				continue; // just to show we do want to!
+				}
+#endif
+			}
+		}
+	return found;
+	}
+
+void CPhysicalLinksManager::DumpPhysicalLinks()
+	{
+#ifdef __FLOG_ACTIVE
+	TInt count = iPhysicalLinks.Count();
+
+	CPhysicalLink* con = NULL;
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		con = iPhysicalLinks[i];
+		LOG(_L(" - Link: "));
+		LOGBTDEVADDR(con->BDAddr());
+		}
+#endif
+	}
+
+TBool CPhysicalLinksManager::IsAcceptPairedOnlyMode()
+	{
+	return iAcceptPairedOnlyMode;
+	}
+
+CPhysicalLink* CPhysicalLinksManager::FindPhysicalLink(THCIConnHandle aConnH) const
+/**
+	Handy for when a disconnection comes in - disconnections dont give either
+	the address or (if SCO) the 'bound' handle.
+
+	This method asks each CPhysicalLink if it knows of the handle (ACL or SCO)
+**/
+	{
+	CPhysicalLink* con = NULL;
+	CPhysicalLink* found = NULL;
+	
+	TInt count = iPhysicalLinks.Count();
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		con = iPhysicalLinks[i];
+		if (con->HasHandle(aConnH))
+			{
+#ifndef _DEBUG
+			found = con;
+			break;
+#else
+			if (found) //make sure there's only one
+				{
+				__DEBUGGER()
+				}
+			else
+				{
+				found = con;
+				continue; // just to show we do want to!
+				}
+#endif
+			}
+		}
+	return found;
+	}
+
+CPhysicalLink* CPhysicalLinksManager::FindPhysicalLink() const
+/**
+	Handy for finding a random connected PHY
+**/
+	{
+	CPhysicalLink* con = NULL;
+	CPhysicalLink* found = NULL;
+
+
+	TInt count = iPhysicalLinks.Count();
+	if(!count)
+		{
+		return NULL;
+		}
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		con = iPhysicalLinks[i];
+		if (con->Handle()!=KHCIBroadcastHandle)
+			{
+			found = con;
+			break;
+			}
+		}
+	return found;
+	}
+
+TLogicalLinkListener* CPhysicalLinksManager::FindListener(TPhysicalLinkPort aPort)
+	{
+	// go through listeners until we find a listener on the required port
+	TLogicalLinkListener* found = NULL;
+	TLogicalLinkListener* l = NULL;
+
+	TInt count = iListeners.Count();
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		l = &iListeners[i];
+		if (l->iPort == aPort)
+			{
+			found = l;
+			break;
+			}
+		}
+
+	return found;
+	}
+
+void CPhysicalLinksManager::ClearTerminatingProxy(CBTProxySAP* aProxySAP)
+	{
+	if (aProxySAP==iTerminatingProxy)
+		{
+		iTerminatingProxy=NULL;
+		}
+	}
+	
+void CPhysicalLinksManager::UpdateTerminatingProxy(CBTProxySAP* aProxySAP)
+	{
+	// If there is an outstanding terminate request for a different Proxy SAP
+	// complete it here and deal with the new request
+	if (iTerminatingProxy && (iTerminatingProxy!=aProxySAP))
+		{
+		iTerminatingProxy->TerminatePhysicalLinksComplete();
+		}
+	iTerminatingProxy=aProxySAP;
+	}
+	
+TInt CPhysicalLinksManager::TerminateAllPhysicalLinks(CBTProxySAP* aProxySAP)
+	{
+	TInt count=iPhysicalLinks.Count();
+	TInt retVal = (count==0) ? KErrNotFound : KErrNone;
+		
+	for (TInt i=0; i<count; i++)
+		{
+		// If any one of the physical links return an error then this 
+		// function needs to return an error.
+		TInt err = iPhysicalLinks[i]->Terminate(ERemoteUserEndedConnection);
+		if (err != KErrNone)
+			{
+			retVal=err;
+			}
+		}
+		
+	// aProxy==NULL	means that the calling Proxy SAP does not want to be told when the
+	// physical link has terminated
+	if ((retVal==KErrNone) || !aProxySAP)
+		{
+		UpdateTerminatingProxy(aProxySAP);
+		}
+	
+	return retVal;
+	}
+
+TInt CPhysicalLinksManager::TerminatePhysicalLink(CPhysicalLink* aConnection, CBTProxySAP* aProxySAP)
+	{
+	TInt count = iPhysicalLinks.Count();
+	TInt retVal=KErrNotFound;
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		if (iPhysicalLinks[i]==aConnection)
+			{
+			retVal=iPhysicalLinks[i]->Terminate(ERemoteUserEndedConnection);
+			break;	
+			}
+		}
+	
+	// aProxy==NULL	means that the calling Proxy SAP does not want to be told when the
+	// physical link has terminated
+	if ((retVal==KErrNone) || !aProxySAP)
+		{
+		UpdateTerminatingProxy(aProxySAP);
+		iTerminatingConnection=aConnection;
+		}
+	
+	return retVal;
+	}
+
+void CPhysicalLinksManager::UpdateRemoteDevicesDetails()
+	{
+	//Update paired device list
+	iPairingsCache->UpdateCache();
+	
+	//Update cached device info for existing PHYs
+	TInt count=iPhysicalLinks.Count();
+	for (TInt i=0; i<count; i++)
+		{
+		// try to get details of remote device
+		TRAP_IGNORE(iPhysicalLinks[i]->GetDeviceFromRegistryL());
+		// ignore error - as with other registry accessing routines
+		}
+	}
+
+void CPhysicalLinksManager::SetAcceptPairedOnlyMode(TBool aOn)
+	{
+	iAcceptPairedOnlyMode = aOn;
+	LinkManagerProtocol().SetAcceptPairedOnlyMode(aOn);
+	}
+
+
+void CPhysicalLinksManager::SetSimplePairingDebugMode(TBool aOn)
+	{
+	if(!SecMan().LocalSimplePairingMode())
+		{
+		LOG(_L("CPhysicalLinksManager: Simple pairing not supported by controller - ignoring request for debug mode"));
+		return;
+		}
+	if(aOn)
+		{
+		iSecMan.SetDebugMode(aOn);
+		}
+	}
+
+void CPhysicalLinksManager::SimplePairingDebugModeChanged(TBool aOn)
+	{
+	LinkManagerProtocol().SetSimplePairingDebugMode(aOn);
+	}
+
+void CPhysicalLinksManager::FatalError(TInt /*aErr*/)
+	{
+	// simulate a disconnection complete on all PHYs
+	TInt count=iPhysicalLinks.Count();
+
+	//The call to l.Disconnection removes an element from array iPhysicalLink, therefore invalidates 
+	//count. So it's best iterating through the array backwards because count is only used for 
+	//initialising i, and i is always valid. 
+	for (TInt i=count-1; i>=0; i--)
+		{
+		// we say remote, as this is a passive-like disconnection
+		CPhysicalLink& l = *iPhysicalLinks[i];
+		l.Disconnection(EHardwareFail, l.Handle(), EHardwareFail);
+		}
+
+	// Don't error the Listeners
+	}
+
+void CPhysicalLinksManager::NewLinkKey(const TBTDevAddr& aBDAddr, const TBTLinkKey& aLinkKey, THCILinkKeyType aLinkKeyType)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aBDAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->NewLinkKey(aBDAddr, aLinkKey, aLinkKeyType);
+	}
+
+void CPhysicalLinksManager::RemovePhysicalLink(const CPhysicalLink& aConnection)
+/**
+	A Physical Link wants us to remove our knowledge of it
+**/
+	{
+	TInt count = iPhysicalLinks.Count();
+	TInt i;
+	for (i=(count-1); i>=0; i--)
+		{
+		if (iPhysicalLinks[i]==&aConnection)
+			{
+			iPhysicalLinks.Remove(i);
+			count--;
+			break;
+			}
+		}
+		
+	if (count!=0)
+		{
+		//compress the array if needs be
+		if (i!=count)
+			{
+			iPhysicalLinks.GranularCompress();
+			}
+
+		// A physical link has been removed. Arbitrate for role switch allowed 
+		// on the remainder of the links.
+		ArbitrateAllPhysicalLinks();
+		}
+	
+	// IF we have a Terminate request from a ProxySAP AND
+	// either it was a request to terminate all connections and we have no connections left OR
+	// it was a request for a specific connection that we have just removed THEN
+	// tell the Proxy SAP that the request is complete
+	if (iTerminatingProxy && 
+		((!iTerminatingConnection && (count==0)) || 
+		 ((iTerminatingConnection==&aConnection) && (i!=-1))))
+		{
+		iTerminatingProxy->TerminatePhysicalLinksComplete();
+		iTerminatingProxy=NULL;
+		iTerminatingConnection=NULL;
+		}
+	
+	// good time to update that UI
+	LinkManagerProtocol().SetUINumPhysicalLinks(count);
+	LOG1(_L("CPhysicalLinksManager: Number of physical links %d"), count);
+	}
+
+void CPhysicalLinksManager::ArbitrateAllPhysicalLinks()
+	{
+	TInt i;
+	for (i=(iPhysicalLinks.Count()-1); i>=0; i--)
+		{
+		iPhysicalLinks[i]->Arbitrate();
+		}
+	}
+
+void CPhysicalLinksManager::SimplePairingComplete(const TBTDevAddr& aBDAddr, THCIErrorCode /*aErr*/)
+	{
+	//find the connection and pass on the request
+	CPhysicalLink* found = FindPhysicalLink(aBDAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->AuthenticationComplete(ESimplePairingPending);
+	}
+
+void CPhysicalLinksManager::PinRequest(const TBTDevAddr& aBDAddr,
+										MPINCodeResponseHandler& aRequester)
+	{
+	//find the connection and pass on the request
+	CPhysicalLink* found = FindPhysicalLink(aBDAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->PinRequest(aBDAddr, aRequester);
+	}
+
+void CPhysicalLinksManager::LinkKeyRequest(const TBTDevAddr& aBDAddr, MLinkKeyResponseHandler& aRequester)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aBDAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->LinkKeyRequest(aBDAddr, aRequester);
+	}
+
+void CPhysicalLinksManager::ReadRemoteSupportedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTFeatures& aBitMask)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->ReadRemoteSupportedFeaturesComplete(aErr, aConnH, aBitMask);
+	}
+
+void CPhysicalLinksManager::ReadRemoteExtendedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, TUint64 aBitMask, TUint8 aPageNumber, TUint8 aMaximumPageNumber)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->ReadRemoteExtendedFeaturesComplete(aErr, aConnH, aBitMask, aPageNumber, aMaximumPageNumber);
+	}
+
+
+void CPhysicalLinksManager::ReadRemoteVersionInfoComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTDevRemoteHwVersion& aVer)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->ReadRemoteVersionInfoComplete(aErr, aConnH, aVer);
+	}
+
+void CPhysicalLinksManager::PacketTypeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewPacket)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->PacketTypeChange(aErr, aConnH, aNewPacket);
+	}
+
+void CPhysicalLinksManager::LinkSupervisionTimeoutChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewTimeout)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->LinkSupervisionTimeoutChange(aErr, aConnH, aNewTimeout);
+	}
+
+TBasebandTime CPhysicalLinksManager::CheckPageTimeoutAgainstLSTO(TBasebandTime aPageTimeout)
+	{
+	// Calculate the page timeout:
+	//   Iterate through all physical links, and take the minimum
+	//   LSTO of them all. This is maximum possible page timeout value
+	TInt connections = iPhysicalLinks.Count();
+	TInt newPageTimeout = aPageTimeout;
+	for(TInt i = 0; i < connections; i++)
+		{
+		// Reduce pageTimeout if greater than LSTO for any physical link
+		// LSTO could be zero if no LSTO event received; ignore zero values
+		TUint lsto = iPhysicalLinks[i]->LSTO();
+		if (lsto > 0 && newPageTimeout > lsto)
+			{
+			newPageTimeout = lsto;
+			}
+		}
+	
+	// The pageTimeout has been changed. Ensure it is 5% less
+	// than the minimum LSTO that it has already been set to.
+	if (newPageTimeout != aPageTimeout)
+		{
+		const TReal32 KNinetyFivePercentMultiplier = 0.95;
+		newPageTimeout = newPageTimeout * KNinetyFivePercentMultiplier; // Reduce by 5%
+		}
+	return newPageTimeout;
+	}
+
+void CPhysicalLinksManager::MaxSlotsChange(THCIConnHandle aConnH, TUint8 aSlots)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->MaxSlotsChange(aConnH, aSlots);
+	}
+
+void CPhysicalLinksManager::ModeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBTLinkMode aMode, TBasebandTime aInterval)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->ModeChange(aErr, aConnH, aMode, aInterval);
+	}
+
+void CPhysicalLinksManager::RoleChange(THCIErrorCode aErr, const TBTDevAddr& aAddr, TBTBasebandRole aRole)
+	{
+	// interesting that this is an address, not a Conn handle! Good old H:1.
+	CPhysicalLink* found = FindPhysicalLink(aAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->RoleChange(aErr, aAddr, aRole);
+	}
+
+void CPhysicalLinksManager::WriteLinkPolicySettingsCompleteEvent(THCIErrorCode aErr, THCIConnHandle aConnH)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->WriteLinkPolicySettingsCompleteEvent(aErr, aConnH);
+	}
+
+void CPhysicalLinksManager::ClockOffset(THCIErrorCode aErr, THCIConnHandle aConnH, TBasebandTime aClockOffset)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->ClockOffset(aErr, aConnH, aClockOffset);
+	}
+
+void CPhysicalLinksManager::RemoteName(THCIErrorCode aErr, const TBTDevAddr& aAddr, const TBTDeviceName8& aName)
+	{
+	CPhysicalLink* found = FindPhysicalLink(aAddr);
+	RETURN_IF_NULL_CONNECTION(found);
+	found->RemoteName(aErr, aAddr, aName);
+	}
+
+void CPhysicalLinksManager::ConnectionComplete(THCIErrorCode aErr, const TBTConnect& aConn)
+	{
+	// we at this stage demux ACL and SCO
+	CPhysicalLink* phy = FindPhysicalLink(aConn.iBdaddr);
+
+	if (!phy)
+		{
+		// to be safe we should cancel all outstanding connections of correct type
+		// since some HW leaves the address blank when errroring a connect
+		CancelPendingConnections(aErr, aConn);
+		// disconnect this weirdo link if it came up
+		if (aErr==EOK)
+			{
+			TRAP_IGNORE(HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+			// if error just have to wait for a timeout
+			}
+		}
+	else
+		{
+		phy->ConnectionComplete(aErr, aConn);
+		// good time to update that UI
+		LinkManagerProtocol().SetUINumPhysicalLinks(iPhysicalLinks.Count());
+		}
+	}
+
+void CPhysicalLinksManager::SynchronousConnectionComplete(THCIErrorCode aErr,
+													const TBTConnect& aConn,
+													const TBTSyncConnectOpts& aSyncOpts)
+	{
+	__ASSERT_DEBUG(((aConn.iLinkType == ESCOLink) || (aConn.iLinkType == EeSCOLink)), Panic(EBTNonSyncConnectInSyncConnectFunc));
+
+	if((aConn.iLinkType == ESCOLink) || (aConn.iLinkType == EeSCOLink))
+		{
+		CPhysicalLink* phy = FindPhysicalLink(aConn.iBdaddr);
+
+		if (!phy)
+			{
+			// to be safe we should cancel all outstanding connections of correct type
+			// since some HW leaves the address blank when errroring a connect
+			CancelPendingConnections(aErr, aConn);
+			// disconnect this weirdo link if it came up
+			if (aErr==EOK)
+				{
+				TRAP_IGNORE(HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+				// if error just have to wait for a timeout
+				}
+			}
+		else
+			{
+			phy->SynchronousConnectionComplete(aErr, aConn, aSyncOpts);
+			// good time to update that UI
+			LinkManagerProtocol().SetUINumPhysicalLinks(iPhysicalLinks.Count());
+			}
+		}
+	else
+		{
+		TRAP_IGNORE(HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
+		}
+	}
+
+void CPhysicalLinksManager::MbpcoDeferredDecisionResolved(TInt aError)
+	{
+	// See if any connection requests are pending,
+	// having been held up whilst the stack was
+	// being updated with all paired devices
+	// in the registry.
+	for(TInt i = iPhysicalLinks.Count() - 1; i>=0; i--)
+		{
+		if(iPhysicalLinks[i]->IsConnectionRequestPending())
+			{
+			iPhysicalLinks[i]->PendingConnectionRequest(aError); //try now we have a list of paired devices
+			}
+		}
+	}
+
+TInt CPhysicalLinksManager::AddListener(MLogicalLink& aLogicalLink, TPhysicalLinkPort aPort)
+	{
+	TLogicalLinkListener l;
+	l.iObserver = &aLogicalLink;
+	l.iPort = aPort;
+
+	TInt retVal = KErrNone;
+
+	// only allowed one listener on either port at moment
+	for (TInt i=0; i<iListeners.Count(); i++)
+		{
+		if (iListeners[i].iPort == aPort)
+			{
+			retVal = KErrInUse;
+			}
+		}
+
+	if (retVal == KErrNone)
+		{
+		retVal = iListeners.Append(l);
+		if (retVal == KErrNone)
+			{
+			retVal = LinkManagerProtocol().IncrementListeners();
+			}
+		}
+	
+	return retVal;
+	}
+
+/*static*/ TBool CPhysicalLinksManager::ListenerMatch(const TLogicalLinkListener& aA,
+										   const TLogicalLinkListener& aB)
+	{
+	return (aA.iObserver == aB.iObserver);
+	}
+
+void CPhysicalLinksManager::RemoveListener(MLogicalLink& aLogicalLink)
+	{
+	__ASSERT_DEBUG(iListeners.Count(), Panic(EBTConnectionMgrCountBelowZero));
+
+	TLogicalLinkListener key;
+	key.iObserver = &aLogicalLink;
+
+	TIdentityRelation<TLogicalLinkListener> relation(ListenerMatch);
+
+	TInt result = iListeners.Find(key, relation);
+	__ASSERT_DEBUG(result!=KErrNotFound, Panic(EBTConnectionMgrBadListener));
+
+	// remove from listener queue
+	iListeners.Remove(result);
+	// and tell protocol we have one fewer
+	LinkManagerProtocol().DecrementListeners();
+	// ignore err
+	}
+
+void CPhysicalLinksManager::CancelPendingConnections(THCIErrorCode aErr, const TBTConnect& aConn)
+/**
+	When we cannot work out who to notify, we regrettably have to cancel all
+**/
+	{
+	for (TInt index = 0; index < iPhysicalLinks.Count(); index++)
+		{
+		CPhysicalLink& l = *iPhysicalLinks[index];
+		if (   (aConn.iConnH && l.HasHandle(aConn.iConnH))
+			|| ((aConn.iLinkType == EACLLink) && l.ACLConnectPending())
+			|| ((aConn.iLinkType != EACLLink) && l.SyncConnectPending())
+			)
+			{
+			l.ConnectionComplete(aErr, aConn);
+			}
+		}
+	}
+
+void CPhysicalLinksManager::ConnectionRequest(const TBTConnect& aConn)
+	{
+	// we haven't got much to go on to find a Listener
+	// we don't support directed listening on address
+	// so just go on link type
+	// this isn't too bad as Bluetooth only has one ACL listener at the moment: L2CAP
+	LOG1(_L("Connection request received, link type %d"), aConn.iLinkType);
+	
+	switch (aConn.iLinkType)
+		{
+		case EACLLink:
+			{
+			// this also (see BT1.2) implies that a new PHY is coming up
+
+			// Check that either there exist listeners or the protocol knows
+			// that it should stop listening (even if the message has evidently
+			// not yet reached the controller).
+			__ASSERT_DEBUG(iListeners.Count() || !iLinkManagerProtocol.IsListening(), Panic(EBTConnectionRequestWithNoListeners));
+			if (iListeners.Count())
+				{
+				TRAP_IGNORE(DoConnectionRequestL(aConn));
+				}
+			else
+				{
+				// should never reach here
+				// leave to timeout - not required
+				}
+			}
+			// ignore err for timeout
+			break;
+		case ESCOLink:
+		case EeSCOLink:
+			{
+			// should by a non-listening PHY already in place!
+			CPhysicalLink* c = FindPhysicalLink(aConn.iBdaddr);
+			ASSERT_DEBUG(c);
+			c->ConnectionRequest(aConn);
+			}
+		}
+	}
+
+
+void CPhysicalLinksManager::DoConnectionRequestL(const TBTConnect& aConn)
+	{
+	// Only called for ACL links
+	// ASSERT aConn.iLinkType == EACLLink
+	
+	// First check that this is not a request for a duplicate address, some hardware
+	// can be tediously erratic.
+	CPhysicalLink* reqAddr = FindPhysicalLink(aConn.iBdaddr);
+	if (!reqAddr)
+		{
+		CPhysicalLink& c = NewPhysicalLinkL(aConn.iBdaddr);
+		// introduce the first listener to the PHY
+		// IF WE SUPPORT connections distinguished on aConn then we can find the correct listener 
+		// now tell the (new) PHY
+		c.ConnectionRequest(aConn);
+		}
+	else
+		{
+		// reject incoming connection
+		LOG(_L("CPhysicalLinksManager: Duplicate address received - reject"));
+		TRAP_IGNORE(HCIFacade().RejectConnectionRequestL(aConn, EHostSecurityRejection));
+		}
+	}
+
+void CPhysicalLinksManager::Disconnection(THCIErrorCode aErr, THCIConnHandle aConnH, THCIErrorCode aResult)
+	{
+	// notify the correct connection
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	LOG1(_L("Links manager: disconnection, phys. link 0x%08x"), con);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->Disconnection(aErr, aConnH, aResult);
+	}
+
+void CPhysicalLinksManager::CompletedPackets(THCIConnHandle aConnH, TUint16 aNumPackets)
+	{
+	// forward to the connections - they can possibly unblock their sockets
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->CompletedPackets(aConnH, aNumPackets);
+	
+	//now unblock all connections to prevent any deadlock
+	TInt connections = iPhysicalLinks.Count();
+	for(TInt i = 0; i < connections;i++)
+		{
+		CPhysicalLink* other_con = iPhysicalLinks[i];
+		if(other_con != con)
+			{
+			other_con->TryToSend();
+			}
+		}
+	}
+
+void CPhysicalLinksManager::AuthenticationComplete(THCIErrorCode aErr, THCIConnHandle aConnH)
+	{
+	// tell the anonymous notifiers?  could be useful for updating UI?
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->AuthenticationComplete(aErr, aConnH);
+	}
+
+void CPhysicalLinksManager::EncryptionChange(THCIErrorCode aErr, THCIConnHandle aConnH,TBool aEncrypted)
+	{
+	// tell the anonymous notifiers?  could be useful for updating UI?
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->EncryptionChange(aErr, aConnH, aEncrypted);
+	}
+
+void CPhysicalLinksManager::ACLDataReceived(THCIConnHandle aConnH, TUint8 aFlag, const TDesC8& aData)
+	{
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->ACLDataReceived(aConnH, aFlag, aData);
+	}
+
+void CPhysicalLinksManager::SCODataReceived(THCIConnHandle aConnH, const TDesC8& aData)
+	{
+	CPhysicalLink* con = FindPhysicalLink(aConnH);
+	RETURN_IF_NULL_CONNECTION(con);
+	con->SCODataReceived(aConnH, aData);
+	}
+
+TInt CPhysicalLinksManager::ChangeMode(TBTLinkMode aMode, THCIConnHandle aConnHandle)
+	{
+	return iHCIRequestHandler.ChangeMode(aMode, aConnHandle);
+	}
+
+TInt CPhysicalLinksManager::ExitMode(TBTLinkMode aMode, THCIConnHandle aConnHandle)
+	{
+	return iHCIRequestHandler.ExitMode(aMode, aConnHandle);
+	}
+
+TInt CPhysicalLinksManager::ChangeRole(TBTBasebandRole aRole, CPhysicalLink& aPhysicalLink)
+	{
+	TRAPD(err, iHCIRequestHandler.ChangeRoleL(aRole, aPhysicalLink.BDAddr()));
+	return err;
+	}
+	
+TInt CPhysicalLinksManager::Encrypt(TBool aEnable, CPhysicalLink& aPhysicalLink)
+	{
+	TRAPD(err, iHCIRequestHandler.SetEncryptL( aPhysicalLink.Handle(), aEnable));
+	return err;
+	}
+	
+
+TBool CPhysicalLinksManager::ActiveConnectRoleSwitchAllowed() const
+	{
+	// An active connection role change will be allowed in all cases,
+	// except when there is an existing master connection from this device.
+	if(iPhysicalLinks.Count() > 1)
+		{
+		if(iPhysicalLinks[0]->Role() == EMaster)
+			{
+			return EFalse;
+			}
+		}
+	return ETrue;
+	}
+
+TBool CPhysicalLinksManager::PassiveConnectBecomeMaster() const
+	{
+	// If this device is currently working as a master for an existing
+	// connection, request a role switch to master.
+	return (iPhysicalLinks.Count() > 1 && iPhysicalLinks[0]->Role() == EMaster);
+	}
+
+TBool CPhysicalLinksManager::RoleSwitchAllowed() const
+	{
+	// By default the stack will not try and prevent this device scatterneting.
+	// If the BT hardware can't gracefully cope with requests to scatternet (i.e.,
+	// disconnects existing phy's if a new scatternet connection is attempted) then
+	// this macro should be defined.
+#ifdef HARDWARE_DOES_NOT_SUPPORT_SCATTERNET_OPERATION
+	return (iPhysicalLinks.Count() <= 1);
+#else
+	return ETrue;
+#endif	
+	}
+
+
+void CPhysicalLinksManager::EnumeratePhysicalLinks(TDes8& aPackagedArrayBuffer)
+/**
+	This method fills the memory passed from the client with a number of TBTDevAddrs.
+	Useful for control panel apps that need to see what links are present
+
+	Because the array passed to us here may be smaller than the number of active links
+	at this point, we shall return as much as possible in the array.
+
+	Also if the array has enough space for more addresses than we currently have, the
+	descriptor size will be changed to reflect reality.
+*/
+	{
+	TInt connections = Min(iPhysicalLinks.Count(),
+							aPackagedArrayBuffer.MaxLength()/sizeof(TBTDevAddr));
+	aPackagedArrayBuffer.Zero(); // start from the err.. start and then append
+
+	for(TInt i=0; i<connections; i++)
+		{
+		if (iPhysicalLinks[i]->IsConnected())
+			{
+			TBTDevAddr theDevAddr=iPhysicalLinks[i]->BDAddr();
+
+			TPckg<TBTDevAddr> pckg(theDevAddr);
+			aPackagedArrayBuffer.Append(pckg);
+			}
+		}
+	}
+
+void CPhysicalLinksManager::RoleChangeRejectedByHW(THCIErrorCode aErr)
+	{	
+	// we can notify the users about Role Change failure on HW with BTBasebandEvent error	
+	// Make sure the Q is not empty before trying to access First()
+	if(!iRoleSwitchersQ.IsEmpty())
+    	{
+    	RoleChange(aErr, iRoleSwitchersQ.First()->BDAddr(), iRoleSwitchersQ.First()->RequestedRole());
+		}
+	
+	}
+ 
+ void CPhysicalLinksManager::AddRoleSwitcher(CRoleSwitcher& aRoleSwitcher)
+ 	{
+ 	iRoleSwitchersQ.AddLast( aRoleSwitcher ); // ownership still remains in CPhysicalLink
+ 
+ 	if (iRoleSwitchersQ.IsFirst(&aRoleSwitcher))
+ 		{
+ 		// this the only role request, it's safe to kick off the state machine
+ 		// first suspend host resolver
+ 		LinkManagerProtocol().InquiryMgr().Suspend();
+ 		aRoleSwitcher.Start();
+ 		}
+ 	}
+ 
+ void CPhysicalLinksManager::RemoveRoleSwitcher(CRoleSwitcher& aRoleSwitcher)
+ 	{
+ 	TBool startNextRoleSwitcher = EFalse;
+ 	
+ 	if (iRoleSwitchersQ.IsFirst(&aRoleSwitcher))
+ 		{
+ 		startNextRoleSwitcher = ETrue;
+ 		}
+ 
+	if (!iRoleSwitchersQ.IsEmpty())
+		{
+		iRoleSwitchersQ.Remove(aRoleSwitcher);
+		}
+ 	
+ 	if (startNextRoleSwitcher && !iRoleSwitchersQ.IsEmpty())
+ 		{
+ 		iRoleSwitchersQ.First()->Start();
+ 		}
+ 	else 
+ 		{
+ 		// resume host resolver
+ 		LinkManagerProtocol().InquiryMgr().Resume();
+ 		}
+ 	}
+
+TInt CPhysicalLinksManager::GetConnectionHandles(RHCIConnHandleArray& aConnectionHandles, TLinkType aLinkType) const
+	{
+	aConnectionHandles.Reset(); // sub-optimal
+
+	const TInt phycount(iPhysicalLinks.Count());
+	if (phycount == 0)
+		{
+		return KErrNone;
+		}
+
+	TInt err(aConnectionHandles.Reserve(phycount));
+
+	if (err != KErrNone)
+		{
+		return err;
+		}
+
+	for (TInt i(0); i < phycount; ++i)
+		{
+		const CPhysicalLink* const phy(iPhysicalLinks[i]);
+		if (!phy)
+			{
+			return KErrNotFound;
+			}
+
+		// append to aConnectionHandles
+		err = phy->GetConnectionHandles(aConnectionHandles, aLinkType);
+
+		if (err != KErrNone)
+			{
+			return err;
+			}
+		}
+
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType) const
+	{
+	aConnectionHandles = 0;
+	
+	const TInt phycount(iPhysicalLinks.Count());
+	if (phycount == 0)
+		{
+		return KErrNone;
+		}
+
+	for (TInt i(0); i < phycount; ++i)
+		{
+		const CPhysicalLink* const phy(iPhysicalLinks[i]);
+		if (!phy)
+			{
+			return KErrNotFound;
+			}
+
+		TInt count(0);
+		// append to aConnectionHandles
+		TInt err(phy->GetNumPendingHandles(count, aLinkType));
+
+		if (err != KErrNone)
+			{
+			return err;
+			}
+
+		aConnectionHandles += count;
+		}
+
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetConnectionHandles(RHCIConnHandleArray& aConnectionHandles, TLinkType aLinkType, const TBTDevAddr& aBDAddr) const
+	{
+	aConnectionHandles.Reset(); // sub-optimal
+
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aConnectionHandle.
+		return KErrNotFound;
+		}
+
+	return phy->GetConnectionHandles(aConnectionHandles,
+									 aLinkType);
+	}
+
+TInt CPhysicalLinksManager::GetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType, const TBTDevAddr& aBDAddr) const
+	{
+	aConnectionHandles = 0;
+
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aConnectionHandle.
+		return KErrNotFound;
+		}
+
+	return phy->GetNumPendingHandles(aConnectionHandles, aLinkType);
+	}
+
+TInt CPhysicalLinksManager::GetRemoteAddress(TBTDevAddr& aBDAddr, THCIConnHandle aConnectionHandle) const
+	{
+	const CPhysicalLink* const phy(FindPhysicalLink(aConnectionHandle));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aBDAddr.
+		return KErrNotFound;
+		}
+
+	aBDAddr = phy->BDAddr();
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetRemoteDeviceClass(TBTDeviceClass& aDeviceClass, const TBTDevAddr& aBDAddr) const
+	{
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aBasebandLinkState.
+		return KErrNotFound;
+		}
+
+	aDeviceClass = phy->DeviceClass();
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetRemoteSupportedFeatures(TBTFeatures& aRemoteSupportedFeatures, const TBTDevAddr& aBDAddr) const
+	{
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aRemoteSupportedFeatures.
+		return KErrNotFound;
+		}
+
+	aRemoteSupportedFeatures = phy->RemoteFeatures();
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetLinkPolicySettings(TLinkPolicy& aLinkPolicySettings, const TBTDevAddr& aBDAddr) const
+	{
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aLinkPolicySettings.
+		return KErrNotFound;
+		}
+
+	aLinkPolicySettings = phy->LinkPolicy();
+	return KErrNone;
+	}
+
+TInt CPhysicalLinksManager::GetBasebandLinkState(TBTBasebandLinkState& aBasebandLinkState, const TBTDevAddr& aBDAddr) const
+	{
+	const CPhysicalLink* const phy(FindPhysicalLink(aBDAddr));
+
+	if (!phy)
+		{
+		// In the event of an error the client should not inspect
+		// aBasebandLinkState.
+		return KErrNotFound;
+		}
+
+	aBasebandLinkState = phy->LinkState();
+	return KErrNone;
+	}
+
+
+//
+// TPageTimeoutController
+//
+
+TPageTimeoutController::TPageTimeoutController(MHCICommandQueue& aCommandQueue)
+	: iCommandQueue(aCommandQueue)
+	, iOutstandingCommands(0)
+	{
+	}
+
+void TPageTimeoutController::Abort()
+	{
+	iCommandQueue.MhcqRemoveAllCommands(*this);
+	}
+
+void TPageTimeoutController::WritePageTimeout(TBasebandTime aPageTimeout)
+	{
+	// Setting the page-timeout is typically best effort, hence this non-erroring
+	// API.
+	TRAP_IGNORE(WritePageTimeoutL(aPageTimeout));
+	}
+
+void TPageTimeoutController::MhcqcCommandEventReceived(const THCIEventBase& /*aEvent*/, const CHCICommandBase* /*aRelatedCommand*/)
+	{
+	// Nothing else needs to be done.
+	--iOutstandingCommands;
+	}
+
+void TPageTimeoutController::MhcqcCommandErrored(TInt /*aErrorCode*/, const CHCICommandBase* /*aCommand*/)
+	{
+	// This is a best effort service so errors are just dropped.
+	--iOutstandingCommands;
+	}
+
+void TPageTimeoutController::WritePageTimeoutL(TBasebandTime aPageTimeout)
+	{
+	if(iOutstandingCommands)
+		{
+		// best effort removal of any existing command on the queue.
+		static_cast<void>(iCommandQueue.MhcqRemoveCommand(iLastCommandId, *this));
+		}
+	CWritePageTimeoutCommand* cmd = CWritePageTimeoutCommand::NewL(aPageTimeout);
+	// ownership of cmd is taken by command queue.
+	iLastCommandId = iCommandQueue.MhcqAddCommandL(cmd, *this); // "leak" any previous command id.
+	++iOutstandingCommands;
+	}
+
+
+
+CBluetoothPrefetchManager* CBluetoothPrefetchManager::NewL()
+	{
+	CBluetoothPrefetchManager* self = new(ELeave) CBluetoothPrefetchManager;
+	return self;
+	}
+
+CBluetoothPrefetchManager::CBluetoothPrefetchManager()
+	{
+	}
+
+CBluetoothPrefetchManager::~CBluetoothPrefetchManager()
+	{
+	while(iPinRequesters.Count() > 0)
+		{
+		iPinRequesters[0].CompleteRequest(KErrCancel);
+		iPinRequesters.Remove(0);
+		}
+	iPinRequesters.Close();
+	}
+
+TBool CBluetoothPrefetchManager::CompareAddressInRequest(const TBTDevAddr* aDevAddr, const RPinRequest& aRequest)
+	{
+	return aDevAddr && *aDevAddr == aRequest.iAddr;
+	}
+
+TBool CBluetoothPrefetchManager::CompareAddressInStore(const TBTDevAddr* aDevAddr, const TPrefetchedPin& aPin)
+	{
+	return aDevAddr && *aDevAddr == aPin.iAddr;
+	}
+
+TInt CBluetoothPrefetchManager::HandleOverPinRequester(const TBTDevAddr& aAddr, CBTPinRequester* aPinRequester)
+	{
+	RPinRequest req;
+	TInt err = req.Create(aAddr, aPinRequester);
+	if(err == KErrNone)
+		{
+		err = iPinRequesters.Append(req);
+		}
+	if(err == KErrNone)
+		{
+		aPinRequester->UpdateHandler(*this);
+		}
+	return err;
+	}
+
+TInt CBluetoothPrefetchManager::RegisterForPrefetching(const TBTDevAddr& aAddr, MBluetoothPrefetchNotifier& aNotifier)
+	{
+	TInt ix = iPinRequesters.Find(aAddr, CompareAddressInRequest);
+	if (ix < 0)
+		{
+		return ix;
+		}
+	iPinRequesters[ix].AddNotifier(aNotifier);
+	return KErrNone;
+	}
+
+TBool CBluetoothPrefetchManager::IsPrefetchAvailable(const TBTDevAddr& aAddr, TBTPinCode& aPinCode)
+	{
+	TInt ix = iPrefetchedPins.Find(aAddr, CompareAddressInStore);
+	if (ix < 0)
+		{
+		return EFalse;
+		}
+	aPinCode.Copy(iPrefetchedPins[ix].iPin);
+	iPrefetchedPins.Remove(ix);
+	return ETrue;
+	}
+
+TInt CBluetoothPrefetchManager::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin) const
+	{
+	TInt ix = iPinRequesters.Find(aDevAddr, CompareAddressInRequest);
+	__ASSERT_DEBUG(ix >= 0, Panic(EBTPrefetchManagerReplyForNoRequest));
+
+	// Always check if someone is prefetching before storing a prefetch result.
+	TInt result = EBTSecManAccessDenied;
+	if(iPinRequesters[ix].NotifiersWaiting())
+		{
+		TPrefetchedPin pin;
+		pin.iAddr = aDevAddr;
+		pin.iPin.Copy(aPin);
+		result = iPrefetchedPins.Append(pin);
+		}
+	iPinRequesters[ix].CompleteRequest(result);
+	iPinRequesters.Remove(ix);
+	return KErrNone;
+	}
+
+TInt CBluetoothPrefetchManager::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr) const
+	{
+	TInt ix = iPinRequesters.Find(aDevAddr, CompareAddressInRequest);
+	__ASSERT_DEBUG(ix >= 0, Panic(EBTPrefetchManagerReplyForNoRequest));
+	iPinRequesters[ix].CompleteRequest(EBTSecManAccessDenied);
+	iPinRequesters.Remove(ix);
+	return KErrNone;
+	}
+
+CBluetoothPrefetchManager::RPinRequest::RPinRequest()
+	: iNotifiers(NULL)
+	{
+	}
+
+TInt CBluetoothPrefetchManager::RPinRequest::Create(const TBTDevAddr& aAddr, CBTPinRequester* aPinRequester)
+	{
+	iAddr = aAddr;
+	iPinRequester = aPinRequester;
+	iNotifiers = new TDblQue<TPrefetchNotifierQLink>();
+	return iNotifiers ? KErrNone : KErrNoMemory;
+	}
+
+void CBluetoothPrefetchManager::RPinRequest::AddNotifier(MBluetoothPrefetchNotifier& aNotifier)
+	{
+	iNotifiers->AddLast(aNotifier.MbpnQueLink());
+	}
+
+TBool CBluetoothPrefetchManager::RPinRequest::NotifiersWaiting() const
+	{
+	return !iNotifiers->IsEmpty();
+	}
+
+void CBluetoothPrefetchManager::RPinRequest::CompleteRequest(TInt aError)
+	{
+	while(!iNotifiers->IsEmpty())
+		{
+		MBluetoothPrefetchNotifier* notifier = iNotifiers->First()->Item();
+		ASSERT_DEBUG(notifier);
+		notifier->MbpnQueLink().Deque();
+		notifier->MbpnPrefetchComplete(aError);
+		}
+	delete iPinRequester;
+	iPinRequester = NULL;
+	delete iNotifiers;
+	iNotifiers = NULL;
+	}
+
+