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

#include <bluetooth/logger.h>
#include "physicallinksmanager.h"
#include "physicallinks.h"
#include "AclDataQController.h"
#include "ACLSAP.h"
#include "SCOSAP.h"
#include "ProxySAP.h"
#include "linkconsts.h"
#include "hcifacade.h"
#include "hostresolver.h"
#include "PhysicalLinkHelper.h"
#include "pairingscache.h"
#include "oobdata.h"
#include "pairingserver.h"

#include <bt_sock.h>

#include <bluetooth/hci/commandstatusevent.h>
#include <bluetooth/hci/sniffmodecommand.h>
#include <bluetooth/hci/exitsniffmodecommand.h>
#include <bluetooth/hci/holdmodecommand.h>
#include <bluetooth/hci/parkmodecommand.h>
#include <bluetooth/hci/exitparkmodecommand.h>
#include <bluetooth/hci/modechangeevent.h>
#include <bluetooth/hci/disconnectcommand.h>
#include <bluetooth/hci/disconnectioncompleteevent.h>
#include <bluetooth/hci/hciconsts.h>

#include <bluetooth/hci/linkkeyrequestreplycommand.h>
#include <bluetooth/hci/linkkeyrequestreplynegativecommand.h>
#include <btextnotifierspartner.h>

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
#endif

#ifdef _DEBUG
PANICCATEGORY("plink");
#endif

static const THCIErrorCode KDefaultRejectReason = EHostSecurityRejection; // see spec Error Codes

#ifdef _DEBUG
#define __CHECK_CONNECTION_HANDLE(aHandle) __ASSERT_DEBUG(aHandle==iHandle, Panic(EBTLinkMgrConnectionEventInWrongSAP));
#else
#define __CHECK_CONNECTION_HANDLE(aHandle) aHandle=aHandle; // to suppress warnings
#endif

CPhysicalLink* CPhysicalLink::NewLC(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
	{
	LOG_STATIC_FUNC
	CPhysicalLink* s = new(ELeave) CPhysicalLink(aConnectionMan, aRegSess, aDevice);
	CleanupStack::PushL(s);
	s->ConstructL();
	return s;
	}

CPhysicalLink* CPhysicalLink::NewL(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
	{
	LOG_STATIC_FUNC
	CPhysicalLink* s = NewLC(aConnectionMan, aRegSess, aDevice);
	CleanupStack::Pop(s);
	return s;
	}

CPhysicalLink::CPhysicalLink(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice)
	: iLinksMan(aConnectionMan)
	, iRegSess(aRegSess)
	, iDevice(aDevice)
	, iHandle(KHCIBroadcastHandle)
	, iRemoteFeatures(KInvalidRemoteFeatures)
	, iDeviceResult(KDeviceNotObtained)
	, iRegistryHelpers(_FOFF(CBTRegistryHelperBase,iLink))
	, iProxySAPs(_FOFF(CBTProxySAP, iQueueLink))
	, iOverrideParkRequests(EFalse)
	, iOverrideLPMRequests(EFalse)
	, iLPMOverrideTimerQueued(EFalse)
	, iConnectionPacketTypeChanged(EFalse)
	, iLowPowModeCtrl(*this, iLinksMan.HCIFacade().CommandQController())
	, iDisconnectCtrl(*this, iLinksMan.HCIFacade().CommandQController())
	, iAuthenticationCtrl(*this, iLinksMan.HCIFacade().CommandQController())
	, iLSTO(0)
	, iAutoKeyRefreshQue(_FOFF(XAutoKeyRefreshToken, iQueLink))
	, iRemoteDeviceRecordedByStack(ENull)
	{
	LOG_FUNC
	// don't initialise physical link policy just yet - wait until connection completes
	// by then we'll have best information to go on
	LOG1(_L("New CPhysicalLink [0x%08x]"), this);
	}

CPhysicalLink::~CPhysicalLink()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iACLLogicalLinks.Count()==0, Panic(EBTACLLogicalLinkBadDebind));
	__ASSERT_DEBUG(iSyncLogicalLink == NULL, Panic(EBTSCOLogicalLinkBadDebind));

	LOG1(_L("CPhysicalLink Destructing this = 0x%08x"), this);

	// tell ConnectionsManager...
	iLinksMan.RemovePhysicalLink(*this);
	iACLLogicalLinks.Close();

	RemoveIdleTimer();
	if (iLPMOverrideTimerQueued)
		{
		BTSocketTimer::Remove(iOverrideLPMTimerEntry);
		iLPMOverrideTimerQueued = EFalse;
		}

	LOG(_L("sec\tClosing subscribers..."))

	LOG(_L("sec\tClosing helpers..."))
	TSglQueIter<CBTRegistryHelperBase> iter(iRegistryHelpers);
	CBTRegistryHelperBase* helper;
	// Detach from any Registry Helpers for which this object
	// is the parent.
	while(iter)
		{
		helper = iter++;
		helper->DetachParent();
		}

	iLowPowModeCtrl.Abort();
	iDisconnectCtrl.Abort();
	iAuthenticationCtrl.Abort();

	delete iPhysicalLinkMetrics;
	delete iPinRequester;
	delete iNumericComparator;
	delete iPasskeyEntry;
	delete iArbitrationDelay;
	delete iRoleSwitchCompleteCallBack;
	delete iEncryptionEnforcer;

	DeleteRoleSwitcher();
	}

void CPhysicalLink::ConstructL()
	{
	LOG_FUNC
	LOG1(_L("CPhysicalLink::ConstructL() this = 0x%08x"), this);
	GetDeviceFromRegistryL();
	iArbitrationDelay = CArbitrationDelayTimer::NewL(this);

	TCallBack cb1(RoleSwitchCompleteCallBack, this);
	iRoleSwitchCompleteCallBack = new (ELeave)CAsyncCallBack(cb1, EActiveHighPriority);

	TCallBack cb2(OverrideLPMTimeoutCallback, this);
	iOverrideLPMTimerEntry.Set(cb2);

	iPhysicalLinkMetrics = CPhysicalLinkMetrics::NewL(*this, iLinksMan.HCIFacade().CommandQController());
	}

TBool CPhysicalLink::HasHandle(THCIConnHandle aHandle) const
	{
	LOG_FUNC
	if (iHandle == aHandle)
		return ETrue;	// Only one ACL handle is available.
	if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aHandle)
		return ETrue;
	return EFalse;
	}

TBool CPhysicalLink::HasSyncLink() const
	{
	LOG_FUNC
	return (iSyncLogicalLink != NULL);
	}

void CPhysicalLink::SubscribeLinkObserver(MPhysicalLinkObserver& aObserver)
/**
The subscribed object will be notified whenever a link state change occurs.
**/
	{
	LOG_FUNC
#ifdef _DEBUG
	//ensure the subscriber isn't already subscribed...
	TInt found = 0;
	TDblQueIter<MPhysicalLinkObserver> iter(iBasebandSubscribers);
	while (iter)
		{
		if (iter++ == &aObserver)
			{
			found++;
			}
		}

	__ASSERT_DEBUG(found==0, Panic(ELinkMgrBadBasebandArray));
#endif	//_DEBUG

	iBasebandSubscribers.AddFirst(aObserver.ObserverQLink());
	}

void CPhysicalLink::UnsubscribeLinkObserver(MPhysicalLinkObserver& aObserver)
/**
aSubscriber will no longer be notified of state changes.
**/
	{
	LOG_FUNC
	aObserver.ObserverQLink().Deque();
	}

TInt CPhysicalLink::TryToAndThenPreventHostEncryptionKeyRefresh(TAny* aOutToken)
	{
	LOG_FUNC
	TInt err = KErrNone;
	// The handling of the TAny* parameter seems a bit wacky - but it makes sense as follows
	// this call handles a call from the bluetooth control plane (which passes
	// only a TAny* as a parameter).  We need to return a value back through as well, so we need
	// a pointer to a pointer (so after using the input it can be modified to point to the
	// output).  We need a Bluetooth device address so a pointer to a pointer to a TBTDevAddr 
	// is passed down. Then the pointer to a pointer is used to update the pointer to a control
	// plane token (which represents a handle preventing host encryption key refreshes).
	if (!IsEncryptionPauseResumeSupported())
		{
		err = KErrNotSupported;
		*reinterpret_cast<MBluetoothControlPlaneToken**>(aOutToken) = NULL;
		}
	else
		{
		if (iAutoKeyRefreshQue.IsEmpty())
			{
			TRAP_IGNORE(iLinksMan.HCIFacade().RefreshEncryptionKeyL(iHandle));
			// If we can't refresh the encryption key, there's not much we can do
			}
		XAutoKeyRefreshToken* token = new XAutoKeyRefreshToken();
		if (token)
			{
			iAutoKeyRefreshQue.AddLast(*token);
			}
		else
			{
			err = KErrNoMemory;
			}
		*reinterpret_cast<MBluetoothControlPlaneToken**>(aOutToken) = token;
		}
	return err;
	}

void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TInt /*aResult*/)
/**
	A task has completed where we don't expect a response - just cleanup helper
**/
	{
	LOG_FUNC
	// remove from array
	iRegistryHelpers.Remove(*aHelper);
	// delete
	delete aHelper;
	}

/**
The completion function of a registry helper returning a remote device entry.
*/
void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper,
										 const TBTNamelessDevice& aDevice,
										 TInt aResult)
/**
	A registry task has completed where we expect a device to be returned
**/
	{
	LOG_FUNC
	LOG1(_L("CPhysicalLink: Registry task complete; result %d"), aResult);

	// The TBTNamelessDevice assignment operator only performs an update on valid
	// entries, as we need a bitwise copy (since we want to mirror what is in the registry)
	// we need to go to these extras steps.
	TPckg<TBTNamelessDevice> cachedVersion(iRegistryDevice);
	cachedVersion.Copy(TPckg<TBTNamelessDevice>(aDevice));

	// We have to be careful about information flow from the registry to the actual representation (iDevice).
	// Although the values in the returned entry are useful if the stack has not yet got the information,
	// if the stack has already gathered values then they will be more up-to-date.  Therefore we need to
	// assign the current values first if they are valid to build up the latest representation.
	TBTNamelessDevice device = aDevice; // Take a local copy to work on.
	if(iDevice.IsValidDeviceClass())
		{
		device.SetDeviceClass(iDevice.DeviceClass());
		}
	if(iDevice.IsValidPageScanRepMode())
		{
		device.SetPageScanRepMode(iDevice.PageScanRepMode());
		}
	if(iDevice.IsValidPageScanPeriodMode())
		{
		device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode());
		}
	if(iDevice.IsValidPageScanMode())
		{
		device.SetPageScanMode(iDevice.PageScanMode());
		}
	if(iDevice.IsValidClockOffset())
		{
		device.SetClockOffset(iDevice.ClockOffset());
		}
	if(iDevice.IsValidUsed())
		{
		device.SetUsed(iDevice.Used());
		}
	if(iDevice.IsValidSeen())
		{
		device.SetSeen(iDevice.Seen());
		}

	// With the link key (and friends) we need to be even more careful, as there is effectively
	// two-way flow of information:
	// 1) Link key storage from the stack.
	// 2) Unbonding from the clients of the registry.
	// Further to this, if the registry entry does not have a link key, it can mean one of several
	// things:
	// a) No link key has been generated with the remote device yet.
	// b) A client of the registry has unbonded the remote device.
	// c) The current link key is not to form a bond.
	// Taking this into account we make a suitable choice about what to do.
	if(!iDevice.IsValidLinkKey())
		{
		// If there isn't any link key in the system yet - and so it is safe to update the representation
		// to use the value from the registry.
		// <NOP> - see the global update below.
		}
	else
		{
		// We currently have a link key....
		if(device.IsValidLinkKey() || iRegistryDevBeingMod.InUse())
			{
			// Even if the linkkey in registry is valid, it must be older or same as the current
			// link key (as the stack is the only one who should be adding keys). Also, if we are in the
			// middle of updating the registry (for any purpose) then we can ignore the registry value as it
			// may not be up-to date. So, for both the cases just update with the latest.
			// There is an interesting case if client modifies the registry by unpairing/or deleting the device
			// and stack also tries to updates the link key in registry at the same time. We will give priority
			// to stack's updations by keeping the latest copy as it is.
			device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType());
			if(iDevice.IsValidPassKey())
				{
				device.SetPassKey(iDevice.PassKey());
				}
			}
		else
			{
			// This is the interesting case where a client has unbonded, but we already have a link
			// key.  What we do here is interesting.
			if(IsAuthenticationPending() || LinkState().Authenticated() || SimplePairingMode() == EPhySimplePairingEnabled)

				{
				// If we're already authenticated / authenticating then we're already using our link
				// key.  As such we need to continue using it, but we don't want to update
				// Also if the link is SSP enabled then we can re-authenticate - this removes any chance of a
 				// race condition with the registry.
				iPreventLinkKeyUpdateReg = ETrue; // prevent locally cached link key being given back to the registry
				}
			else
				{
				// The link key hasn't been used yet - so we can update the representation to not
				// have a link key (as requested by the user).
				// Currently there is no chance of a race with the registry as combination link keys are
 				// always form a bond.
				iDevice.DeleteLinkKey(); // delete locally cached link key
				}
			}
		}

	// Finally update the representation - we should have manipulated it correctly.
	iDevice = device;

	// Store the result of the retrieval for usage later.
	iDeviceResult = aResult;

	// the HW asked earlier for a link key - we can respond now
	__ASSERT_DEBUG(iDevice.IsValidAddress(), Panic(EBTPhysicalLinksInvalidAddress));
	if(iWaitingForLinkKeyFromRegistry)
		{
		if (aResult == KErrNone && iDevice.IsValidLinkKey())
			{
			if ( iDevice.LinkKeyType() != ELinkKeyCombination)
				{
				if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug)
					{
					LOG(_L("CPhysicalLink: Debug mode - Link to debug link key"))
					iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
					}
				else
				if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable())
					{
					LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated"))
					iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
					}
				else
					{
					LOG(_L("CPhysicalLink: Issuing link key to HC now"))
					iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey());
					}
				}
			else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled)
				{
				LOG(_L("CPhysicalLink: Issuing link key to HC now"))
				iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey());
				}
			else
				{
				LOG(_L("CPhysicalLink: Current PIN code too short!"))
				iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
				}
			}
		else
			{
			iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address());
			}	
		}
	iRequireAuthenticatedLinkKey = EFalse;
	iWaitingForLinkKeyFromRegistry = EFalse;

	RegistryTaskComplete(aHelper, aResult);	 // cleans up our helper
	}

void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TRegistryUpdateStatus aRegUpdateStatus, TInt aResult)
	{
/**
	This method allows the RegistryHelpers to indicate the registry action taking place

**/
	LOG_FUNC
	if(iRemoteDeviceRecordedByStack == EPending && aRegUpdateStatus != ENoChange)
		{
		if (aResult == KErrNone)
			{
			iRemoteDeviceRecordedByStack = EComplete;
			}
		else
			{
			iRemoteDeviceRecordedByStack = ENull;
			}
		}

	iRegistryDevBeingMod.RequestCompleted();
	RegistryTaskComplete(aHelper, aResult);	 // cleans up our helper
	}

void CPhysicalLink::CompletedPackets(THCIConnHandle aConnH, TUint16 aNumPackets)
/**
	In this domain we are happy that a valid event has come in telling us that
	the buffers in the local controller have been freed

	Since this reflects both local hardware features (buffers) and flow control
	we treat them separately

**/
	{
	LOG_FUNC
	// firstly report that local hardware buffers free (via inlines!)
	iLinksMan.LinkManagerProtocol().LinkMuxer().DataQController().CompletedPackets(aConnH, aNumPackets);

	// we forward this to the thing that sends the packets so that they can do Flow Control

	// could speed up - already done this handle test to get into *this* object
	if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aConnH)
		{
		iSyncLogicalLink->PacketsSent(aConnH, aNumPackets);
		}
	else
		{
		for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
			{
			iACLLogicalLinks[i]->PacketsSent(aConnH, aNumPackets);
			}
		}
	}

void CPhysicalLink::TryToSend()
	{
	LOG_FUNC
	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
		{
		iACLLogicalLinks[i]->TryToSend();
		}
	}

void CPhysicalLink::NewLinkKey(const TBTDevAddr& /*aAddr*/, const TBTLinkKey& aLinkKey, THCILinkKeyType aLinkKeyType)
	{
	LOG_FUNC
	if(iLinkKeyPending)
		//user has entered a PIN and a new link key has come in.
		{
		iLinkKeyPending = EFalse;
		iPreventLinkKeyUpdateReg = EFalse; //override previous unpair so that link key will be stored in the registry.
		}

	TBTLinkKeyType linkKeyType = ELinkKeyCombination;

	switch (aLinkKeyType)
		{
	case ECombinationKey:
		linkKeyType = ELinkKeyCombination;
		break;
	case EDebugCombinationKey:
		linkKeyType = ELinkKeyDebug;
		break;
	case EAuthenticatedCombinationKey:
		linkKeyType = ELinkKeyAuthenticated;
		break;
	case EUnauthenticatedCombinationKey:
		if (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask) ||
			(iIOCapability == EIOCapsDisplayOnly) || (iIOCapability == EIOCapsNoInputNoOutput))
			{
			linkKeyType = ELinkKeyUnauthenticatedNonUpgradable;
			}
		else
			{
			linkKeyType = ELinkKeyUnauthenticatedUpgradable;
			}
		break;
	case EChangedCombinationKey:
		linkKeyType = iDevice.LinkKeyType(); // The link key type hasn't changed
		break;

	case ELocalUnitKey:
	case ERemoteUnitKey:
	default:
		LOG1(_L("CPhysicalLink: Unexpected link key type (%d)"), aLinkKeyType);
		__ASSERT_DEBUG(EFalse, Panic(EBTUnexpectedLinkKeyType));
		break;
		}

	if (linkKeyType == ELinkKeyCombination && iNewPinCodeValid)
		{
		// Only bind a PIN code to the device if we receive a link key based on it.
		iNewPinCodeValid = EFalse;
		iDevice.SetPassKey(iNewPinCode);
		}


	SetLinkKey(aLinkKey, linkKeyType);	// keeps a copy in our 'cache', updates paired list in PHYs mananger

	TRAP_IGNORE(StoreDeviceL(EFalse)); //EFalse: new meaning - do not prevent addition
	// if that errored we just have to keep it here - but it won't be in registry
	// so it'll be a transient pairing
	}

void CPhysicalLink::UpdateFromInquiryCache()
	{
	LOG_FUNC
	CBTInqResultRecord* juice = NULL; //juice is data from remote which can help/speed up a connection
	// see if we can speed up the connection - the inquiry cache may have some juice
	juice = iLinksMan.LinkManagerProtocol().InquiryMgr().BasebandParametersFromCache(iDevice.Address());
	if(!juice)
	//no juice in inquiry manager
		{
		return;
		}

	TInquiryLogEntry& jle = juice->LogEntry();
	// Inquiry cache may have useful info we can use: put them in our device
	// so that we can use this, and update this into the Registry later.
	// This will allow Apps to get this to initiate faster connections.
	// The update to the Registry doesn't happen here, but at other interesting times
	ASSERT_DEBUG(jle.iBdaddr == iDevice.Address());
	//Only update with juice values from inquiry manager if they are have come from HCI
	//(i.e. from remote) - records in the inquiry manager are filled with default
	//values and then fields are updated as relevant HCI events occur.
	// - iDevice may already contain values found in the registry which are
	//not default and therefore should have come themselves from the HCI
	if(juice->IsPageScanModeFromHCI())
		{
		iDevice.SetPageScanMode(jle.iPageScanMode);
		}
	if(juice->IsPageScanRepModeFromHCI())
		{
		iDevice.SetPageScanRepMode(jle.iPageScanRepetitionMode);
		}
	if(juice->IsClockOffsetFromHCI())
		{
		iDevice.SetClockOffset(jle.iClockOffset);
		}
	}

void CPhysicalLink::StoreDeviceL( TBool aPreventDeviceAddition )
	{
	LOG_FUNC

	UpdateFromInquiryCache(); //check juice has not just come in - update iDevice if so

	//only write back the necessary attributes
	TBTNamelessDevice device;
	if(iDevice.IsValidAddress())
		{
		device.SetAddress(iDevice.Address());
		}
	if(iDevice.IsValidDeviceClass())
		{
		device.SetDeviceClass(iDevice.DeviceClass());
		}
	if(iDevice.IsValidLinkKey() && !iPreventLinkKeyUpdateReg)
		{
		if(!(iIOCapsReceived && (iAuthenticationRequirement == EMitmNotReqNoBonding || iAuthenticationRequirement == EMitmReqNoBonding)))
			{
			LOG(_L("!!! Storing Link Key in Registry"));
			device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType());
			}
		}
	if(iDevice.IsValidPageScanRepMode())
		{
		device.SetPageScanRepMode(iDevice.PageScanRepMode());
		}
	if(iDevice.IsValidPageScanPeriodMode())
		{
		device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode());
		}
	if(iDevice.IsValidPageScanMode())
		{
		device.SetPageScanMode(iDevice.PageScanMode());
		}
	if(iDevice.IsValidClockOffset())
		{
		device.SetClockOffset(iDevice.ClockOffset());
		}
	if(iDevice.IsValidUsed())
		{
		device.SetUsed(iDevice.Used());
		}
	if(iDevice.IsValidSeen())
		{
		device.SetSeen(iDevice.Seen());
		}
	if(iDevice.IsValidPassKey())
		{
		device.SetPassKey(iDevice.PassKey());
		}

	// if already exists an outstanding request with the same device info
	// is useless to store it again. Just return doing nothing.
	if (iRegistryDevBeingMod.InUse() && iRegistryDevBeingMod.IsEqual(device))
		{
		return;	// do nothing
		}

	CBTDeviceModifier* modifier = CBTDeviceModifier::NewL(iRegSess, *this, iLinksMan.LinkManagerProtocol().InquiryMgr());
	iRegistryHelpers.AddLast(*modifier);
	TBool allowAdd = EFalse;

	if(!aPreventDeviceAddition && iRemoteDeviceRecordedByStack == ENull)
		//Only allow the device to be added to the registry if
		// 1) this StoreDeviceL function has not been called
		//	  with its "prevent device addition" parameter set to true
		// 2) we believe the cached remote device details have not yet
		//    been recorded in the registry.
		//    (If they have and if they are no longer there it is
		// 	   because an app has deleted them from the registry...
		//	   so we should not re-add them )
		{
		allowAdd = ETrue;
		}
	modifier->Start(device, allowAdd);
	iRegistryDevBeingMod.Begin(device);

	if(allowAdd)
		//The physical link object has now made its attempt to add its cached
		//remote device details to the registry.
		{
		iRemoteDeviceRecordedByStack = EPending;
		}

	}

void CPhysicalLink::SetLinkKey(const TBTLinkKey& aLinkKey, TBTLinkKeyType aLinkKeyType)
	{
	LOG_FUNC
	iDevice.SetLinkKey(aLinkKey, aLinkKeyType); // keeps a copy in our 'cache'
	}

void CPhysicalLink::ReadRemoteSupportedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTFeatures& aBitMask)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	if (aErr == KErrNone)
		{
		iRemoteFeatures = aBitMask;

		if(!iConnectionPacketTypeChanged)
			{
			//Send change connection packet type HCI command if application hasn't, we have to do this
			//because some hardware defaults to using 1-slot packets unless told otherwise by the host.
			ChangeConnectionPacketType(EAnyACLPacket);
			}

		//If remote host supports extended features, then send request to read these.
		if (iRemoteFeatures[ESupportedExtendedFeaturesBit])
			{
			TRAP_IGNORE(iLinksMan.HCIFacade().ReadRemoteExtendedFeaturesL(iHandle, KRemoteExtendedFeaturesPage1));
			}
		else
			{
			// If the remote doesn't support extended features, then neither they support SSP
			// (no way to indicate the host supported bit).  So set it as disabled.
			TPhysicalLinkSimplePairingMode previousSetting = SimplePairingMode();
			__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
			iSimplePairingMode = EPhySimplePairingDisabled;
			if(SimplePairingMode() != previousSetting)
				{
				iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
				}
			}
		}
	else
		{
		iRemoteFeatures = TBTFeatures(KInvalidRemoteFeatures);
		}
	}

void CPhysicalLink::ReadRemoteExtendedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, TUint64 aBitMask, TUint8 aPageNumber, TUint8 /* aMaximumPageNumber */)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	LOG1(_L("CPhysicalLink: ReadRemoteExtendedFeaturesComplete; result %d"), aErr);

	switch (aPageNumber)
		{
		case KRemoteExtendedFeaturesPage1:
			{
			TPhysicalLinkSimplePairingMode currentSetting = SimplePairingMode();

			if (aErr == EOK && aBitMask & (1 << ESecureSimplePairingHostSupportBit) && iLinksMan.SecMan().LocalSimplePairingMode())
				{
				__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
				iSimplePairingMode = EPhySimplePairingEnabled;
				}
			else
				{
				__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
				iSimplePairingMode = EPhySimplePairingDisabled;
				}
			if(SimplePairingMode()!=currentSetting)
				{
				iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
				}			
			break;
			}
		default:
			{
			// Ignore
			}
		}
	}

void CPhysicalLink::ReadRemoteVersionInfoComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTDevRemoteHwVersion& aVer)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	if (aErr == KErrNone)
		{
		iRemoteVersion = aVer;
		}
	else
		{
		iRemoteVersion = KInvalidRemoteHwVersion;
		}
	}

void CPhysicalLink::AuthenticationComplete(THCIErrorCode aErr, THCIConnHandle aConnH)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	iLinkState.SetAuthenticated(aErr == EOK);
	//__ASSERT_DEBUG(iAuthStateMask == EAuthenticationRequestPending,Panic(EUnexpectedAuthenticationState));
	AuthenticationComplete(EAuthenticationRequestPending);

	if (aErr!=EOK)
		{
		if(aErr == ERemoteUserEndedConnection)
			{
			HandlePrefetch();
			}
		iNewPinCodeValid = EFalse;
		delete iPinRequester;
		iPinRequester = NULL;
		iPinHandler = NULL;
		}
	else
		{
		__ASSERT_DEBUG(!iPinHandler, Panic(EBTPhysicalLinkPinHandlerStillPresent));
		}

	iLinksMan.SecMan().AuthenticationComplete(BDAddr(), aErr);

	TBTBasebandEventNotification event(ENotifyAuthenticationComplete, aErr);
	NotifyStateChange(event);
	}

void CPhysicalLink::HandlePrefetch()
	{
	// Here we determine whether we need to handle a pre-fetch case.  If there is an
	// outbound authentication attempt and we have a PIN notifier we need to hand it
	// over to the pre-fetch manager.
	// We also note the fact that we've done this for any access requester to know whether
	// it should indicate to defer.
	if(iPinRequester && iLinksMan.SecMan().IsOutboundAccessRequest(BDAddr()) && iSimplePairingMode != EPhySimplePairingEnabled)
		{
		TInt err = iLinksMan.PrefetchMan().HandleOverPinRequester(BDAddr(), iPinRequester);
		if(err == KErrNone)
			{
			iPinRequester = NULL; // As far as phys links is concerned the pin requester doesn't exist anymore.
			iPinHandler = NULL; // So the handler is also not needed.
			}
		}
	}

void CPhysicalLink::EncryptionChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBool aEncrypted)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	if(aErr == EOK)
		{
		// Only change state if it's an actual encryption change - not an error (because if an error
		// we should presumably stay the same).
		if(aEncrypted)
			{
			iLinkState.SetEncrypted(ETrue);
			if(!iLinkState.Authenticated())
				{
				// If a link is encrypted then we know that the link is also authenticated.
				iLinkState.SetAuthenticated(ETrue);
				}
			}
		else
			{
			iLinkState.SetEncrypted(EFalse);
			}
		}

	TBTBasebandEventNotification event(aEncrypted ? ENotifyEncryptionChangeOn : ENotifyEncryptionChangeOff, aErr);
	NotifyStateChange(event);

	// Having the encryption enforcer present means that we have asked for encryption.
	// But we should only act if this is a valid encryption change event.
	if (iEncryptionEnforcer && aErr == EOK)
		{
		// if the roleswitch has been requested by us then CRoleSwitcher has already parked / unparked
		// the ACL controller. That means that the request is submitted twice.
		// This is not supposed to be a problem, but we need to be aware of it.
		if (!aEncrypted)
			{
			iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, ETrue);
			iEncryptionEnforcer->EncryptionDisabled(SimplePairingMode()== EPhySimplePairingEnabled);
			}
		else
			{
			iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, EFalse);
			iEncryptionEnforcer->EncryptionEnabled();
			}
		}
	}

TInt CPhysicalLink::ChangeEncryption(THCIEncryptModeFlag aEnable)
	{
	LOG_FUNC
	// no policy to adjust, but test local & remote features
	if (!IsEncryptionSupported())
		{
		return KErrNotSupported;
		}

	TRAPD(err, iLinksMan.HCIFacade().SetEncryptL(Handle(), aEnable));
	if (err == KErrNone)
		{
		err = ManageEncryptionEnforcement(aEnable);
		}

	return err;
	}

TInt CPhysicalLink::Authenticate(TBool aRequireAuthenticatedLinkKey)
	{
	LOG_FUNC
	// no policy to adjust, just check to see if the link is already authenticated
	TInt err = KErrNone;
	__ASSERT_DEBUG(iSimplePairingMode != EPhySimplePairingUndefined, Panic(EBTPhysicalLinksInvalidArgument));

	/* If its a dedicated bonding attempt, then we always want to authenticate again in order to generate
	 * a stronger linkkey if possible
	 */
	if (!iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()) && iLinkState.Authenticated() && !iLinksMan.LinkManagerProtocol().IsSecureSimplePairingSupportedLocally())
		{
		err = KErrAlreadyExists;
		}
	else
		{
		iRequireAuthenticatedLinkKey = aRequireAuthenticatedLinkKey;
		TRAP(err, iLinksMan.HCIFacade().AuthenticateL(Handle()));
		if(err == KErrNone)
			{
			SetAuthenticationPending(EAuthenticationRequestPending);
			}
		}
	return err;
	}

TInt CPhysicalLink::ChangeLinkKey()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iLinkState.Authenticated(), Panic(EBTPhysicalLinkNotAuthenticated));

	// Check if the peer device can support the link key regeneration procedure.
	if(PeerSupportsLinkKeyRegeneration())
		{
		return iLinksMan.HCIFacade().ChangeConnectionLinkKey(Handle());
		}
	return KErrNone;
	}

TBool CPhysicalLink::PeerSupportsLinkKeyRegeneration() const
	{
	LOG_FUNC
	// Any future manufacturers or manufacturer versions that do not support
	// link key re-generation should be added to this method.

	// See Bluetooth assigned numbers for an explanation of this value.
	return (iRemoteVersion.iManufacturerID != 0x000a);
	}

void CPhysicalLink::ACLDataReceived(THCIConnHandle aConnH, TUint8 aFlag, const TDesC8& aData)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	// there may be a race condition in that we have got ACL data but just started
	// shutdown procedures...

	// find the ACL wanting this type of data...
	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
		{
		iACLLogicalLinks[i]->DataReceived(aConnH, aFlag, aData);
		}
	// else just dump
	}

void CPhysicalLink::SCODataReceived(THCIConnHandle aConnH, const TDesC8& aData)
	{
	LOG_FUNC
	if (iSyncLogicalLink)
		iSyncLogicalLink->DataReceived(aConnH, 0, aData);	// flags ignored

	}

void CPhysicalLink::ConnectionComplete(THCIErrorCode aErr, const TBTConnect& aConn)
	{
	LOG_FUNC
	if (aErr == KErrNone)
		{
		if(aConn.iLinkType == ESCOLink && !iSyncLogicalLink)
			{
			LOG(_L("Got a ConnectionComplete for a non-existant SCO link"))
			//This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for
			//a remote device to respond to a connection request.
			iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);

			if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that
				{
				//The baseband might actually have established a SCO link, so send a Disconnect.
				//If no SCO link exists the command will fail gracefully.
				TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
				}

			return;
			}

		// update bb model
		iLinksMan.Baseband().UpdateModel(aConn);
		if (aConn.iLinkType == EACLLink)
			{
			// we need to store the physical handle we're on - in BT1.2 this is also taken to be
			// the ACL Link handle too...
			// do the PHY stuff first
			LOG(_L("CPhysicalLink: PHY ready"))

			iHandle = aConn.iConnH;
			iDevice.SetAddress(aConn.iBdaddr);

			//Set Default Local Link Policy;
			//Get this in early to try and avoid conditions where remote requests
			//something we haven't told the controller we allow yet.
			SetModesAllowed(iLinksMan.LinkManagerProtocol().ModesSupportedLocally(),
							iLinksMan.RoleSwitchAllowed());

			// try to get details of remote device
			TRAP_IGNORE(GetRemoteDetailsL(aConn));
			// ignore error - only optimisations

			// assume we are going with 1 slot packets until notified otherwise, and in activemode
			iLinkState.SetLinkState(TBTBasebandLinkState::ELinkUp);
			iLinkState.SetMaxSlots(1);
			iLinkState.SetLinkMode(EActiveMode);

			// If the peer is in security mode 3 then authenication has completed.
			if(iPeerInSecurityMode3)
				{
				iLinkState.SetAuthenticated(ETrue);
				AuthenticationComplete(EAuthenticationRequestPending);
				}

			// update bb model for max slots
			iLinksMan.Baseband().UpdateModel(aConn.iConnH, 1);

			TBTBasebandEventNotification event(ENotifyPhysicalLinkUp);
			NotifyStateChange(event);

			if (aConn.iEncryptMode)
				{
				// pretend there's been an encryption event
				EncryptionChange(aErr, aConn.iConnH, aConn.iEncryptMode);
				}
			}

		// This is assuming that our stack only allows one synchronous link per phy link.
 		// SCO (not eSCO) Link getting notified here.
 		else if(aConn.iLinkType == ESCOLink)
 			{
 			TBTBasebandEventNotification event(ENotifySynchronousLinkUp);
 			NotifyStateChange(event);
 			}

		// tell the logical links
		NotifyLogicalLinkUp(aConn);
		iLinksMan.ArbitrateAllPhysicalLinks();

		// Check to see if we got a disconnect request during the period before the link was
		// fully established.
		if (iDisconnectRequested)
			{
			__ASSERT_DEBUG(aConn.iLinkType == EACLLink, Panic(EDisconnectRequestedNotOnACLLink));

			// We allow the link to come up fully so that the link is not in a strange state between
			// pending and up. This also means that the notifications to the logical links all work
			// correctly, they just see a link come up and then go down again straight away.
			TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
			}
		else
			{
			TTime t;
			t.UniversalTime();
			iDevice.SetUsed(t);
			TRAP_IGNORE(StoreDeviceL(EFalse));
			}
		}
	else
		{
		// error occurred - need to see if it's PHY(and so ACL) or SCO that failed
		// tell logical links
		LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType);

		if(((aConn.iLinkType == ESCOLink)||(aConn.iLinkType == EeSCOLink)) && iSyncLogicalLink &&
		iSyncLogicalLink->Handle() != KInvalidConnectionHandle && iSyncLogicalLink->Handle() != aConn.iConnH)
			{
			// This is a secondary SCO link we just rejected or something - don't mess with our own
			// The KInvalidConnectionHandle test ensures that we ignore errored outgoing SCO connects.
			return;
			}

		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);
		NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr));
		if (aConn.iLinkType == EACLLink)
			{
			// BT 1.2 says that as the ACL Link goes up and down, so does the physical link
			// so if the ACL Link has gone, so has this
			// for SCO we remain in place.
			TBTBasebandEventNotification event(ENotifyPhysicalLinkError, CHciUtil::SymbianErrorCode(aErr));
			NotifyStateChange(event);
			delete this;
			}
		}
	// ***Watchout*** delete this above: careful about code here
	}


void CPhysicalLink::SynchronousConnectionComplete(THCIErrorCode aErr,
													const TBTConnect& aConn,
													const TBTSyncConnectOpts& aSyncOpts)
	{
	LOG_FUNC
	__ASSERT_DEBUG(((aConn.iLinkType == ESCOLink) || (aConn.iLinkType == EeSCOLink)), Panic(EBTNonSyncConnectInSyncConnectFunc));

	if(!iSyncLogicalLink)
		{
		LOG(_L("Got a SynchronousConnectionComplete when no SCO link is bound"))
		//This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for
		//a remote device to respond to a connection request.
		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);

		if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that
			{
			//The baseband might actually have established a SCO link, so send a Disconnect.
			//If no SCO link exists the command will fail gracefully.
			TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection));
			}

		return;
		}

	if (aErr == KErrNone)
		{
		// update bb model
		iLinksMan.Baseband().UpdateModel(aConn);

 		// This is assuming that our stack only allows one synchronous link per phy link.
 		// eSCO (not SCO) Link getting notified here.
 		TBTBasebandEventNotification event(ENotifySynchronousLinkUp);
 		NotifyStateChange(event);

		// tell the logical links
		NotifyLogicalSyncLinkUp(aConn, aSyncOpts);
		iLinksMan.ArbitrateAllPhysicalLinks();
		}
	else
		{
		// error occurred - need to see if it's PHY(and so ACL) or SCO that failed
		// tell logical links
		LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType);
		iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType);

		// Before passing error around, check if it's actually for this link...
		// A listening link will have a NULL handle, otherwise the handle should match.
		// If the eSCO connection fails, the handle is KInvalidConnectionHandle (as initialised in CBTBasebandSAP)
		// This avoids the situation where we get notified of a rejection.
		if (iSyncLogicalLink &&
			(iSyncLogicalLink->Handle() == 0 || iSyncLogicalLink->Handle() == KInvalidConnectionHandle || iSyncLogicalLink->Handle() == aConn.iConnH))
			{
			NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr));
			}
		}
	}


void CPhysicalLink::GetRemoteDetailsL(const TBTConnect& aConn)
	{
	LOG_FUNC
	GetDeviceFromRegistryL();
	iLinksMan.HCIFacade().ReadClockOffsetL(aConn.iConnH);
	iLinksMan.HCIFacade().ReadRemoteSupportedFeaturesL(aConn.iConnH);
	iLinksMan.HCIFacade().ReadRemoteVersionL(aConn.iConnH);
	iLinksMan.LinkManagerProtocol().InquiryMgr().ReadRemoteNameL(aConn.iBdaddr);
	}

void CPhysicalLink::LinkSupervisionTimeoutChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewTimeout)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	if(aErr == EOK)
		{
		iLSTO = aNewTimeout;
		}
	}

void CPhysicalLink::PacketTypeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewPacket)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);

	if(aErr==EOK)
		{
		iLinksMan.Baseband().UpdateModelIfRecordExists(aConnH, aNewPacket);
		iLinkState.SetPacketTypes(aNewPacket);
		}

	TUint32 eventType = 0;

	if(aNewPacket & EPacketsDM1)
		eventType |= ENotifyPacketsDM1;
	if(aNewPacket & EPacketsDH1)
		eventType |= ENotifyPacketsDH1;
	if(aNewPacket & EPacketsDM3)
		eventType |= ENotifyPacketsDM3;
	if(aNewPacket & EPacketsDH3)
		eventType |= ENotifyPacketsDH3;
	if(aNewPacket & EPacketsDM5)
		eventType |= ENotifyPacketsDM5;
	if(aNewPacket & EPacketsDH5)
		eventType |= ENotifyPacketsDH5;

	if(aNewPacket & EPacketsHV1)
		eventType |= ENotifyPacketsHV1;
	if(aNewPacket & EPacketsHV2)
		eventType |= ENotifyPacketsHV2;
	if(aNewPacket & EPacketsHV3)
		eventType |= ENotifyPacketsHV3;

	TBTBasebandEventNotification event(eventType, aErr);
	NotifyStateChange(event);
	}

void CPhysicalLink::MaxSlotsChange(THCIConnHandle aConnH, TUint8 aSlots)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	LOG2(_L("Connection Handle 0x%04x, using %d slots"), aConnH, aSlots);
	iLinksMan.Baseband().UpdateModel(aConnH, aSlots);	//event only for ACL
	iLinkState.SetMaxSlots(aSlots);

	TBTBasebandEventNotification event;
	switch(aSlots)
		{
		case 1:
			event.SetEventType(ENotifyMaxSlots1);
			break;
		case 3:
			event.SetEventType(ENotifyMaxSlots3);
			break;
		case 5:
			event.SetEventType(ENotifyMaxSlots5);
			break;
		};
	NotifyStateChange(event);
	}

void CPhysicalLink::ModeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBTLinkMode aMode, TBasebandTime aInterval)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	LOG3(_L("Connection Handle 0x%04x: ModeChange event %d->%d"), aConnH, iLinkState.LinkMode(), aMode);

	TBTLinkMode oldMode = iLinkState.LinkMode();

	iLinkState.SetLinkMode(aMode);	// remember the state for ourselves

	if (aErr == EOK)
		{
		if (aMode == EParkMode)
			{
			iLinksMan.Baseband().ParkLink(aConnH);	//remove from model whilst parked
			}

		if (aMode == EActiveMode && oldMode==EParkMode)
			{
			// unparking we must tell the DataQ - we could always get the dataQ to ask us before sending of course
			// but that might be performance harming
			iLinksMan.Baseband().UnParkLink(aConnH); //NB Max Slots = 1 default
			}

		if (aMode == ESniffMode )
			{
			// Store the Sniff Interval
			iSniffInterval = aInterval;
			}

		if (aMode == EActiveMode && iSniffInterval)
			{
			//Active mode enable and SniffInterval is set.
			//Therefore must be leaving sniff mode.
			__ASSERT_DEBUG(oldMode==ESniffMode, Panic(EBTPhysicalLinkModeChangeErrorLeavingSniffMode));

			//When not in sniff mode, sniff interval is obviously 0!
			iSniffInterval = 0;
			}
		}

	// Generate a baseband event, and offer it to the proxySAP's associated
	// with this link.
	TBTBasebandEventNotification event;
	switch(aMode)
		{
		case EActiveMode:
			event.SetEventType(ENotifyActiveMode);
			break;
		case ESniffMode:
			event.SetEventType(ENotifySniffMode);
			break;
		case EHoldMode:
			event.SetEventType(ENotifyHoldMode);
			break;
		case EParkMode:
			event.SetEventType(ENotifyParkMode);
			break;
		case EScatterMode:
			break; // shouldnt be seen yet - if so, ignore for now
		};
	event.SetErrorCode(aErr);
	NotifyStateChange(event);
	}

void CPhysicalLink::RoleChange(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, TBTBasebandRole aRole)
	{
	LOG_FUNC
	LOG1(_L("CPhysicalLink: Role change - new role %S"), aRole == EMaster ? _S("Master") : _S("Slave"));

	if (aErr == EOK)
		{
		iLinkState.SetLinkRole(aRole);
		}

	// Generate a baseband event, and offer it to the proxySAP's associated
	// with this link.
	TBTBasebandEventNotification event;
	if(aRole == EMaster)
		{
		event.SetEventType(ENotifyMaster);
		}
	else
		{
		event.SetEventType(ENotifySlave);
		}
	event.SetErrorCode(aErr);
	NotifyStateChange(event);

	// if iEncryptionEnforcer is present it means we asked for encryption, so we inform it
	// about the roleswitch
	if (iEncryptionEnforcer)
		{
		iEncryptionEnforcer->RoleSwitchEvent();
		}
	}

void CPhysicalLink::WriteLinkPolicySettingsCompleteEvent(THCIErrorCode aErr, THCIConnHandle aConnH)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	// we don't get the things back we set...
	if (aErr != EOK)
		{
		// The command has failed.	Clear the cached values and re-arbitrate.
		iLinkPolicy.SetModesAllowed(0);
		iLinkPolicy.SetSwitchAllowed(EFalse);
		Arbitrate();
		}
	}

void CPhysicalLink::ClockOffset(THCIErrorCode aErr, THCIConnHandle aConnH, TBasebandTime aClockOffset)
	{
	LOG_FUNC
	__CHECK_CONNECTION_HANDLE(aConnH);
	if (aErr==EOK)
		{
		// tell inquiry manager - we found this information to help it
		iLinksMan.LinkManagerProtocol().InquiryMgr().ClockOffsetResult(BDAddr(), aClockOffset);
		}
	// we don't do anything more useful knowing this information
	}

void CPhysicalLink::RemoteName(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, const TBTDeviceName8& aName)
/**
	We don't keep this, but we're interested in persisting it in el Reg
	By receiving this we now our attempts have completed in getting name
*/
	{
	LOG_FUNC
	if (aErr==EOK)
		{
		// slam it into the Registry - this will be useful to UIs if not us
		TRAP_IGNORE(DoUpdateNameL(aName));
		}
	}

void CPhysicalLink::DoUpdateNameL(const TBTDeviceName8& aName)
	{
	LOG_FUNC
	CBTDeviceNameChanger* nameChanger = CBTDeviceNameChanger::NewL(iRegSess, *this);
	iRegistryHelpers.AddLast(*nameChanger);
	nameChanger->Start(BDAddr(), aName);
	}

void CPhysicalLink::Disconnection(THCIErrorCode aErr, THCIConnHandle aConnH, THCIErrorCode aResult)
	{
	LOG_FUNC
	// tell our socket if it's our handle - otherwise the SCO SAP

	LOG1(_L("Physical link: disconnection on handle %d"), aConnH);
	LOG1(_L(" ACL link handle:	 %d"), Handle());
#ifdef __FLOG_ACTIVE
	if (iSyncLogicalLink) LOG1(_L(" Sync link handle: %d"), iSyncLogicalLink->Handle());
#endif

	//no matter if it is the socket or SCO SAP the ui dialogs will be cancelled
	if (iNumericComparator && iNumericComparator->IsActive())
		{
		iNumericComparator->Cancel();
		}
	if (iPasskeyEntry && iPasskeyEntry->IsActive())
		{
		iPasskeyEntry->Cancel();
		}

	if (aConnH == Handle())
		{
		TBTBasebandEventNotification event;
		switch(aErr)
			{
			case EHardwareFail:
				{

				if(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp ||
				   iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending)
					{
					//Only propagate if link is up or pending, otherwise
					//we may get disconnection in unexpected states

					//Fall through
					}
				else
					{
					// this object must go when no link
					delete this;
					break;
					}
				}
			case EOK:
				{
				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown);

				// PHY went down - so have logical links then
				NotifyLogicalLinkDown(EACLLink);
				NotifyLogicalLinkDown(ESCOLink);

				event.SetEventType(ENotifyPhysicalLinkDown);
				event.SetErrorCode(aResult);
				iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink);
				HandlePrefetch();
				NotifyStateChange(event);
				// this object must go when no link
				delete this;
				break;
				}
			case ECommandDisallowed:
				{
				// The connection is still up, so do nothing, and wait for the next idle timer to fire
				break;
				}
			default:
				{
				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown);

				// there's an error
				NotifyLogicalLinkError(EACLLink, aErr);
				NotifyLogicalLinkError(ESCOLink, aErr);
				event.SetEventType(ENotifyPhysicalLinkError);
				event.SetErrorCode(aErr);
				// baseband model needs to err on the side of least bandwidth usage -
				// a link COULD have come down
				iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink);
				HandlePrefetch();
				NotifyStateChange(event);
				// this object must go when no link
				delete this;
				break;
				}
			}
		}

	else if (iSyncLogicalLink && aConnH == iSyncLogicalLink->Handle())
		{
		TBTBasebandEventNotification event;
		// just the SCO link is down so we'd better notify
		if (aErr)
			{
			LOG(_L("Physical link: error disconnection on sync link"));
			TLinkType type = iSyncLogicalLink->LinkType();
			iSyncLogicalLink->Error(CHciUtil::SymbianErrorCode(aErr));
			event.SetEventType(ENotifySynchronousLinkError);
			event.SetErrorCode(aErr);
			// baseband model needs to err on the side of least bandwidth usage -
			// a sync link COULD have come down
			iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type);
			}
		else
			{
			LOG(_L("Physical link: normal disconnection on sync link"));
			TLinkType type = iSyncLogicalLink->LinkType();
			iSyncLogicalLink->Disconnection();
			event.SetEventType(ENotifySynchronousLinkDown);
			event.SetErrorCode(aResult);
			iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type);
			}

		NotifyStateChange(event);
		}
	// NOTE!! delete this above - careful about adding code
	}

void CPhysicalLink::ConnectionRequest(const TBTConnect& aConn)
	{
	LOG_FUNC
	// we must be page scan enable for this to come in
	// but still need to see if it got routed a listening SAP
	TBool accept = EFalse;
	TLogicalLinkListener* listener = NULL;

	__ASSERT_DEBUG(iPendingConnection == EFalse, Panic(EPendingConnectionNotCleared));

	switch (aConn.iLinkType)
		{
		case ESCOLink:
		case EeSCOLink:
			if (!iSyncLogicalLink)
				{
				// If we already have a sync link active, reject request.
				LOG(_L("No current link, checking for listener"));
				listener = iLinksMan.FindListener(aConn.iLinkType);
				}
			break;

		default:
			listener = iLinksMan.FindListener(aConn.iLinkType);
			break;
		}

	if (listener)
		{
		accept = ETrue;
		if (iLinksMan.IsAcceptPairedOnlyMode())
			{
			// do not accept if we are only accepting connection
			// requests from paired devices and we are not already
			// paired with the remote
			CBTPairingsCache::TPairingState pairingState = iLinksMan.PairingsCache().IsPaired(aConn.iBdaddr);

			if(pairingState == CBTPairingsCache::EDeferred)
				{
				// We're still waiting for the Pairing Caches paired device list to be filled.
 				// We'll respond when this is complete, so store details away for then.
				LOG(_L("CPhysicalLink: Waiting for physical link manager's paired device list from Registry!"))
				iPendingConnection = ETrue;
    			iLastPendingConnection.iConnH = aConn.iConnH;
			    iLastPendingConnection.iBdaddr = aConn.iBdaddr;
			    iLastPendingConnection.iCoD = aConn.iCoD;
			    iLastPendingConnection.iLinkType = aConn.iLinkType;
			    iLastPendingConnection.iEncryptMode = aConn.iEncryptMode;
			    // Return now as we are waiting and don't want to 'RejectConnection'
				return;
				}

			// If here then the cache has either informed us that the device is paired
 			// or not.  We only accept paired connections.
 			accept = (pairingState == CBTPairingsCache::EPaired) ? ETrue : EFalse;
			}
		if (accept)
			{
			// this may mean the physical link's acceptance is determined by the
			// ACL Logical link - but that's what the spec implies :-)
			accept = static_cast<MLogicalLink*>(listener->iObserver)->ConnectRequest(aConn, *this);
			// since we only support one SCO listener at present we dont need to mark it as the acceptor
			// or indeed find a specific one: later we could choose via CoD
			}
		}

	if (accept)
		{

		// store CoD now, not told it ever again! if connection fails this object will go
		// Don't update CoD if it is 0 (NULL) as this is the default.
		// This also prevents a valid CoD being overwritten.
		if (aConn.iCoD != 0)
			{
			iDevice.SetDeviceClass(aConn.iCoD);
			}

		TUint8 roleSwitch = static_cast<TUint8>(iLinksMan.PassiveConnectBecomeMaster() ? 0x00 : 0x01);

		TInt err;
		if (aConn.iLinkType == EeSCOLink)
			{
			TBTeSCOLinkParams options;

			CeSCOLink* eSCO = static_cast<CeSCOLink*>(listener->iObserver);
			TUint16 packetMask = eSCO->GetPacketMask();
			eSCO->GetExtOptions(options);

			TRAP(err, iLinksMan.HCIFacade().AcceptSynchronousConnectionL(aConn.iBdaddr,
				options.iBandwidth.iTransmit, options.iBandwidth.iReceive,
				options.iLatency, options.iCoding, options.iRetransmissionEffort,
				packetMask
				));
			}
		else
			{
			TRAP(err, iLinksMan.HCIFacade().AcceptConnectionRequestL(aConn.iBdaddr, roleSwitch));
			}

		if (err == KErrNone)
			{
			// assume we're slave until told otherwise on ACL(=PHY) links
			if (aConn.iLinkType == EACLLink)
				{
				// this is a PHY connect request too, so we should store our role
				//The handle in 'aConn' is bogus, so..
				iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending);
				iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultPacketType, aConn.iLinkType);
				iLinkState.SetLinkRole((roleSwitch == 0x01) ? ESlave : EMaster);

				// have to store it now, since the complete wont tell us anything!
				}
			else
				{
				//The handle in 'aConn' is bogus, so..
				iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultSCOPacketType, aConn.iLinkType);
				}
			}
		else
			{
			// Out of memory:
			// Cause newly created spawned SAP (whether ACL or SCO)
			// to die.
			// Attempt to reject connection with newly freed memory...
			// ...this of course may fail in which case we have
			// to rely on supervison timeouts.
			if (aConn.iLinkType == EACLLink)
				{
				TInt last = iACLLogicalLinks.Count() - 1;
				if(last>=0)
					{
					iACLLogicalLinks[last]->Error(err);
					}
				}
			else
				{
				if(iSyncLogicalLink)
					{
					iSyncLogicalLink->Error(err);
					}
				}
			RejectConnection(aConn);
			}
		}
	else
		{
		// reject
		RejectConnection(aConn);
		}

	// Tell inquiry manager - should have a valid CoD within 'aConn'
	// Don't update CoD if it is 0 (NULL) as this is the default.
	// This also prevents a valid CoD being overwritten.
	if (aConn.iCoD != 0)
		{
		iLinksMan.LinkManagerProtocol().InquiryMgr().CoDResult(aConn.iBdaddr, aConn.iCoD);
		}
	}

void CPhysicalLink::RejectConnection(const TBTConnect& aConn)
	{
	LOG_FUNC
	if (aConn.iLinkType == EeSCOLink)
		{
		TRAP_IGNORE(iLinksMan.HCIFacade().RejectSynchronousConnectionL(aConn.iBdaddr, KDefaultRejectReason));
		}
	else
		{
		TRAP_IGNORE(iLinksMan.HCIFacade().RejectConnectionRequestL(aConn, KDefaultRejectReason));
		}
	}


void CPhysicalLink::GetDeviceFromRegistryL()
	{
	LOG_FUNC
	CBTDeviceGetter* getter = CBTDeviceGetter::NewL(iRegSess, *this);
	iRegistryHelpers.AddLast(*getter);
	getter->Start(BDAddr());		// now get the rest of the details
	// just BDAddr?
	}

TInt CPhysicalLink::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const
	{
	LOG_FUNC
	// good stuff here!
	if (aLevel == KSolBtLM)
		{
		switch (aName)
			{
			case KLMGetBasebandHandle:
				{
				if (aOption.Length() != sizeof(THCIConnHandle))
					{
					return KErrArgument;
					}
				aOption = TPtrC8(reinterpret_cast<const TUint8*>(&iHandle), sizeof(THCIConnHandle));
				return KErrNone;
				}

			case EBBGetSniffInterval:
				if (aOption.Length() != sizeof(TBasebandTime))
					{
					return KErrArgument;
					}
				aOption = TPtrC8(reinterpret_cast<const TUint8*>(&iSniffInterval), sizeof(TBasebandTime));
			    return KErrNone;

			default:
				return KErrNotSupported;
			}
		}
	else
		{
		return KErrNotSupported;
		}
	}

TInt CPhysicalLink::Arbitrate(TBool aImmediately, TBool aLocalPriority)
	{
	LOG_FUNC
	if (!IsConnected())
		return KErrDisconnected;

	if ( aImmediately )
		{
		iArbitrationDelay->Cancel();
        return DoArbitrate(aLocalPriority);		
		}
	else if (iArbitrationDelay->IsActive())
		{
		return KErrNone;
		}
    else
        {
        iArbitrationDelay->Start(aLocalPriority);
        return KErrNone;
        }
    }

TInt CPhysicalLink::DoArbitrate(TBool aLocalPriority)
    {
    if (!IsConnected())
        {
        return KErrDisconnected;
        }
	    
	//start arbitrate process with what our local controller supports
	TUint8 allowedModesMask = EHoldMode | EParkMode | ESniffMode; // local features sorted out later
	TBool roleSwitchAllowed = EFalse;

 	if (iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally() && iLinksMan.RoleSwitchAllowed())
 		{
 		roleSwitchAllowed = ETrue;
 		}

	// ask proxies what they want from the PHY
 	TUint8 requestedModeMask = 0;
 	TUint8 requestedMode = 0;
 	TBool activeModeIsRequested = EFalse;
	TSglQueIter<CBTProxySAP> iter(iProxySAPs);
	while (iter)
		{
		CBTProxySAP* proxy = iter++;

 		requestedMode = proxy->GetRequestedModes();
 		requestedModeMask |= requestedMode;

		if (requestedMode == EActiveMode && proxy->RequestedActiveMode())
 			{
 			// An Active Mode request will override all other local low power mode requests
 			// but continue to collect the requirement from the other proxies..
 			activeModeIsRequested = ETrue;
 			}

		allowedModesMask &= proxy->GetAllowedModes();
		roleSwitchAllowed &= proxy->IsRoleSwitchAllowed();
		}

 	if (activeModeIsRequested)
 		{
 		// Any Active Mode request will override all other low power mode requests,
 		// so overwrite the requestedModeMask but keep allowedModesMask and roleSwitchAllowed
 		// as specified by all the local proxies
 		requestedModeMask = EActiveMode;
 		}

	// clear out modes not supported by local Controller
	allowedModesMask &= iLinksMan.LinkManagerProtocol().ModesSupportedLocally();

	if(iOverrideParkRequests)
		{
		// We wish to ensure the physical link is not in PARK mode.
		// The only way to guarantee this is to disallow PARK via the link policy settings.
		allowedModesMask &= ~EParkMode;
		}

	if(allowedModesMask != iLinkPolicy.LowPowerModePolicy()
		|| roleSwitchAllowed != iLinkPolicy.IsSwitchAllowed())
		{
		// Controller policy for the connection needs updating
		SetModesAllowed(allowedModesMask, roleSwitchAllowed);
		}

	//If OverrideLPM flag is set, we do not disable LP modes via the link policy settings
	//This is done because OverrideLPM should not prevent remotes putting us into an LPM
	//Later on, when OverrideLPM flag is cancelled, it would allow us to enforce requested LPM
	if(iOverrideLPMRequests)
		{
		// We need to ensure the physical link is in active mode.
		allowedModesMask = EActiveMode;
		}

	TUint8 modeChangeMask = static_cast<TUint8>(requestedModeMask & allowedModesMask);
	TUint8 modeCompareMask = 0;

	if(aLocalPriority)
		{
		// If we want local priority, we go with what we want to do
		// irrespective of what the remote may have previously requested.
		modeCompareMask = modeChangeMask;
		}
	else
		{
 		// This logic allows us to see if the current low power mode has recently
 		// changed NOT because of a change in the local proxies' requests, but (probably)
 		// because a remote device has changed it..

		// modeCompareMask should start only having zero bits where
		// requestedModeMask has a zero bit and iPreviousRequestedModeMask does not
		// i.e. a mode is newly no longer requested.
		modeCompareMask = requestedModeMask | ~iPreviousRequestedModeMask;

		// Remove bits from modeCompareMask that are not in allowedModesMask
		// We cannot stay in a power mode that we do not allow.
		modeCompareMask &= allowedModesMask;
		}

	iPreviousRequestedModeMask = requestedModeMask; // Update previous requested to current value.

	TUint8 currentModeMask = static_cast<TUint8>(iLinkState.LinkMode());
	if(modeCompareMask & currentModeMask)
		{
		// The current state is the same as the permitted required role(s).
		return KErrNone;
		}

	if(modeChangeMask == EActiveMode && currentModeMask != EActiveMode)
		{
		// The current low power mode should be exited.
		return RequestActive();
		}

	if(modeChangeMask != EActiveMode)
		{
		if(currentModeMask != EActiveMode)
			{
			// The system is currently in a low power mode.  Exit this before
			// entering the new mode.
			TInt rerr = RequestActive();
			if(rerr != KErrNone)
				{
				return rerr;
				}
			}

		if(modeChangeMask & EHoldMode)
			{
			return RequestHold();
			}
		if(modeChangeMask & ESniffMode)
			{
			return RequestSniff();
			}
		if(modeChangeMask & EParkMode)
			{
			return RequestPark();
			}
		}

	// This point in the code is reached if the Link Policy settings are
	// changed but the mode is not.	 Return OK error code.
	return KErrNone;
	}

void CPhysicalLink::SetPassKey(const TDesC8& aPassKey)
	{
	LOG_FUNC
	// We store the key for use if it succeeds.
	iNewPinCode.Copy(aPassKey);
	iNewPinCodeValid = ETrue;
	}

const TBTPinCode& CPhysicalLink::PassKey() const
	{
	LOG_FUNC
	return iDevice.PassKey();
	}

void CPhysicalLink::StartArbitrationTimer() const
	{
	LOG_FUNC
	iArbitrationDelay->Start();
	}

TInt CPhysicalLink::Connect(TBasebandPageTimePolicy aPolicy)
	{
	LOG_FUNC
	// assume that we will be master until told otherwise
	ASSERT_DEBUG(!IsConnected());

	UpdateFromInquiryCache();

	TUint8 psrm = iDevice.IsValidPageScanRepMode() ? iDevice.PageScanRepMode() : TUint8(KDefaultBluetoothPageScanRepMode);
	TUint8 psm = iDevice.IsValidPageScanMode() ? iDevice.PageScanMode() : TUint8(KDefaultBluetoothPageScanMode);
	TUint16 clockOffset = iDevice.IsValidClockOffset() ? iDevice.ClockOffset() : TUint16(KDefaultBluetoothClockOffset);

	TUint8 allowRoleSwitch = static_cast<TUint8>(iLinksMan.ActiveConnectRoleSwitchAllowed());

	TUint16 pkt = KHCIDefaultPacketType;

	// optimise paging (as a best-effort attempt).
	TBasebandTime pagetimeout = CalculatePageTimeout(aPolicy, psrm, clockOffset & KHCIClockOffsetValidBit);
	iLinksMan.TryToChangePageTimeout(pagetimeout);

	TRAPD(ret, iLinksMan.HCIFacade().ConnectL(iDevice.Address(), pkt, psrm, psm, clockOffset, allowRoleSwitch));
	if(ret==KErrNone)
		{
		iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending);
		iLinksMan.Baseband().UpdateModel(iDevice.Address(), pkt, EACLLink);
		iLinkState.SetLinkRole(EMaster);
		}

	return ret;
	}

TInt CPhysicalLink::SCOConnect()
/**
	A utility service provided to the SCO transport
	We bring up a "default" SCO link, choosing the packet based on what we
	know the controllers can support
*/
	{
	LOG_FUNC
	return SCOConnect(EAnySCOPacket);
	}

TInt CPhysicalLink::SCOConnect(const TUint16 aUserHVPacketTypes)
/**
	A utility service provided to the SCO transport
	We bring up a SCO link which allows only the user specified packet types
	Implementation of SCOSAP::SetOption guarantees aUserHVPacketTypes contains at
	least one valid SCO packet type
*/
	{
	LOG_FUNC
	TBTSCOPackets scoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SCOPackets();

	scoPackets &= aUserHVPacketTypes;
	// combine with remote supported packets
	scoPackets &= iRemoteFeatures.SCOPackets();

	TInt ret;

	if (!scoPackets)
		ret = KErrNotSupported;
	else
		{
		// we put all in for now - if HW doesn't support that, the HCI can be changed
		OverridePark();

		TRAP(ret, iLinksMan.HCIFacade().SCOConnectL(Handle(), scoPackets, BDAddr()));
		if(ret==KErrNone)
			{
			iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, scoPackets, ESCOLink);
			}
		}
	return ret;
	}

TInt CPhysicalLink::SynchronousConnect(TUint aTransmitBandwidth, TUint aReceiveBandwidth,
		TUint16 aMaxLatency, TUint16 aVoiceSettings,
		TUint8 aRetransmissionEffort, const TBTSyncPacketTypes aUserPacketTypes)
/**
	A utility service provided to the eSCO transport
	We bring up a eSCO link which allows only the user specified packet types
	Implementation of SCOSAP::SetOption guarantees aUserPacketTypes contains at
	least one valid SCO packet type
*/
	{
	LOG_FUNC
	TBTSyncPacketTypes escoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SyncPackets();

	escoPackets &= aUserPacketTypes;
	// combine with remote supported packets
	escoPackets &= iRemoteFeatures.SyncPackets();

	TInt ret;

	if (!escoPackets)
		ret = KErrNotSupported;
	else
		{
		// we put all in for now - if HW doesn't support that, the HCI can be changed
		// THCIConnHandle aACLHandle, TUint aTransmitBandwidth, TUint aReceiveBandwidth,
		//	TUint16 aMaxLatency, TUint16 aVoiceSettings, TUint8	 aRetransmissionEffort,
		//	TUint16 aPacketTypeMask
		TRAP(ret, iLinksMan.HCIFacade().SetupSynchronousConnectionCommandL(
			Handle(),
			aTransmitBandwidth, aReceiveBandwidth,
			aMaxLatency, aVoiceSettings, aRetransmissionEffort,
			escoPackets,
			BDAddr()
			));
		if(ret==KErrNone)
			{
			iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, escoPackets, EeSCOLink);
			}
		}
	return ret;
	}

TInt CPhysicalLink::SubscribeProxySAP(CBTProxySAP& aProxySAP)
	{
	LOG_FUNC
	// a new Proxy SAP wishes to attach to this physical SAP
#ifdef _DEBUG
	// check to see if the same proxy is already there
	TSglQueIter<CBTProxySAP> iter(iProxySAPs);
	while (iter)
		{
		ASSERT_DEBUG(iter++!=&aProxySAP);
		}
#endif
	SubscribeLinkObserver(aProxySAP);
	iProxySAPs.AddLast(aProxySAP);
	Arbitrate();
	return KErrNone;
	}

void CPhysicalLink::UnsubscribeProxySAP(CBTProxySAP& aProxySAP)
	{
	LOG_FUNC
	ASSERT_DEBUG(!iProxySAPs.IsEmpty());
	iProxySAPs.Remove(aProxySAP);
	UnsubscribeLinkObserver(aProxySAP);
	Arbitrate(); // good time to see what phy properties should be used
	PhysicalLinkUserIdle();
	}


TInt CPhysicalLink::AttachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP)
	{
	// effectively this part of this class encapsulates the Logical Transports
	// but they're not really worthy of a class - just some understanding that they're
	// not ignored
	LOG_FUNC
	TInt retVal = KErrNone;

	switch (aLink)
		{
		case EACLLink:
			{
			// it is important for error handling at the end of
			// CPhysicalLink::ConnectionRequest that this remains an
			// append i.e. 'aSAP' is added to the end of the queue
			retVal = iACLLogicalLinks.Append(static_cast<CACLLink*>(&aSAP));
			break;
			}

		case ESCOLink:
			{
			if (!(iRemoteFeatures.IsSCOLinkSupported()))
				retVal = KErrNotSupported;
			else
				{
				// Don't attach SCO if already exists, avoids confusion with some h/w.
				// .....return the outcome, higher levels depend on this action
				if (iSyncLogicalLink)
					{
					retVal = KErrInUse;
					}
				else
					{
					iSyncLogicalLink = static_cast<CSCOLink*>(&aSAP);
					}
				}
			break;
			}

		case EeSCOLink:
			{
			if (!(iRemoteFeatures.IsExtendedSCOLinkSupported()))
				retVal = KErrNotSupported;
			else
				{
				if (iSyncLogicalLink)
					{
					retVal = KErrInUse;
					}
				else
					{
					iSyncLogicalLink = static_cast<CeSCOLink*>(&aSAP);
					}
				}
			break;
			}

		default:
			Panic(EBTUnknownLogicalLink);
		}

	if (retVal == KErrNone)
		{
		RemoveIdleTimer();
		}

	return retVal;
	}

void CPhysicalLink::DetachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP)
	{
	LOG_FUNC
	LOG3(_L("CPhysicalLink: Logical Link type %d, 0x%08x detaching from PHY 0x%08x"), aLink, &aSAP, this);
	switch (aLink)
		{
		case EACLLink:
			{
#ifdef _DEBUG
			TInt numFound = 0;
#endif
			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
				{
				if (iACLLogicalLinks[i] == &aSAP)
					{
#ifdef _DEBUG
					numFound++;
#endif
					iACLLogicalLinks.Remove(i); // don't delete - it's not ours!
					}
				}
			__ASSERT_DEBUG(numFound==1, Panic(EBTACLLogicalLinkBadDebind));
			break;
			}

		case ESCOLink:
		case EeSCOLink:
			{
			__ASSERT_DEBUG(iSyncLogicalLink==&aSAP, Panic(EBTSCOLogicalLinkBadDebind));
			iSyncLogicalLink = NULL;
			break;
			}

		default:
			Panic(EBTUnknownLogicalLink);
		}
	}

void CPhysicalLink::PhysicalLinkUserIdle()
	{
	LOG_FUNC
	if(IsPhysicalLinkIdle() && iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp)
		{
		// try to stash device details - juice and last used date
		TTime t;
		t.UniversalTime();
		iDevice.SetUsed(t);
		TRAPD(ignore, StoreDeviceL(ETrue)); //ETrue: new meaning - prevent addition of device in registry

		if (!iIdleTimerQueued)
			// we don't want to send the read clock offset command more than one
			// here, so just check if we already passed from here.
			{
			// Take this opportunity to get latest info on clock offset
			// so that we can stash it in HR - subsequent connections may then be quicker
			TRAP(ignore, iLinksMan.HCIFacade().ReadClockOffsetL(iHandle));
			// ignore error - was an optimisation

			// Disconnect the PHY, it is no longer required.
			QueueIdleTimer(KPhysicalLinkIdleTimeout);
			}
		}
	}


TBool CPhysicalLink::IsPhysicalLinkIdle() const
	{
	LOG_FUNC
	TBool physicalLinkIdle = ETrue;
	// Check ACL and SCO links.	 If any link is not idle, then the PHY remains
	// active.
	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
		{
		physicalLinkIdle &= iACLLogicalLinks[i]->IsIdle();
		}

	if(iSyncLogicalLink)
		{
		physicalLinkIdle &= iSyncLogicalLink->IsIdle();
		}

 	if (iPinRequester)
 		{
 		physicalLinkIdle = EFalse;
 		}

 	if (IsAuthenticationPending())
 		{
 		physicalLinkIdle = EFalse;
 		}

	return physicalLinkIdle;
	}


void CPhysicalLink::QueueIdleTimer(TInt aTime)
/**
	Queue idle timer entry.
	When this timer expires, it'll call TryToClose, which actually
	causes the thing to finally close down.
**/
	{
	LOG_FUNC
	if (!iIdleTimerQueued)
		{
		TCallBack cb(TerminateCallback, this);
		iIdleTimerEntry.Set(cb);
		iIdleTimeout = aTime;
		BTSocketTimer::Queue(iIdleTimeout * 1000000, iIdleTimerEntry);
		iIdleTimerQueued = ETrue;
		}
	}


void CPhysicalLink::RemoveIdleTimer()
/**
	Called whenever we're opened.
	Checks there are no idle timer entries queued.
**/
	{
	LOG_FUNC
	if (!iIdleTimerQueued)
		{
		// it's fine for callers to just try to remove the timer
		return;
		}
	BTSocketTimer::Remove(iIdleTimerEntry);
	iIdleTimerQueued = EFalse;
	}

void CPhysicalLink::QueueLPMOverrideTimer(TInt aTimeout)
/**
	Queue LPM Override timer entry.
	When this timer expires, it'll call UndoLPMOverride.
**/
	{
	LOG_FUNC
	__ASSERT_DEBUG(aTimeout!=0, Panic(EBTPhysicalLinksInvalidArgument));
	if (iLPMOverrideTimerQueued)
		{
		BTSocketTimer::Remove(iOverrideLPMTimerEntry);
		}
	BTSocketTimer::Queue(aTimeout, iOverrideLPMTimerEntry);
	iLPMOverrideTimerQueued = ETrue;
	}

void CPhysicalLink::NotifyStateChange(TBTBasebandEventNotification& aEvent)
	{
	LOG_FUNC
	// If this event is 'physical link down' / 'physical link error' then any ProxySAP
	// subscribers will unsubscribe as a result of PhysicalLinkChange being called.
	// This will result in the entry being removed from the iBasebandSubscribers array.
	// Consequently the array must be traversed from end to start.
	TDblQueIter<TPhysicalLinkObserverQLink> iter(iBasebandSubscribers);
	TPhysicalLinkObserverQLink* l;
	while ((l=iter++)!=NULL)
		{
		l->Item()->PhysicalLinkChange(aEvent, *this);
		}

	if(((aEvent.EventType() & ENotifyActiveMode) ||
		(aEvent.EventType() & ENotifySniffMode)||
		(aEvent.EventType() & ENotifyParkMode)||
		(aEvent.EventType() & ENotifyHoldMode)) &&
		(aEvent.ErrorCode() == KErrNone))
		{
		iArbitrationDelay->Start();
		}
	}

/*static*/ TInt CPhysicalLink::TerminateCallback(TAny* aCPhysicalLink)
	{
	LOG_STATIC_FUNC
	CPhysicalLink* c = reinterpret_cast<CPhysicalLink*>(aCPhysicalLink);
	c->iIdleTimerQueued = EFalse;

	TInt retVal = EFalse;
	
	// Queue another idle timer in case this disconnect fails
	
	// Check if the physical link is still idle after the timer.
	if(c->IsPhysicalLinkIdle() && c->iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp)
		{
		// Increase the idle time for the next callback by just over 50%
		TInt newIdleTime = (c->iIdleTimeout * 3)/2 + 1;
		if (newIdleTime > KMaxPhysicalLinkIdleTimeout)
			{
			newIdleTime = KMaxPhysicalLinkIdleTimeout;
			}

		c->QueueIdleTimer(newIdleTime);
		if (c->Terminate(ERemoteUserEndedConnection) == KErrNone)
			{
			retVal = EFalse;
			}
		else
			{
			retVal = ETrue;
			}
		}
	return retVal;
	}

TInt CPhysicalLink::Terminate(THCIErrorCode aReason)
	{
	LOG_FUNC
	TInt err = KErrNone;

	if (iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending)
		{
		// If the Link is not yet up then we cannot know the correct connection handle
		// to disconnect so remember the request so we disconnect the link straight away
		// after it does come up.
		iDisconnectRequested = ETrue;
		}
	else
		{
		__ASSERT_DEBUG(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp, Panic(EInvalidLinkStateDuringDisconnect));

		TRAP(err, iDisconnectCtrl.Disconnect(aReason));
		}

	return err;
	}

void CPhysicalLink::SetModesAllowed(TUint8 aModesAllowed, TBool aRoleSwitchAllowed)
	{
	LOG_FUNC
	TBTLinkModeSet supportedModes = iRemoteFeatures.LinkModes();
	aModesAllowed &= supportedModes;
	aRoleSwitchAllowed &= IsRoleSwitchSupported();

	if(aModesAllowed != iLinkPolicy.LowPowerModePolicy() ||
	   aRoleSwitchAllowed != iLinkPolicy.IsSwitchAllowed())
		{
		TLinkPolicy tempPolicy;
		tempPolicy.SetModesAllowed(aModesAllowed);
		tempPolicy.SetSwitchAllowed(aRoleSwitchAllowed);

		TRAPD(err, iLinksMan.HCIFacade().WriteLinkPolicySettingsL(Handle(), tempPolicy.LinkPolicy()));

		if (err==KErrNone)
			{
			iLinkPolicy = tempPolicy;
			}
		}
	}

void CPhysicalLink::NotifyLogicalLinkDown(TPhysicalLinkPort aPort)
	{
	LOG_FUNC
	switch (aPort)
		{
		case EACLLink:
			{
			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
				{
				// subscribers will unsubscribe as a result of this
				// This will result in the entry being removed from the iACLLogicalLinks array.
				// Consequently the array must be traversed from end to start.
				iACLLogicalLinks[i]->Disconnection();
				}
			break;
			}
		case ESCOLink:
		case EeSCOLink:
			{
			if (iSyncLogicalLink)
				{
				iSyncLogicalLink->Disconnection();
				}
			break;
			}

		default:
			Panic(EBTPhysicalLinkBadPort);
		}
	}

void CPhysicalLink::NotifyLogicalLinkUp(const TBTConnect& aConnect)
	{
	LOG_FUNC
	switch (aConnect.iLinkType)
		{
		case EACLLink:
			{
			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
				{
				// all of them are open
				iACLLogicalLinks[i]->ConnectComplete(aConnect);
				}
			break;
			}

		case ESCOLink:
		case EeSCOLink:
			{
			if (iSyncLogicalLink)
				{
				iSyncLogicalLink->ConnectComplete(aConnect);
				}
			break;
			}

		default:
			Panic(EBTPhysicalLinkBadPort);
		}
	}

void CPhysicalLink::NotifyLogicalSyncLinkUp(const TBTConnect& aConnect, const TBTSyncConnectOpts& aSyncOpts)
	{
	LOG_FUNC
	switch (aConnect.iLinkType)
		{
		case ESCOLink:
		case EeSCOLink:
			{
			if (iSyncLogicalLink)
				{
				iSyncLogicalLink->SyncConnectComplete(aConnect, aSyncOpts);
				}
			break;
			}

		default:
			Panic(EBTPhysicalLinkBadPort);
		}
	}


void CPhysicalLink::NotifyLogicalLinkError(TPhysicalLinkPort aPort, TInt aError)
	{
	LOG_FUNC
	switch (aPort)
		{
		case EACLLink:
			{
			for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
				{
				// all of them are open
				iACLLogicalLinks[i]->Error(aError);
				}
			break;
			}

		case ESCOLink:
		case EeSCOLink:
			{
			if (iSyncLogicalLink)
				{
				iSyncLogicalLink->Error(aError);
				}
			break;
			}

		default:
			Panic(EBTPhysicalLinkBadPort);
		}
	}


TBool CPhysicalLink::IsModeSupportedRemotely(TBTLinkMode aMode) const
	{
	LOG_FUNC
	return iRemoteFeatures.LinkModes() & aMode;
	}

TBool CPhysicalLink::IsEncryptionPauseResumeSupported() const
	{
	LOG_FUNC
	return (iLinksMan.LinkManagerProtocol().IsEncryptionPauseResumeSupportedLocally() && iRemoteFeatures.IsEncryptionPauseResumeSupported());
	}

TBool CPhysicalLink::IsEncryptionSupported() const
	{
	LOG_FUNC
	return (iRemoteFeatures.IsEncryptionSupported() && iLinksMan.LinkManagerProtocol().IsEncryptionSupportedLocally());
	}

TBool CPhysicalLink::IsRoleSwitchSupported() const
	{
	LOG_FUNC
	// the physical link _can_ switch when encryption is on - see RequestChangeRole
	return (iRemoteFeatures.IsRoleSwitchSupported() && iLinksMan.RoleSwitchAllowed());
	}


//
// Change the packet types allowed on the link.
// Note that this is only suitable for use on ACL and old-style (pre eSCO) SCO links.
//
TInt CPhysicalLink::ChangeConnectionPacketType(TUint16 aType)
	{
	LOG_FUNC
	TInt rerr = KErrNone;

	// Build a mask of locally supported packet types (we shouldn't care about
	// what the remote supports (this is alluded to in the spec) probably
	// because they can change too.

	// DM1, DH1 and HV1 packets are always supported.
	TUint16 supportedTypes = EPacketsDM1 | EPacketsDH1 | EPacketsHV1;

	// get extra locally-supported packets (our controller may not support things)
	TBTFeatures localPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally();

	// 3 slot ACL packets
	if (localPackets[ESupportedThreeSlotPacketsBit])
		{
		supportedTypes |= (EPacketsDM3 | EPacketsDH3);
		}

	// 5 slot ACL packets
	if (localPackets[ESupportedFiveSlotPacketsBit])
		{
		supportedTypes |= (EPacketsDM5 | EPacketsDH5);
		}

	// HV2 single-slot packets
	if (localPackets[ESupportedHV2PacketsBit])
		{
		supportedTypes |= EPacketsHV2;
		}

	// HV3 single-slot packets
	if (localPackets[ESupportedHV3PacketsBit])
		{
		supportedTypes |= EPacketsHV3;
		}

	// Note: If one of the following EDR bits is set it indicates that the EDR
	// packet type shall NOT be used on this physical link. However, we must
	// not set any packet types that are not supported, so check first.

	// EDR 2Mbps
	if (localPackets[EEDR_ACL_2MbpsModeBit])
		{
		supportedTypes |= EPackets2_DH1;
		// EDR 3 slot ACL packets
		if (localPackets[ESupportedEDRThreeSlotPacketsBit])
			{
			supportedTypes |= EPackets2_DH3;
			}
		// EDR 5 slot ACL packets
		if (localPackets[ESupportedEDRFiveSlotPacketsBit])
			{
			supportedTypes |= EPackets2_DH5;
			}

		// EDR 3Mbps - Have to have 2Mbps to have 3Mbps
		if (localPackets[EEDR_ACL_3MbpsModeBit])
			{
			supportedTypes |= EPackets3_DH1;
			// EDR 3 slot ACL packets
			if (localPackets[ESupportedEDRThreeSlotPacketsBit])
				{
				supportedTypes |= EPackets3_DH3;
				}
			// EDR 5 slot ACL packets
			if (localPackets[ESupportedEDRFiveSlotPacketsBit])
				{
				supportedTypes |= EPackets3_DH5;
				}
			}
		}

	supportedTypes &= aType;
	// Check if the new supported types differ from the current configuration.
	if(supportedTypes != iLinkState.PacketTypes())
		{
		rerr = iLinksMan.HCIFacade().ChangeConnectionPacketType(iHandle, supportedTypes);
		}

	if(rerr == KErrNone)
		{
		iConnectionPacketTypeChanged = ETrue;
		}

	return rerr;
	}

TInt CPhysicalLink::ExitMode(TBTLinkMode aMode)
	{
	LOG_FUNC
	return iLowPowModeCtrl.ExitMode(aMode, iHandle);
	}

TInt CPhysicalLink::RequestMode(TBTLinkMode aMode)
	{
	LOG_FUNC
	if (!IsConnected())
		return KErrDisconnected;

	// if active mode is required, try to exit whatever Low Power mode we are in -
	// if neither sniff nor park nothing will happen.
	if(aMode == EActiveMode)
		{
		return ExitMode(iLinkState.LinkMode());
		}

	// now request this connection goes to requested mode
	return iLowPowModeCtrl.ChangeMode(aMode, iHandle);
	}

TInt CPhysicalLink::RequestChangeRole(TBTBasebandRole aRole)
	{
	LOG_FUNC
	TInt err = KErrNone;

	// Check that the requested role is not the current role.
	if(iLinkState.LinkRole() != aRole)
		{
		if(!iLinkPolicy.IsSwitchAllowed())
			{
			err = KErrNotSupported;
			}

		// 1) Role switch is not allowed if there is a sync connection
		//    (or there is a sync connection negotiation in progress)
		else if (HasSyncLink() || SyncConnectPending())
			{
			err = KErrNotReady;
			}

		// 2) Check if role switch is supported on the remote device
		else if (!IsRoleSwitchSupported())
			{
 			err = KErrNotSupported;
			}

		// 3) Check if role switch is supported locally
 		else if (!iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally())
 			{
 			err = KErrNotSupported;
 			}

		// Same role request is already with LinkMgr, or most probably running just now
		else if (iRoleSwitcher)
			{
			LOG(_L("CPhysicalLink - Same role change in progress!"))
			err = KErrNone;
			}

		// ok to change role
		// we kick off a role switcher state machine, to remove LPM and encryption, prior to
		// role switch
		else
			{
			TRAP(err, iRoleSwitcher = CRoleSwitcher::NewL(iLinksMan, *this, aRole));
			}
		}
#ifdef _DEBUG
	else
		{
		LOG(_L("CPhysicalLink We are in requested role!!!"))		
		}
#endif

	return err;
	}

void CPhysicalLink::AsyncDeleteRoleSwitcher()
	{
	LOG_FUNC
	iRoleSwitchCompleteCallBack->CallBack();
	}

/*static*/ TInt CPhysicalLink::RoleSwitchCompleteCallBack(TAny* aPhysicalLink)
	{
	LOG_STATIC_FUNC
	CPhysicalLink* physicalLink = static_cast<CPhysicalLink*>(aPhysicalLink);
	physicalLink->DeleteRoleSwitcher();
	return EFalse;
	}

void CPhysicalLink::DeleteRoleSwitcher()
	{
	LOG_FUNC
	delete iRoleSwitcher;
	iRoleSwitcher = NULL;
	}

TBool CPhysicalLink::IsEncryptionDisabledForRoleSwitch() const
/**
	If link is encrypted, but role switcher temporarily disabled encryption, returns true.
**/
	{
	LOG_FUNC
	if (iRoleSwitcher)
		{
		return iRoleSwitcher->IsEncryptionDisabledForRoleSwitch();
		}
	return EFalse;
	}

TInt CPhysicalLink::OverridePark()
/**
	A request has come in that requires us to ensure we are not parked.

	Try to make it happen
**/
	{
	LOG_FUNC
	iOverrideParkRequests = ETrue;

	// Only arbitrate if Park mode is currently active.
	TInt rerr = KErrNone;
	if(iLinkState.LinkMode() & EParkMode)
		{
		rerr = Arbitrate();
		}
	return rerr;
	}

TInt CPhysicalLink::UndoOverridePark()
/**
	A need to ensure we are not parked has gone.

	Try to go back to power mode resulting from an unoverriden Arbitrate
**/
	{
	LOG_FUNC
	iOverrideParkRequests = EFalse;

	//Arbitrate even if there isn't an outstanding local park mode request beacuse
	//the remote device may be requesting park mode.
	return Arbitrate();
	}

TInt CPhysicalLink::OverrideLPMWithTimeout(TUint aTimeout)
	{
	LOG_FUNC
	if(aTimeout == 0)
		{
		return KErrNone; //facility not wanted
		}

	TInt rerr = OverrideLPM();
	QueueLPMOverrideTimer(aTimeout);

	return rerr;
	}

TInt CPhysicalLink::OverrideLPM()
/**
	A request has come in that requires us to ensure we are not using
	an LPM (low power mode).

	Try to make it happen
**/
	{
	LOG_FUNC
	iOverrideLPMRequests = ETrue;

	return Arbitrate();
	}

/*static*/ TInt CPhysicalLink::OverrideLPMTimeoutCallback(TAny* aCPhysicalLink)
	{
	LOG_STATIC_FUNC	
	CPhysicalLink* c = reinterpret_cast<CPhysicalLink*>(aCPhysicalLink);
	TInt err = c->UndoOverrideLPM();
	//we deliberately ignore this return value because we can't do anything to correct the error situation
	if (KErrNone != err)
		{
		LOG2(_L("Physical link: UndoOverrideLPM returned an error %d on the connection 0x%08x"), err, c);
		}
	c->iLPMOverrideTimerQueued = EFalse;
	return KErrNone;
	}

TInt CPhysicalLink::UndoOverrideLPM()
/**
	A need to ensure we are not in LPM has gone.

	Try to go back to power mode resulting from an unoverriden Arbitrate
**/
	{
	LOG_FUNC
	iOverrideLPMRequests = EFalse;

	// Call Arbitrate(...) to determine if removing the override requires any change to
	// the physical link state.
	return Arbitrate();
	}

// security stuff
// connection looks after this as it may be passively initiated and there wont
// be a accessrequester in those situations

void CPhysicalLink::PinRequest(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aRequester)
	{
	LOG_FUNC
	// If we receive a Pin_Request while another is outstanding then the controller is faulty. 
	// So we just ignore any multiple requests, as they do not make sense.
	// However, in UDEB we panic to raise awareness that the hardware is behaving incorrectly.
	__ASSERT_DEBUG(!iPinRequester, Panic(EBTConnectionPINRequestedTwice));

	SetAuthenticationPending(EPinRequestPending); // if not already set (because the remote initiated authentication).

	if (iPinRequester)
		{
		return;
		}

	__ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress));
	
	if(!IsPairable())
		{
		aRequester.PINCodeRequestNegativeReply(aAddr);
		return;
		}

	TBTPinCode pinCode;
	if(iLinksMan.PrefetchMan().IsPrefetchAvailable(aAddr, pinCode))
		{
		aRequester.PINCodeRequestReply(aAddr, pinCode);
		return;
		}

	iPinHandler = &aRequester;

	TRAPD(err, DoPinRequestL(aAddr, *this)); // physical links will proxy a request.

	// if there is an error we should reply PIN negative...
	if (err)
		{
		iPinHandler->PINCodeRequestNegativeReply(aAddr);
		iPinHandler = NULL;
		}
	}

void CPhysicalLink::DoPinRequestL(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aResponseHandler)
	{
	LOG_FUNC
	// Bluetooth is fairly poor at allowing name requests on partially connected links
	// this typically occurs in security mode 3

	// find out is it locally or remotley initiated authentication ...
	TBool locallyInitiated; //Authentication
	TBool strongKeyRequired;
	TUint minPasskeyLength=0;
	TUint recommendedPasskeyLength=0;

	iLinksMan.SecMan().GetPassKeyLengthAndOriginator(aAddr, minPasskeyLength, locallyInitiated, strongKeyRequired);

	/* If we have an activeAccessRequester and authentication was not actually required,
	 * this is a speculative authentication.
	 * In that case we did not expect to get a PinRequest. 
	 * We leave as the link already exists, causing the PinRequest to get a negative reply.
	 */
	if(iDevice.IsValidLinkKey())
		{
		CBTAccessRequester* activeAccessRequester = iLinksMan.SecMan().FindActiveAccessRequester(aAddr);
		if (activeAccessRequester)
			{
			if (!activeAccessRequester->AuthenticationRequired())
				{
				User::Leave(KErrAlreadyExists);
				}
			}
		}

	/* If link is already authenticated with a link key greater than the currently required minimum key length, then store 
	 * the current passkey length as a recommended length. If this recommendation is followed, then the strength of 
	 * the current link key will be maintained. This recommended length is made available via 
	 * TBTPinCodeEntryNotifierParams::RecommendedPinCodeMinLength().
	 */
	TUint devicePasskeyLength = iDevice.PassKeyLength();
	if(iDevice.IsValidLinkKey()&&devicePasskeyLength>minPasskeyLength)
		{
		//Ignore the previous link key length if it is not at least the minimum we require now anyway.
		recommendedPasskeyLength=devicePasskeyLength;
		}
	else
		{
		//Recommend the currently required minimum length.
		recommendedPasskeyLength=minPasskeyLength;
		}

	iPinRequester = CBTPinRequester::NewL(BDAddr(), aResponseHandler, iLinksMan.SecMan(),
										  minPasskeyLength, locallyInitiated, strongKeyRequired, recommendedPasskeyLength);
	}

TBool CPhysicalLink::LinkKeyRequestPending()
	{
	LOG_FUNC
	return iAuthStateMask & ELinkKeyRequestPending;
	}

void CPhysicalLink::SetAuthenticationPending(TUint8 aState)
	{
	LOG_FUNC
	iAuthStateMask |= aState;
	LOG1(_L("SetAuthenticationPending: set state [%d] in an authentication state mask"), aState);
	}

void CPhysicalLink::AuthenticationComplete(TUint8 aState)
	{
	LOG_FUNC
	iAuthStateMask &= ~aState;
	LOG1(_L("AuthenticationComplete: reset state [%d] in an authentication state mask"), aState);
	PhysicalLinkUserIdle();
	}

TBool CPhysicalLink::IsPairable() const
	{
	// Determines if pairing can be performed on the physical link;
	// it previously was determined by the PIN code notifier, but with
	// 2.1 pairable mode has been replaced with bondable mode and
	// so being pairable is now a function of the security level
	// the device is operating under.
	// Here the policy is as follows:
	//
	// Pairable if...
	// 1) Not in paired only connections mode
	// OR 
	// 2) The link is already authenticated
	// OR
	// 3) A logical link was locally initiated
	// OR
	// 4) A dedicated bonding attempt is in progress
	// (it might be better to migrate dedicated bonding above the logical
	// link to remove this special case...)
	//
	// This policy provides strong security for while not applying
	// blanket restricts on explicit user requests (i.e. outgoing
	// connections).
	TBool locallyInitiatedLogicalLink = EFalse;
	for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--)
		{
		if(iACLLogicalLinks[i]->IsLocallyInitiated())
			{
			locallyInitiatedLogicalLink = ETrue;
			}
		}
	return (!iLinksMan.IsAcceptPairedOnlyMode()) 
		|| iLinkState.Authenticated() 
		|| locallyInitiatedLogicalLink
		|| iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address());
	}

void CPhysicalLink::DeleteLinkKeyL()
/**
ensure that the LinkKey is removed from the device in the registry
**/
	{
	LOG_FUNC
	CBTLinkKeyDeleter* deleter = CBTLinkKeyDeleter::NewL(iRegSess, *this);
	iRegistryHelpers.AddLast(*deleter);
	deleter->Start(BDAddr());
	}

TBool CPhysicalLink::IsConnectionRequestPending() const
	{
	LOG_FUNC
	return iPendingConnection;
	}

void CPhysicalLink::PendingConnectionRequest(TInt /*aError*/)
	{
	LOG_FUNC
 	__ASSERT_DEBUG(iPendingConnection, Panic(EPhysicalLinkNoConnectionPending));

 	// Note: We could do something clever with the error code returned
 	// to prevent us thrashing in OOM situations, but it is complicated.
 	// We want to try again as now the pending connection address may
 	// now be in the cache even if we have a non-KErrNone error code,
 	// and so with that we will proceed sucessfully.  But if we are
 	// deferred we may end up here again and again with, perhaps with a
 	// KErrNoMemory for example ... which is how the thrashing will occur.
 	// This thrashing will be limited however by the "Connection Accept
 	// Timeout" imposed by the controller on the host, and for this
 	// reason we live with it.
	iPendingConnection = EFalse;
	ConnectionRequest(iLastPendingConnection);
	}

TBool CPhysicalLink::IsPasskeyMinLengthOK()
	{
	LOG_FUNC
/**
	check whether current passkey (retrieved from BTRegistry) is long enough to fulfill user passkey requirement
	It must be called only when iDevice is ready and has LinkKey
**/
	__ASSERT_DEBUG(iDeviceResult==KErrNone && iDevice.IsValidLinkKey(),
		 Panic(EBTPhysicalLinkNotAuthenticated));

	TUint newMinPasskeyLength=0;
	TBool isPasskeyMinLengthOK = ETrue;

	TBool locInit, strongKey;
	iLinksMan.SecMan().GetPassKeyLengthAndOriginator( iDevice.Address(), newMinPasskeyLength, locInit, strongKey);

	TBTPinCode currentPassKey = iDevice.PassKey();

	// check current PIN code length against user requirement
	if ( newMinPasskeyLength )
		{
		 if ( newMinPasskeyLength > currentPassKey().iLength )
			{
			// longer PIN code is requested
			isPasskeyMinLengthOK = EFalse;
			}
		}

	return isPasskeyMinLengthOK;
	}

void CPhysicalLink::LinkKeyRequest(const TBTDevAddr& aAddr, MLinkKeyResponseHandler& /*aRequester*/)
	{
	LOG_FUNC
	// we don't keep a copy of the device record - we just leave the one copy with
	// the baseband - it can tell us if there's a link key
	// we can tell if the baseband has the device record or not

	SetAuthenticationPending(ELinkKeyRequestPending); //authentication process started by remote

	// If the ACL to the peer device is not yet connected, and the peer has initiated
	// authentication then it must be in security mode 3.  This information is stored and
	// if the connection completes the link will be set as authenticated.
	if (!IsConnected())
		{
		iPeerInSecurityMode3 = ETrue;
		}

	if(!iPeerInSecurityMode3 && iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()))
		{
		// If we are doing DedicatedBonding then we should ignore the existing linkkey
		// in an attempt to generate a stronger one if possible.
		// Security mode 3 is a odd case - because we get what looks like double pairing (the remote
		// initiated pairing on connection, then the dedicated bonding pairing).  So we have removed
		// this feature for security mode 3 devices...they will have to suffer for their transgressions
		LOG(_L("CPhysicalLink: Dedicated bonding attempt - Sending link key request negative reply"));
		iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
		iRequireAuthenticatedLinkKey = EFalse;
		}
	else if (iDeviceResult==KErrNone && iDevice.IsValidLinkKey())
		{
		if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug)
			{
			LOG(_L("CPhysicalLink: Debug mode - Link to debug link key"))
			iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
			}
		else
			{
			if (iDevice.LinkKeyType() != ELinkKeyCombination)
				{
				if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable())
					{
					LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated"))
					iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
					}
				else
					{
					LOG(_L("CPhysicalLink: Issuing link key to HC now"))
					iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey());
					}
				}
			else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled)
				{
				LOG(_L("CPhysicalLink: Issuing link key to HC now"))
				iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey());
				}
			else
				{
				LOG(_L("CPhysicalLink: Current PIN code too short!"))
				iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
				}
			}
		iRequireAuthenticatedLinkKey = EFalse;
		}
	else if (iDeviceResult==KErrNone && !iDevice.IsValidLinkKey() || iDeviceResult==KErrNotFound)
		{
		LOG(_L("CPhysicalLink: No Link key available for the device"));
		iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr);
		iRequireAuthenticatedLinkKey = EFalse;
		}
	else
		{
		LOG(_L("CPhysicalLink: Waiting for link key from Registry!"))
		// we're still waiting for the device....we'll respond when it turns up
		iWaitingForLinkKeyFromRegistry = ETrue;
		}

	}

TInt CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin) const
	{
	ASSERT_DEBUG(aDevAddr == this->BDAddr());
	const_cast<CPhysicalLink*>(this)->PINCodeRequestReply(aDevAddr, aPin);
	return KErrNone;
	}

TInt CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr) const
	{
	ASSERT_DEBUG(aDevAddr == this->BDAddr());
	const_cast<CPhysicalLink*>(this)->PINCodeRequestNegativeReply(aDevAddr);
	return KErrNone;
	}

void CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin)
	{
	SetPassKey(aPin);
	PinRequestSent();
	iPinHandler->PINCodeRequestReply(aDevAddr, aPin);
	PinRequestComplete();
	}

void CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr)
	{
	iPinHandler->PINCodeRequestNegativeReply(aDevAddr);
	PinRequestComplete();
	}


void CPhysicalLink::PinRequestSent()
	{
	LOG_FUNC
	iLinkKeyPending = ETrue;
	}

void CPhysicalLink::PinRequestComplete()
/**
	Just clean up now that PIN request has completed
**/
	{
	LOG_FUNC
	delete iPinRequester;
	iPinRequester = NULL;
	iPinHandler = NULL;
	AuthenticationComplete(EPinRequestPending);
	}


TInt CPhysicalLink::RequestHold()
	{
	LOG_FUNC
	return RequestMode(EHoldMode);
	}

TInt CPhysicalLink::RequestSniff()
	{
	LOG_FUNC
	return RequestMode(ESniffMode);
	}

TInt CPhysicalLink::RequestPark()
	{
	LOG_FUNC
	return RequestMode(EParkMode);
	}

TInt CPhysicalLink::RequestActive()
	{
	LOG_FUNC
	return RequestMode(EActiveMode);
	}

void CPhysicalLink::ReadNewPhysicalLinkMetricValue(TUint aIoctlName, CBTProxySAP& aSAP, TInt aCurrentValue)
	{
	LOG_FUNC
	iPhysicalLinkMetrics->ReadNewPhysicalLinkMetricValue(aIoctlName, aSAP, aCurrentValue);
	}

void CPhysicalLink::GetCurrentBasebandState(TBTBasebandEventNotification & aEvent)
	{
	LOG_FUNC
	// Populate the event with the current baseband state.
	TUint32 events = 0;
	switch(iLinkState.LinkRole())
		{
		case EMaster:
			events |= ENotifyMaster;
			break;
		case ESlave:
			events |= ENotifySlave;
			break;
		case ERoleUnknown:
		default:
			break;
		};

	switch(iLinkState.LinkMode())
		{
		case EActiveMode:
			events |= ENotifyActiveMode;
			break;
		case ESniffMode:
			events |= ENotifySniffMode;
			break;
		case EParkMode:
			events |= ENotifyParkMode;
			break;
		case EHoldMode:
			events |= ENotifyHoldMode;
			break;
		case EScatterMode:
		default:
			break;
		};

	switch(iLinkState.MaxSlots())
		{
		case 1:
			events |= ENotifyMaxSlots1;
			break;
		case 3:
			events |= ENotifyMaxSlots3;
			break;
		case 5:
			events |= ENotifyMaxSlots5;
			break;
		default:
			break;
		};

	TUint16 packetTypes = iLinkState.PacketTypes();
	if(packetTypes & EPacketsDM1)
		{
		events |= ENotifyPacketsDM1;
		}
	if(packetTypes & EPacketsDM3)
		{
		events |= ENotifyPacketsDM3;
		}
	if(packetTypes & EPacketsDM5)
		{
		events |= ENotifyPacketsDM5;
		}
	if(packetTypes & EPacketsDH1)
		{
		events |= ENotifyPacketsDH1;
		}
	if(packetTypes & EPacketsDH3)
		{
		events |= ENotifyPacketsDH3;
		}
	if(packetTypes & EPacketsDH5)
		{
		events |= ENotifyPacketsDH5;
		}
	if(packetTypes & EPacketsHV1)
		{
		events |= ENotifyPacketsHV1;
		}
	if(packetTypes & EPacketsHV2)
		{
		events |= ENotifyPacketsHV2;
		}
	if(packetTypes & EPacketsHV3)
		{
		events |= ENotifyPacketsHV3;
		}

	if(iLinkState.Authenticated())
		{
		events |= ENotifyAuthenticationComplete;
		}

	if(iLinkState.Encrypted())
		{
		events |= ENotifyEncryptionChangeOn;
		}
	else
		{
		events |= ENotifyEncryptionChangeOff;
		}

	switch(iLinkState.LinkState())
		{
		case TBTBasebandLinkState::ELinkUp:
			events |= ENotifyPhysicalLinkUp;

			if (iSyncLogicalLink)
				{
				if (iSyncLogicalLink->IsOpen())
					{
					events |= ENotifySynchronousLinkUp;
					}
				else
					{
					events |= ENotifySynchronousLinkDown;
					}
				}
			break;
		case TBTBasebandLinkState::ELinkDown:
			events |= ENotifyPhysicalLinkDown;
			break;
		default:
			break;
		};

	aEvent.SetEventType(events);
	aEvent.SetErrorCode(0);
	}

TBool CPhysicalLink::ACLConnectPending() const
	{
	LOG_FUNC
	return !IsConnected();
	}

TBool CPhysicalLink::SyncConnectPending() const
	{
	LOG_FUNC
	if (!iSyncLogicalLink)
		{
		return EFalse;
		}

	return iSyncLogicalLink->ConnectPending();
	}

TInt CPhysicalLink::ManageEncryptionEnforcement(THCIEncryptModeFlag aEnable)
	{
	LOG_FUNC
	// Although this code has been written as if this method could be called more then once,
	// with different argument values:
	// ChangeEncryption() (and then ManageEncryptionEnforcement()) is called only
	// once and only with aEnable = EPointToPointEncryption.
	// It's called if during the physical link creation the encryption is requested.
	// That is because the assertion below.
	__ASSERT_DEBUG(aEnable != EEncryptionDisabled, Panic(EBTInvalidEncryptionDisableRequest));

	// although "&& (iEncryptionEnforcer)" is not necessary, we leave it because
	// clarify that we had asked for the encryption, so it's our responsability to
	// delete iEncryptionEnforcer.
	if ((aEnable == EEncryptionDisabled) && (iEncryptionEnforcer))
		{
		delete iEncryptionEnforcer;
		iEncryptionEnforcer = NULL;
		}
	else if ((aEnable != EEncryptionDisabled) && (!iEncryptionEnforcer))
		{
		TRAPD(err, iEncryptionEnforcer = CEncryptionEnforcer::NewL(*this, aEnable));
		return err;
		}
	return KErrNone;
	}

TBasebandTime CPhysicalLink::CalculatePageTimeout(TBasebandPageTimePolicy aPolicy, TUint8 aRepMode, TBool aValidClockOffset)
	{
	LOG_FUNC
	TBasebandTime pageTimeout = TBasebandPolicy::KPageTimeoutR0;

	switch (aRepMode)
		{
		// PSRM defined in spec
		case 0x00:
			// already set
			break;

		case 0x01:
			pageTimeout = TBasebandPolicy::KPageTimeoutR1;
			break;

		case 0x02:
			// fallthrough
		default:
			pageTimeout = TBasebandPolicy::KPageTimeoutR2;
			break;
		}

	if (!aValidClockOffset)
		{
		pageTimeout*=2;
		}
	else
		{
//		normalPageTimeout+=5; //slots
		}

	// policy may not be valid - or dontcare/normal
	switch (aPolicy)
		{
		case EPagingBestEffort:
			pageTimeout = static_cast<TBasebandTime>(TBasebandPolicy::KBestEffortTimeMultiplier * pageTimeout);
			break;

		case EPagingQuick:
			pageTimeout = static_cast<TBasebandTime>(TBasebandPolicy::KQuickTimeMultiplier * pageTimeout);
			break;

		default:
			break;
			// leave as is

		}
	return pageTimeout;
	}

// CArbitrationDelayTimer

CArbitrationDelayTimer::CArbitrationDelayTimer(CPhysicalLink* aParent)
	:CTimer(CActive::EPriorityStandard),
	iParent(aParent)
	{
	LOG_FUNC
	}

void CArbitrationDelayTimer::ConstructL()
	{
	LOG_FUNC
	CTimer::ConstructL();
	CActiveScheduler::Add(this);
	}

CArbitrationDelayTimer* CArbitrationDelayTimer::NewL(CPhysicalLink* aParent)
	{
	LOG_STATIC_FUNC
	CArbitrationDelayTimer* self = new (ELeave) CArbitrationDelayTimer(aParent);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CArbitrationDelayTimer::Start(TBool aLocalPriority)
    {
    LOG_FUNC
    // Work out what the local priority will be now
    TBool localPriority = iLocalPriority || aLocalPriority;
    Cancel(); // cancel current timer (will also reset priority so ...
    iLocalPriority = localPriority; // set the new priority)
	After(KBTArbitrationDelay);
	}


void CArbitrationDelayTimer::RunL()
/**
Allow arbitration of low power modes when the timer expires
**/
	{
	LOG_FUNC
	if (iParent)
		{
        iParent->DoArbitrate(iLocalPriority);
		}
	}

void CArbitrationDelayTimer::DoCancel()
    {
    LOG_FUNC
    CTimer::DoCancel();
    iLocalPriority = EFalse;
    }

TInt CPhysicalLink::GetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType) const
	{
	LOG_FUNC
	aConnectionHandles = 0;

	switch(aLinkType)
		{
	case EeSCOLink:

		if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending())
			{
			// Currently there is a maximum of one synclink per
			// physical link

			++aConnectionHandles;
			}

		break;

	case ESCOLink:

		if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending())
			{
			// Currently there is a maximum of one synclink per
			// physical link

			++aConnectionHandles;
			}

		break;

	case EACLLink:

		if (!IsConnected())
			{
			// Currently there is a maximum of one asynclink per
			// physical link
			++aConnectionHandles;
			}

		break;

	default:
		return KErrUnknown;
		}

	return KErrNone;
	}

TInt CPhysicalLink::GetConnectionHandles(RHCIConnHandleArray& aConnectionHandles,
					 TLinkType aLinkType) const
	{
	LOG_FUNC
	// Always append to client supplied array - do not clear the
	// contents.  Only return connected handles.

	switch(aLinkType)
		{
	case EeSCOLink:

		if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending())
			{
			return KErrNone;
			}

		if (iSyncLogicalLink->LinkType() == EeSCOLink)
			{
			// Currently there is a maximum of one synclink per
			// physical link
			aConnectionHandles.Append(iSyncLogicalLink->Handle());
			}

		break;

	case ESCOLink:

		if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending())
			{
			return KErrNone;
			}

		if (iSyncLogicalLink->LinkType() == ESCOLink)
			{
			// Currently there is a maximum of one synclink per
			// physical link
			aConnectionHandles.Append(iSyncLogicalLink->Handle());
			}

		break;

	case EACLLink:
		// Currently there is a maximum of one asynclink per physical
		// link and they share the same handle - this may change in
		// the future for QoS.

		if (!IsConnected())
			{
			return KErrNone;
			}

		aConnectionHandles.Append(iHandle);
		break;

	default:
		return KErrUnknown;
		}

	return KErrNone;
	}

void CPhysicalLink::IOCapabilityAskForResponse(THCIIoCapability aIOCapability, THCIOobDataPresence aOOBDataPresence, THCIAuthenticationRequirement aAuthenticationRequirement)
	{
	LOG_FUNC
	iIOCapsReceived = ETrue;
	iIOCapability = aIOCapability;
	iOOBDataPresence = aOOBDataPresence;
	iAuthenticationRequirement = aAuthenticationRequirement;
	
	//If we haven't determined the SSP pairing mode till now then enable it and notify the state m/c.
	//This condition is to cater the fast remote device which responds very quickly,  
	//even before we determine whether it supports simple pairing!*/ 
	__ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection));
	if(SimplePairingMode() == EPhySimplePairingUndefined)
		{
		//Since we have received a I/O cap response the simple pairing must be enabled
		iSimplePairingMode = EPhySimplePairingEnabled;
		iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr());
		}
	}


TBool CPhysicalLink::AuthWithMITM() const
	{
	LOG_FUNC
   	return (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask))
  			&& (iIOCapability != EIOCapsNoInputNoOutput);
	}

void CPhysicalLink::SetLocalMITM(TBool aLocalMITM)
	{
	LOG_FUNC
	iLocalMITM = aLocalMITM;
	}

TPhysicalLinkSimplePairingMode CPhysicalLink::SimplePairingMode() const
	{
	LOG_FUNC
	return iSimplePairingMode;
	}

TBool CPhysicalLink::HasRemoteOobData() const
	{
	LOG_FUNC
	return iLinksMan.SecMan().OobDataManager().HasRemoteOobData(BDAddr());
	}

THCIAuthenticationRequirement CPhysicalLink::AuthenticationRequirement() const
	{
	LOG_FUNC
	return iAuthenticationRequirement;
	}

void CPhysicalLink::NewNumericComparatorL(const TBTDevAddr aAddr,
 										  CBTSecMan& aSecMan,
 										  TUint32 aNumericValue,
 										  TBool	aInternallyInitiated)
   	{
	LOG_FUNC
 	__ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress));
 	
 	// Check remote IO capabilities so that the BTNumericComparator can tell the user
 	TBTNumericComparisonParams::TComparisonScenario compScenario = TBTNumericComparisonParams::ERemoteCanConfirm;
 	if(iIOCapsReceived)
 		{
 		switch(iIOCapability)
 			{
 			case EIOCapsDisplayOnly:
 				compScenario = TBTNumericComparisonParams::ERemoteCannotConfirm;
 				break;
 			case EIOCapsDisplayYesNo:
 				compScenario = TBTNumericComparisonParams::ERemoteCanConfirm;
 				break;
 			default:
 				// If iIOCapability = EIOCapsKeyboardOnly or EIOCapsNoInputNoOutput 
 				// we should not be using a Numeric Comparitor
 				__ASSERT_DEBUG(EFalse, Panic(EUnexpectedIOCapability));
 				break;
 			}
 		}
 	iNumericComparator = CBTNumericComparator::NewL(aAddr, aSecMan, aNumericValue, compScenario, aInternallyInitiated);
   	}

CBTNumericComparator* CPhysicalLink::InstanceNumericComparator() const
	{
	LOG_FUNC
	return iNumericComparator;
	}

TBool CPhysicalLink::IsNumericComparatorActive()const
	{
	LOG_FUNC
	return iNumericComparator->IsActive();
	}

void CPhysicalLink::DeleteNumericComparator()
	{
	LOG_FUNC
	delete iNumericComparator;
	iNumericComparator = NULL;
	}
void CPhysicalLink::CancelNumericComparator()
	{
	LOG_FUNC
	iNumericComparator->Cancel();
	}

void CPhysicalLink::NewPasskeyEntryL(const TBTDevAddr aAddr,
												CBTSecMan& aSecMan,
												TUint32 aNumericValue,
												TBool aInternallyInitiated)
	{
	LOG_FUNC
	iPasskeyEntry = CBTPasskeyEntry::NewL(aAddr, aSecMan, aNumericValue, aInternallyInitiated);
	}

CBTPasskeyEntry* CPhysicalLink::InstancePasskeyEntry() const
	{
	LOG_FUNC
	return iPasskeyEntry;
	}

TBool CPhysicalLink::IsPasskeyEntryActive()const
	{
	LOG_FUNC
	return iPasskeyEntry->IsActive();
	}

void CPhysicalLink::DeletePasskeyEntry()
	{
	LOG_FUNC
	delete iPasskeyEntry;
	iPasskeyEntry = NULL;
	}

void CPhysicalLink::CancelPasskeyEntry()
	{
	LOG_FUNC
	iPasskeyEntry->Cancel();
	}

void CPhysicalLink::PasskeyEntryKeyPressed(THCIPasskeyEntryNotificationType aKey)
	{
	LOG_FUNC
	iPasskeyEntry->KeyPressed(aKey);
	}


TBasebandTime CPhysicalLink::GetSniffInterval() const
	{
	return iSniffInterval;
	}

//
// TLowPowModeCmdController
//

TLowPowModeCmdController::TLowPowModeCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
	iParent(aLink),
	iCmdController(aCmdController),
	iOutstandingCmd(EFalse)
	{
	LOG_FUNC
	}

void TLowPowModeCmdController::Abort()
	{
	LOG_FUNC
	LOG(_L("TLowPowModeCmdController::Abort"));
	iCmdController.MhcqRemoveAllCommands(*this);
	}

void TLowPowModeCmdController::DoExitModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle)
	{
	LOG_FUNC
	LOG2(_L("TLowPowModeCmdController::DoExitModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd);
	if (!iOutstandingCmd)
		{
		switch (aMode)
			{
			case ESniffMode:
				{
				ExitSniffL(aConnHandle);
				break;
				}
			case EParkMode:
				{
				ExitParkL(aConnHandle);
				break;
				}
			// Not possile to prematurely exit hold mode
			default:
				break;
			}
		iOutstandingCmd = ETrue;
		}
	else
		{
		LOG1(_L("TLowPowModeCmdController::DoExitModeL: cannot exit from mode:%d due to an outstanding command"), aMode);
		}
	}

TInt TLowPowModeCmdController::ExitMode(TBTLinkMode aMode, THCIConnHandle aHandle)
	{
	LOG_FUNC
	TRAPD(err, DoExitModeL(aMode, aHandle));
	return err;
	}

void TLowPowModeCmdController::DoChangeModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle)
	{
	LOG_FUNC
	LOG2(_L("TLowPowModeCmdController::DoChangeModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd);
	if (!iOutstandingCmd)
		{
		switch (aMode)
			{
			case ESniffMode:
				{
				SniffL(aConnHandle);
				break;
				}
			case EParkMode:
				{
				ParkL(aConnHandle);
				break;
				}
			case EHoldMode:
				{
				HoldL(aConnHandle);
				break;
				}
			case EScatterMode:
			case EActiveMode:
				__ASSERT_DEBUG(0, Panic(EHCICommandBadArgument));
				break;
			}
		iOutstandingCmd = ETrue;
		}
	else
		{
		LOG1(_L("TLowPowModeCmdController::DoChangeModeL: cannot change to mode:%d due to an outstanding command"), aMode);
		}
	}

TInt TLowPowModeCmdController::ChangeMode(TBTLinkMode aMode, THCIConnHandle aHandle)
	{
	LOG_FUNC
	TRAPD(err, DoChangeModeL(aMode, aHandle));
	return err;
	}

void TLowPowModeCmdController::SniffL(THCIConnHandle aHandleToRemote)
	{
	LOG_FUNC
	CSniffModeCommand* cmd = CSniffModeCommand::NewL(aHandleToRemote, KBTSniffModeMaxInterval, KBTSniffModeMinInterval, KBTSniffModeAttempt, KBTSniffModeTimeout);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TLowPowModeCmdController::ExitSniffL(THCIConnHandle aHandleToRemote)
	{
	LOG_FUNC
	CExitSniffModeCommand* cmd = CExitSniffModeCommand::NewL(aHandleToRemote);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TLowPowModeCmdController::HoldL(THCIConnHandle aHandle)
	{
	LOG_FUNC
	CHoldModeCommand* cmd = CHoldModeCommand::NewL(aHandle, KBTHoldModeMaxInterval, KBTHoldModeMinInterval);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TLowPowModeCmdController::ParkL(THCIConnHandle aHandle)
	{
	LOG_FUNC
	CParkModeCommand* cmd = CParkModeCommand::NewL(aHandle, KBTParkModeBeaconMaxInterval, KBTParkModeBeaconMinInterval);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TLowPowModeCmdController::ExitParkL(THCIConnHandle aHandle)
	{
	LOG_FUNC
	CExitParkModeCommand* cmd = CExitParkModeCommand::NewL(aHandle);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TLowPowModeCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
	{
	LOG_FUNC
	THCIEventCode code = aEvent.EventCode();

	__ASSERT_DEBUG(!(code != EModeChangeEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent));	// this should never happen
	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent));	// this should never happen

	if (!aRelatedCommand)	// it is not the response to our command - should never happen
		return;

	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
		{
		LOG3(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode());
		iOutstandingCmd = EFalse;	// reset the outstanding flag
		iParent.StartArbitrationTimer();
		}
	else if (code == EModeChangeEvent)
		{
		iOutstandingCmd = EFalse;	// reset the outstanding flag

		LOG2(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode());

		const TModeChangeEvent& modeChangeEvent = TModeChangeEvent::Cast(aEvent);
		TBTLinkMode mode = EActiveMode;
		switch(modeChangeEvent.CurrentMode())
			{
			// Mode 0, as defined for the Mode Change Event, is Active Mode
			case 0:
				break;
			case 1:
				mode = EHoldMode;
				break;
			case 2:
				mode = ESniffMode;
				break;
			case 3:
				mode = EParkMode;
				break;
			default:
				__ASSERT_ALWAYS(EFalse, Panic(EHCICommandBadArgument));
				break;
			}
			// In the HCI_Facade, in this situation, CPhysicalLinksManager::ModeChanged() is called.
			// Since this methods find the CPhysicalLink object (that is iParent) and then call its
			// ModeChange method, we can call it directly.
			iParent.ModeChange(aEvent.ErrorCode(), modeChangeEvent.ConnectionHandle(), mode, modeChangeEvent.Interval());
		}
	}

void TLowPowModeCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
	{
	LOG_FUNC
	__ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent));	// this should never happen
	#ifdef _DEBUG
	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
	#endif
	iOutstandingCmd = EFalse;	// reset the outstanding flag
	iParent.StartArbitrationTimer();
	}




//
// CEncryptionEnforcer
//

CEncryptionEnforcer::CEncryptionEnforcer (CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode) :
	CActive(EPriorityStandard), // Standard priority
	iLink(aLink),
	iEncryptionMode(aEncryptionMode),
	iState(EInactive)
	{
	LOG_FUNC
	}

CEncryptionEnforcer* CEncryptionEnforcer::NewLC(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode)
	{
	LOG_STATIC_FUNC
	CEncryptionEnforcer* self = new ( ELeave ) CEncryptionEnforcer(aLink, aEncryptionMode);
	CleanupStack::PushL (self);
	self->ConstructL ();
	return self;
	}

CEncryptionEnforcer* CEncryptionEnforcer::NewL(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode)
	{
	LOG_STATIC_FUNC
	CEncryptionEnforcer* self = CEncryptionEnforcer::NewLC (aLink, aEncryptionMode);
	CleanupStack::Pop(self);
	return self;
	}

void CEncryptionEnforcer::ConstructL()
	{
	LOG_FUNC
	User::LeaveIfError (iTimer.CreateLocal () ); // Initialize timer
	CActiveScheduler::Add (this); // Add to scheduler
	}

CEncryptionEnforcer::~CEncryptionEnforcer()
	{
	LOG_FUNC
	Cancel (); // Cancel any request, if outstanding
	iTimer.Close (); // Destroy the RTimer object
	}

void CEncryptionEnforcer::DoCancel ()
	{
	LOG_FUNC
	iTimer.Cancel ();
	ChangeState(EInactive);
	}

void CEncryptionEnforcer::Start(TInt delay)
	{
	LOG_FUNC
	Cancel (); // Cancel any request, just to be sure
	LOG(_L("CEncryptionEnforcer activate timer"))
	iTimer.After (iStatus, delay); // Set for later
	SetActive (); // Tell scheduler a request is active
	}

void CEncryptionEnforcer::Stop()
	{
	LOG_FUNC
	Cancel();
	}

void CEncryptionEnforcer::RunL ()
	{
	LOG_FUNC
	switch(iState)
		{
		case ERoleSwitchTimerInProgress:
			LOG(_L("CEncryptionEnforcer::RunL() : timer expired after a roleswitch, enforce encryption"))
			ChangeState(EForcingInProgress);
			User::LeaveIfError(iLink.ChangeEncryption(iEncryptionMode));
			break;
		case ENoRoleSwitchTimerInProgress:
			LOG(_L("CEncryptionEnforcer::RunL() : timer expired and no roleswitch performed, terminate the link"))
			iLink.Terminate(ERemoteUserEndedConnection);
			break;
		default:
			// this should never happen, so we assert in debug so to raise the problem.
			// however it doesn't break our behaviour, so we don't care in release.
			LOG(_L("CEncryptionEnforcer::RunL() : bad state"))
			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
			break;
		}
	}

TInt CEncryptionEnforcer::RunError(TInt IF_FLOGGING(aError))
	{
	LOG_FUNC
	LOG1(_L("CEncryptionEnforcer::RunError() : error = %d"), aError);
	LOG(_L("CEncryptionEnforcer::RunError() : try to terminate the link"));
	return iLink.Terminate(ERemoteUserEndedConnection);
	}

void CEncryptionEnforcer::ChangeState(TState aState)
	{
	LOG_FUNC
	LOG2(_L("CEncryptionEnforcer::ChangeState(): old state = %d; new state = %d"), iState, aState);
	iState = aState;
	}

void CEncryptionEnforcer::EncryptionEnabled()
	{
	LOG_FUNC
	LOG1(_L("CEncryptionEnforcer::EncryptionEnabled() : current status = %d"), iState);
	switch(iState)
		{
		case EInactive:
			// do nothing, it means is the first encryption event as reply to our ChangeEncryption cmd.
			break;
		case EForcingInProgress:
			ChangeState(EInactive);
			break;
		case ENoRoleSwitchTimerInProgress:
		case ERoleSwitchTimerInProgress:
			Stop();
			break;
		}
	}

void CEncryptionEnforcer::EncryptionDisabled(TBool aSecurityModeFour)
	{
	LOG_FUNC
	LOG1(_L("CEncryptionEnforcer::EncryptionDisabled() : current status = %d"), iState);

	if(aSecurityModeFour)
		{
		Stop();
		ChangeState(EForcingInProgress);
		}

	switch(iState)
		{
		case EInactive:
			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : start timer"))
			ChangeState(ENoRoleSwitchTimerInProgress);
			Start(KTimeOutDelay);
			break;
		case EForcingInProgress:
			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : disconnect the link"))
			iLink.Terminate(ERemoteUserEndedConnection);
			break;
		default:
			// this should never happen, so we assert in debug so to raise the problem.
			// however it doesn't break our behaviour, so we don't care in release.
			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state"))
			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
			break;
		}
	}

void CEncryptionEnforcer::RoleSwitchEvent()
	{
	LOG_FUNC

	switch(iState)
		{
		case EInactive:
			// This is possible with EPR support - we don't need to do anything in this case.
			break;
		case ENoRoleSwitchTimerInProgress:
			ChangeState(ERoleSwitchTimerInProgress);
			break;
		default:
			// this should never happen, so we assert in debug so to raise the problem.
			// however it doesn't break our behaviour, so we don't care in release.
			LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state"))
			__ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState));
			break;
		}
	}

void XAutoKeyRefreshToken::Release()
	{
	LOG_FUNC
	iQueLink.Deque();
	delete this;
	}

//
// TRegistryDeviceOutChecker
//
TRegistryDeviceBeingModified::TRegistryDeviceBeingModified() :
	iReferenceCount(0)
	{
	LOG_FUNC
	}

TBool TRegistryDeviceBeingModified::InUse() const
	{
	LOG_FUNC
		return iReferenceCount > 0;
	}

TBool TRegistryDeviceBeingModified::IsEqual(const TBTNamelessDevice& aDevice) const
	{
	LOG_FUNC
	return (iDevice == aDevice);
	}

void TRegistryDeviceBeingModified::Begin(const TBTNamelessDevice& aDevice)
	{
	LOG_FUNC
	iDevice = aDevice;
	iReferenceCount++;
	}

void TRegistryDeviceBeingModified::RequestCompleted()
	{
	LOG_FUNC
	iReferenceCount--;
	__ASSERT_DEBUG(iReferenceCount >= 0, Panic(ETRegistryDevBeingModUnexpectedCompleted));
	}

//
// TDisconnectCmdController
// HCI Command Queue Interface for Disconnect Commands
//

TDisconnectCmdController::TDisconnectCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
	iParent(aLink),
	iCmdController(aCmdController)
	{
	LOG_FUNC
	}

void TDisconnectCmdController::Abort()
	{
	LOG_FUNC
	iCmdController.MhcqRemoveAllCommands(*this);
	}

TInt TDisconnectCmdController::Disconnect(THCIErrorCode aReason)
	{
	LOG_FUNC
	TRAPD(err, DoDisconnectL(aReason));
	return err;
	}

void TDisconnectCmdController::DoDisconnectL(THCIErrorCode aReason)
	{
	LOG_FUNC
	CDisconnectCommand* cmd = CDisconnectCommand::NewL(iParent.Handle(), aReason);

	// Ownership of cmd transfered even if MhcqAddCommandL leaves
	iCmdController.MhcqAddCommandL(cmd, *this);
	}

void TDisconnectCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
	{
	LOG_FUNC
	THCIEventCode code = aEvent.EventCode();
	
	__ASSERT_DEBUG(!(code != EDisconnectionCompleteEvent && code != ECommandStatusEvent), Panic(EDiscCtrlUnexpectedCommandEvent));	// this should never happen
	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EDiscCtrlUnmatchedCommandEvent));	// this should never happen
	
	if (!aRelatedCommand)	// it is not the response to our command - should never happen
		return;
	
	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
		{
		iParent.Disconnection(aEvent.ErrorCode(), iParent.Handle(), aEvent.ErrorCode());
		}
	else if (code == EDisconnectionCompleteEvent)
		{
		const TDisconnectionCompleteEvent& disconnectionCompleteEvent = TDisconnectionCompleteEvent::Cast(aEvent);
		// The connection handle in the disconnect complete event should be the same as the physical link's connection handle
		__ASSERT_DEBUG(disconnectionCompleteEvent.ConnectionHandle() == iParent.Handle(), Panic(EDiscCtrlUnexpectedConnectionHandle));
		iParent.Disconnection(aEvent.ErrorCode(), disconnectionCompleteEvent.ConnectionHandle(), static_cast<THCIErrorCode>(disconnectionCompleteEvent.Reason()));
		}
	}

void TDisconnectCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
	{
	LOG_FUNC
	__ASSERT_DEBUG(aCommand, Panic(EDiscCtrlUnmatchedCommandEvent));	// this should never happen
	#ifdef _DEBUG
	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
	#endif
	// Pass this to the physical link as Command Disallowed
	iParent.Disconnection(ECommandDisallowed, iParent.Handle(), ECommandDisallowed);
	}


//
// TAuthenticationCmdController
//

TAuthenticationCmdController::TAuthenticationCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : 	
	iParent(aLink),
	iCmdController(aCmdController)
	{
	LOG_FUNC
	}

void TAuthenticationCmdController::Abort()
	{
	LOG_FUNC
	iCmdController.MhcqRemoveAllCommands(*this);
	}

TInt TAuthenticationCmdController::LinkKeyRequestReply(const TBTDevAddr& aBdaddr, const TDesC8& aLinkKey) const
/**
	We have obtained a linkkey, tell HC
**/
	{
	CLinkKeyRequestReplyCommand* cmd = NULL;
	TBTLinkKey linkkey;
	linkkey.Copy(aLinkKey);

	TRAPD(err, cmd = CLinkKeyRequestReplyCommand::NewL(aBdaddr, linkkey));
	if (err == KErrNone)
		{
		// Ownership of cmd transfered even if MhcqAddCommandL leaves
		TRAP(err,iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this)));
		}
	if(err != KErrNone)
		{
		iParent.AuthenticationComplete(ELinkKeyRequestPending);
		}
	return err;
	}
	
TInt TAuthenticationCmdController::LinkKeyRequestNegativeReply(const TBTDevAddr& aBdaddr) const
/**
	We have not obtained a linkkey, tell HC
**/
	{
	CLinkKeyRequestReplyNegativeCommand* cmd = NULL;

	TRAPD(err, cmd = CLinkKeyRequestReplyNegativeCommand::NewL(aBdaddr));
	if (err == KErrNone)
		{
		// Ownership of cmd transfered even if MhcqAddCommandL leaves
		TRAP(err, iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this)));
		}
	
	if(err != KErrNone)
		{
		iParent.AuthenticationComplete(ELinkKeyRequestPending);
		}
	return err;
	}

void TAuthenticationCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand)
	{
	LOG_FUNC
	THCIEventCode code = aEvent.EventCode();

	__ASSERT_DEBUG(!(code != ECommandCompleteEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent));	// this should never happen
	__ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent));	// this should never happen

	if (!aRelatedCommand)	// it is not the response to our command - should never happen
		return;

	if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK))
		{
		LOG3(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode());
		}
	else if (code == ECommandCompleteEvent)
		{
		LOG2(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode());
		iParent.AuthenticationComplete(ELinkKeyRequestPending);
		}
	}

void TAuthenticationCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand))
	{
	LOG_FUNC
	__ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent));	// this should never happen
	#ifdef _DEBUG
	LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode());
	#endif
	iParent.AuthenticationComplete(ELinkKeyRequestPending);
	}

