bluetooth/btstack/common/secman.cpp
changeset 0 29b1cd4cb562
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/common/secman.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1272 @@
+// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <bluetooth/logger.h>
+#include "secman.h"
+#include "hostresolver.h"
+#ifdef BT_LINKMGR_V2
+#include "physicallinks.h"
+#include "physicallinksmanager.h"
+#else
+#include "PhysicalLinks.h"
+#include "PhysicalLinksManager.h"
+#endif
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_SECMAN);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("secman");
+#endif
+
+#pragma warning (disable: 4355) //'this' in base init list - is OK for our usage
+
+static const TInt KBTSecManAccessRequesterArrayGranularity = 4;
+static const TInt KBTSecManNotifierRequesterArrayGranularity = 4;
+
+
+//------------------------------------------------------------------------//
+//class CBTSecMan
+//------------------------------------------------------------------------//
+void Panic(TBTSecPanic aPanic)
+	{
+	LOG_FUNC
+	User::Panic(KBTSecPanic, aPanic);
+	}
+
+CBTSecMan* CBTSecMan::NewL()
+	{
+	LOG_FUNC
+	CBTSecMan* self = CBTSecMan::NewLC();
+	CleanupStack::Pop();	//self
+	return self;
+	}
+
+CBTSecMan* CBTSecMan::NewLC()
+	{
+	LOG_FUNC
+	CBTSecMan* self = new(ELeave) CBTSecMan();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+CBTSecMan::CBTSecMan()
+	: iAccessRequesters(KBTSecManAccessRequesterArrayGranularity),
+	  iNotifierRequesters(KBTSecManNotifierRequesterArrayGranularity)
+	{
+	LOG_FUNC
+	}
+
+void CBTSecMan::ConstructL()
+	{
+	LOG_FUNC
+	}
+
+void CBTSecMan::SetPhysicalLinksMgr(const CPhysicalLinksManager& aConnectionsMgr)
+	{
+	LOG_FUNC
+	iPhysicalLinksManager = &const_cast<CPhysicalLinksManager&>(aConnectionsMgr);
+	}
+
+CPhysicalLinksManager& CBTSecMan::ConnectionsManager() const
+	{
+	LOG_FUNC
+	return *iPhysicalLinksManager;
+	}
+
+
+CBTSecMan::~CBTSecMan()
+	{
+	LOG_FUNC
+
+	iAccessRequesters.ResetAndDestroy();
+	iAccessRequesters.Close();
+
+	iNotifierRequesters.ResetAndDestroy();
+	iNotifierRequesters.Close();
+	}
+
+void CBTSecMan::AccessRequestL(const TBTServiceSecurity& aSecurity,
+							   const TBTServiceSecurityPerDevice* const aOverride,
+							   const TBTDevAddr& aBDAddr,
+							   MAccessRequestResponseHandler& aRequester)
+/**
+Handle an access request...
+Create a new CBTAccessRequester object to handle the request.
+**/
+	{
+	LOG_FUNC
+	// find the baseband this SAP is running on
+	CPhysicalLink& con = *iPhysicalLinksManager->FindPhysicalLink(aBDAddr);
+	CBTAccessRequester* p = CBTAccessRequester::NewLC(con, aSecurity,
+													  aOverride,
+													  aRequester,
+													  *this);
+	User::LeaveIfError(iAccessRequesters.Append(p));
+	CleanupStack::Pop(); //clean up of p now handled by iAccessRequesters
+	// Try to start- it may not happen (depends on if device retreived from registry
+	p->DoRequest();
+	
+	}
+
+void CBTSecMan::CancelRequest(MAccessRequestResponseHandler& aRequester)
+	{
+	LOG_FUNC
+	// search through access requesters to find correct one
+	LOG1(_L("sec\tCBTSecMan::CancelRequest from SAP 0x%08x"), &aRequester)
+	TInt count = iAccessRequesters.Count();
+
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		CBTAccessRequester* requester = iAccessRequesters[i];
+		if(&requester->ServiceRequester() == &aRequester)
+			{
+			// should assert that aRequester is the same as in AccessRequester?
+			LOG(_L("sec\tRemoving AccessRequester..."))
+			iAccessRequesters.Remove(i);
+			delete requester;
+			break;
+			}
+		}
+	}
+	
+void CBTSecMan::GetPassKeyLengthAndOriginator(const TBTDevAddr& aAddr, TUint& aPasskeyMinLength,
+				   	   					      TBool& aLocallyInitiatedAuthentication)
+
+/**
+If authorisation request was initiated locally it will return true and will
+return the passkey minimal length requred by user
+**/
+	{
+	LOG_FUNC
+
+	TUint tmpPasskeyLength = 0;
+	TBool locallyInitiated = EFalse;
+	TInt count = iAccessRequesters.Count();
+
+	if (count == 0)
+		{
+		aLocallyInitiatedAuthentication = EFalse;
+		return;
+		}
+
+	// find all pending AccessRequesters for given BTAddr and find maximum of PasskeyMinLength
+	
+	for (TInt i=0; i<count;i++)
+		{
+		CBTAccessRequester* requester = iAccessRequesters[i];
+		if (requester->IsAuthenticationReqPending(aAddr, tmpPasskeyLength))
+			{
+			locallyInitiated = ETrue;
+			if (aPasskeyMinLength < tmpPasskeyLength)
+				{
+				aPasskeyMinLength = tmpPasskeyLength;
+				}
+			}	
+		}
+	
+	aLocallyInitiatedAuthentication = locallyInitiated;		
+	}
+	
+void CBTSecMan::AuthenticationInProgress()
+/**
+When authorisation request was sent to HW, HCI will notify SecMan
+**/
+	{
+	LOG_FUNC
+	
+	// find first pending AccessRequesters and set AuthenticationInProgress flag
+	for (TInt i=0; i<iAccessRequesters.Count(); i++)
+		{
+		CBTAccessRequester* requester = iAccessRequesters[i];
+		
+		if (requester->AuthenticationRequired() && !requester->AuthenticationInProgress())
+			{
+			requester->SetAuthenticationInProgress();
+			break;
+			}		
+		}
+	}
+
+void CBTSecMan::AccessRequestComplete(CBTAccessRequester* aAccessRequester, TInt aResult)
+/**
+The access request has been fully completed.
+Delete the CBTAccessRequester that was handling the request.
+**/
+	{
+	LOG_FUNC
+
+	TInt count = iAccessRequesters.Count();
+
+	ASSERT_DEBUG(count);
+
+	// find the originating service *now*
+	MAccessRequestResponseHandler& service = const_cast<MAccessRequestResponseHandler&>
+											(aAccessRequester->ServiceRequester());
+
+	// clean up the AccessRequester object
+	TInt i;
+	for (i=(count-1); i>=0; i--)
+		{
+		//find aRequester in iAccessRequesters and delete it
+		if(iAccessRequesters[i] == aAccessRequester)
+			{
+			iAccessRequesters.Remove(i);
+			delete aAccessRequester;
+			aAccessRequester = NULL;
+			break;
+			}
+		}
+	//compress the array if needs be
+	if ((count!=0) && (i!=count))
+		{
+		iAccessRequesters.GranularCompress();
+		}
+
+	// now tell the service
+	service.AccessRequestComplete(aResult);
+	}
+
+
+void CBTSecMan::AddNotifierRequestToQueL(CSecNotifierRequester& aRequest)
+/**
+Add notifier request to front of queue.  If there are no other requests already in the queue,
+initiate this request.
+**/
+	{
+	LOG_FUNC
+	TInt count = iNotifierRequesters.Count();
+	User::LeaveIfError(iNotifierRequesters.Insert(&aRequest,0));	//add to front of queue since requests are taken from the back
+	if (count == 0)	//ok since count was calculated before we inserted the new element
+		{
+		iActiveNotifierRequester = &aRequest;
+		aRequest.DoRequest();
+		}
+	else
+		{
+		LOG(_L("Secman: Request NOT initiated, should start later..."));
+		}
+	}
+
+void CBTSecMan::RemoveNotifierRequestFromQue(CSecNotifierRequester& aRequest)
+/**
+Remove the request from the queue.  If aRequest is the currently active request then we can activate
+the next one in the queue.  Otherwise, aRequest is being deleted prematurely and we must simply
+remove it from the array.
+**/
+	{
+	LOG_FUNC
+	LOG(_L("sec\tCBTSecMan - removing notifier request from queue"));
+	TInt count = iNotifierRequesters.Count();
+	TInt found = 0;
+	for (TInt i=(count-1); i>=0; i--)
+		{
+		if (iNotifierRequesters[i] == &aRequest)
+			{
+			found++;
+			iNotifierRequesters.Remove(i);
+			}
+		}
+	__ASSERT_DEBUG(found, User::Panic(KBTSecPanic, EBTSecBadNotifierArray));
+
+	if (&aRequest == iActiveNotifierRequester)
+		{
+		//start the next request if there is one...
+		count = iNotifierRequesters.Count();
+		if (count > 0)
+			{
+			LOG(_L("sec\tCBTSecMan - auto-starting next notifier request from queue"));
+			iActiveNotifierRequester = iNotifierRequesters[count-1];
+			iActiveNotifierRequester->DoRequest();
+			}
+		}
+	}
+
+
+CSecNotifierRequester::CSecNotifierRequester(CBTSecMan& aSecMan)
+: CActive(EPriorityStandard),
+  iInquiryMgr(aSecMan.ConnectionsManager().LinkManagerProtocol().InquiryMgr()),
+  iSecMgr(aSecMan)
+	{
+	LOG_FUNC
+	}
+
+void CSecNotifierRequester::ConstructL(const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+	User::LeaveIfError(iNotifier.Connect());
+
+	// find the name at this stage for this device - may not be there yet
+	iDeviceName = iInquiryMgr.DeviceNameFromCache(aAddr);
+
+	if (!iDeviceName || iDeviceName->Length() ==0)
+		{
+		// cache didn't have name - so we'll ask for it as a HR action
+		TRAP_IGNORE(iHR = iInquiryMgr.NewHostResolverL());
+		iHRNameRecord = new TNameRecord;
+
+		// ignore error - only an optimisation - don't want to leave if there's
+		// a problem doing this optimisation
+		if (iHR && iHRNameRecord)
+			{
+			iHR->SetNotify(this);
+
+			TInquirySockAddr i;
+			i.SetAction(KHostResName);
+			i.SetBTAddr(aAddr);
+			iHRNameRecord->iAddr = i;
+			iHR->GetByAddress(*iHRNameRecord);
+			}
+		}
+		
+	iDevAddr = aAddr;
+	
+	iSecMgr.AddNotifierRequestToQueL(*this);
+	iIsAddedToNotifierQue = ETrue;
+	}
+
+CSecNotifierRequester::~CSecNotifierRequester()
+	{
+	LOG_FUNC
+	Cancel();
+	
+	//remove ourself from the notifier que if we're still on it.
+	if (iIsAddedToNotifierQue)
+		{
+		iSecMgr.RemoveNotifierRequestFromQue(*this);
+		iIsAddedToNotifierQue = EFalse;
+		}
+
+	delete iHR;
+	delete iHRNameRecord;
+	delete iUpdateNotifierAO; 
+	
+	iNotifier.Close();
+	}
+
+void CSecNotifierRequester::QueryComplete(TInt aErr)
+	{
+	LOG_FUNC
+	if ( (aErr==KErrNone) && (iHRNameRecord!=NULL) )
+		{
+		// now have device name - update notifiers
+		// we do have a copy of the name - but it is now wide :-|
+		// and also we have iDeviceName that is still NULL, so best bet is
+		// to just set our pointer and use the cache one (which we *know* is there!)
+		TBTDevAddr a = TBTSockAddr::Cast(iHRNameRecord->iAddr).BTAddr();
+		iDeviceName = iInquiryMgr.DeviceNameFromCache(a);
+		DoUpdateNotifier();
+		}
+
+	delete iHRNameRecord;
+	iHRNameRecord = NULL;
+	}
+
+//------------------------------------------------------------------------//
+//class CBTPinRequester
+//------------------------------------------------------------------------//
+
+
+CBTPinRequester* CBTPinRequester::NewL(CPhysicalLink& aParent,
+									   MPINCodeResponseHandler& aRequester,
+									   CBTSecMan& aSecMan,
+									   TUint aPasskeyMinLength,
+									   TBool aInternallyInitiated)
+	{
+	LOG_FUNC
+	CBTPinRequester* s = CBTPinRequester::NewLC(aParent, aRequester, aSecMan,
+	                                            aPasskeyMinLength, aInternallyInitiated);
+	CleanupStack::Pop();
+	return s;
+	}
+
+CBTPinRequester* CBTPinRequester::NewLC(CPhysicalLink& aParent,
+										MPINCodeResponseHandler& aRequester,
+										CBTSecMan& aSecMan,
+										TUint aPasskeyMinLength,
+										TBool aInternallyInitiated)
+	{
+	LOG_FUNC
+	CBTPinRequester* s = new(ELeave) CBTPinRequester(aParent, aRequester, aSecMan,
+	                                                 aPasskeyMinLength, aInternallyInitiated);
+	CleanupStack::PushL(s);
+	s->ConstructL(aParent.BDAddr());
+	return s;
+	}
+
+CBTPinRequester::CBTPinRequester(CPhysicalLink& aParent,
+								 MPINCodeResponseHandler& aRequester,
+								 CBTSecMan& aSecMan,
+								 TUint aPasskeyMinLength,
+								 TBool aInternallyInitiated) :
+	CSecNotifierRequester(aSecMan),
+	iParent(aParent),
+	iRequester(aRequester),
+	iSecMan(aSecMan),
+	iPasskeyMinLength(aPasskeyMinLength),
+	iInternallyInitiated(aInternallyInitiated)
+	{
+	LOG_FUNC
+	// a lot of inlines
+	CActiveScheduler::Add(this);
+	}
+
+CBTPinRequester::~CBTPinRequester()
+	{
+	LOG_FUNC
+	Cancel();
+	}
+
+
+void CBTPinRequester::DoUpdateNotifier()
+	{
+	LOG_FUNC
+ 	if(IsActive())
+ 		{	
+ 		if(!iUpdateNotifierAO)
+ 			{
+ 			//Create a new CSecNotifierUpdateAO object
+ 			TRAP_IGNORE(iUpdateNotifierAO = CSecNotifierUpdateAO::NewL(iNotifier, KBTManPinNotifierUid));
+ 			}
+ 		
+ 		if( (iUpdateNotifierAO) && (!iUpdateNotifierAO->IsActive()) )
+ 			{
+ 			TBTNotifierUpdateParamsPckg pckg;
+ 			if(iDeviceName)
+ 				{
+ 				TRAPD(err, pckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ 				pckg().iResult = err; 	// Error code can be KErrNone 
+ 				if (err!=KErrNone)
+	 				{
+	 				pckg().iName = KNullDesC;
+	 				}
+ 				}
+ 			else
+ 				{
+ 				pckg().iName = KNullDesC;
+ 				pckg().iResult = KErrNotFound;
+ 				}
+ 	
+ 			iUpdateNotifierAO->DoUpdate(pckg);
+ 			}
+   		}
+	}
+
+void CBTPinRequester::DoRequest()
+/**
+It's our turn...start the RNotifier plugin that deals with authorisation.
+**/
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iDevAddr == iParent.BDAddr());
+	
+	iPasskeyParamsPckg().iBDAddr = iDevAddr;
+	if (iDeviceName)
+		{
+		TRAPD(err, iPasskeyParamsPckg().iName =	BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+		if (err!=KErrNone)
+			{
+			iPasskeyParamsPckg().iName = KNullDesC;
+			}
+		}
+	else
+		{
+		iPasskeyParamsPckg().iName = KNullDesC;	
+		}			
+	iPasskeyParamsPckg().iPasskeyMinLength = iPasskeyMinLength;	
+	iPasskeyParamsPckg().iLocallyInitiated = iInternallyInitiated;	
+
+	iNotifier.StartNotifierAndGetResponse(iStatus, KBTManPinNotifierUid, iPasskeyParamsPckg, iPassKey);
+	SetActive();
+	}
+
+void CBTPinRequester::FriendlyNameRetrieved(const TDesC& /*aName*/, TInt /*aResult*/)
+	{
+	LOG_FUNC
+	// do nothing for now 
+	}
+
+
+void CBTPinRequester::DoCancel()
+	{
+	LOG_FUNC
+	iNotifier.CancelNotifier(KBTManPinNotifierUid);
+	if (iUpdateNotifierAO)
+		{
+	 	iUpdateNotifierAO->Cancel();
+		}
+	}
+
+void CBTPinRequester::RunL()
+	{
+	LOG_FUNC
+	//got a PIN or error, so finish off: unload the plugin
+	iNotifier.CancelNotifier(KBTManPinNotifierUid);
+
+	//remove ourself from the notifier que, allowing the next notifier to be activated
+	iSecMan.RemoveNotifierRequestFromQue(*this);
+	iIsAddedToNotifierQue = EFalse;
+
+	ASSERT_DEBUG(iPasskeyParamsPckg().iBDAddr == iParent.BDAddr());
+	if (iStatus.Int())
+		{
+		// it failed - be unpairable
+		iRequester.PINCodeRequestNegativeReply((iParent.BDAddr()));
+		}
+	else
+		{
+		// got a PIN
+		iRequester.PINCodeRequestReply(iParent.BDAddr(),iPassKey);		
+		iParent.SetPassKey(iPassKey);
+		iParent.PinRequestSent();
+		iParent.DeleteLinkKeyL(); //only delete link key when user has entered PIN
+		}
+	// in either case tell parent as we're done.
+	iParent.PinRequestComplete();
+	}
+
+#ifdef __FLOG_ACTIVE
+TInt CBTPinRequester::RunError(TInt aError) 
+#else
+TInt CBTPinRequester::RunError(TInt /*aError*/)
+#endif
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iPasskeyParamsPckg().iBDAddr == iParent.BDAddr());
+	LOG1(_L("sec\tCBTPinRequester::RunError(%d)"), aError);
+	iRequester.PINCodeRequestNegativeReply(iParent.BDAddr());
+	iParent.PinRequestComplete();
+	return KErrNone;
+	}
+
+//------------------------------------------------------------------------//
+//class CBTAuthorisor
+//------------------------------------------------------------------------//
+
+CBTAuthorisor* CBTAuthorisor::NewL(CBTAccessRequester& aParent, TUid aServiceUID)
+	{
+	LOG_FUNC
+	CBTAuthorisor* s = CBTAuthorisor::NewLC(aParent, aServiceUID);
+	CleanupStack::Pop();
+	return s;
+	}
+
+CBTAuthorisor* CBTAuthorisor::NewLC(CBTAccessRequester& aParent, TUid aServiceUID)
+	{
+	LOG_FUNC
+	CBTAuthorisor* s = new(ELeave) CBTAuthorisor(aParent, aServiceUID);
+	CleanupStack::PushL(s);
+	s->ConstructL(aParent.DeviceAddress());
+	return s;
+	}
+
+CBTAuthorisor::CBTAuthorisor(CBTAccessRequester& aAccessRequester, TUid aServiceUID) :
+	CSecNotifierRequester(aAccessRequester.SecMan()),iAccessRequester(aAccessRequester)
+	{
+	LOG_FUNC
+	iAuthorisationParamsPckg().iUid = aServiceUID;
+	CActiveScheduler::Add(this);
+	}
+
+CBTAuthorisor::~CBTAuthorisor()
+	{
+	LOG_FUNC
+	Cancel();
+	}
+
+
+void CBTAuthorisor::DoUpdateNotifier()
+	{
+	LOG_FUNC 
+ 	if(IsActive())
+ 		{	
+ 		if(!iUpdateNotifierAO)
+   			{
+ 			//Create a new CSecNotifierUpdateAO object
+ 			TRAP_IGNORE(iUpdateNotifierAO = CSecNotifierUpdateAO::NewL(iNotifier, KBTManAuthNotifierUid));
+   			}
+	
+ 		if( (iUpdateNotifierAO) && (!iUpdateNotifierAO->IsActive()) )
+   			{
+ 			TBTNotifierUpdateParamsPckg pckg;
+ 			if(iDeviceName)
+ 				{
+ 				TRAPD(err, pckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ 				pckg().iResult = err; 	// Error code can be KErrNone 
+ 				if (err!=KErrNone)
+	 				{
+	 				pckg().iName = KNullDesC;
+	 				}
+ 				}
+ 			else
+ 				{
+ 				pckg().iName = KNullDesC;
+ 				pckg().iResult = KErrNotFound;
+ 				}
+ 				
+ 			iUpdateNotifierAO->DoUpdate(pckg);
+ 			}
+ 		}
+	}
+
+void CBTAuthorisor::DoRequest()
+/**
+Start the RNotifier plugin that deals with authorisation.
+**/
+	{
+	LOG_FUNC
+	TInt err(KErrNone);
+	
+	if (iDeviceName)
+		{
+		TRAP(err, iAuthorisationParamsPckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+		if (err!=KErrNone)
+			{
+			iAuthorisationParamsPckg().iName = KNullDesC;
+			}
+		}
+	else
+		{
+		iAuthorisationParamsPckg().iName = KNullDesC;
+		}			
+	iAuthorisationParamsPckg().iBDAddr  = iDevAddr;
+
+	iNotifier.StartNotifierAndGetResponse(iStatus, KBTManAuthNotifierUid, iAuthorisationParamsPckg, iResultPckg);
+	SetActive();
+	}
+
+
+void CBTAuthorisor::DoCancel()
+	{
+	LOG_FUNC
+
+	iNotifier.CancelNotifier(KBTManAuthNotifierUid);
+		
+	}
+
+void CBTAuthorisor::RunL()
+	{
+	LOG_FUNC
+	//unload the plugin
+	iNotifier.CancelNotifier(KBTManAuthNotifierUid);
+	//remove ourself from the notifier que, allowing the next notifier to be activated
+	iAccessRequester.SecMan().RemoveNotifierRequestFromQue(*this);
+	iIsAddedToNotifierQue = EFalse;
+	//check for errors + notify owner of completion
+	LOG1(_L("sec\tCBTAuthorisor::RunL(): iStatus = %d"), iStatus.Int());
+	if (iStatus.Int()!=KErrNone)
+		{
+		//error
+		iAccessRequester.CompleteRequest(iStatus.Int());
+		}
+	else
+		{
+		iAccessRequester.AuthorisationComplete(iResultPckg());
+		}
+	}
+
+TInt CBTAuthorisor::RunError(TInt aError)
+	{
+	LOG_FUNC
+	//will never get called as our RunL doesn't leave.
+	LOG1(_L("sec\tCBTAuthorisor::RunError(%d)"), aError);
+	return aError;
+	}
+
+
+/**
+	class CBTAccessRequester
+
+
+	The access requester handles the state machine involved in allowing accesses:
+	setting authentication, entering PINs, asking for authorisation and encrypting
+
+	This class deals only with security procedures we initiate (which could be for
+	inbound or outbound connections)
+
+*/
+
+CBTAccessRequester* CBTAccessRequester::NewLC(CPhysicalLink& aConnection,
+											  const TBTServiceSecurity& aSecurityRequired,
+  			  							      const TBTServiceSecurityPerDevice* const aOverride,
+											  MAccessRequestResponseHandler& aRequester,
+											  CBTSecMan& aParent)
+	{
+	LOG_FUNC
+	CBTAccessRequester* s = new(ELeave) CBTAccessRequester(aConnection, aSecurityRequired,
+															aOverride, aRequester, aParent);
+	CleanupStack::PushL(s);
+	s->ConstructL();
+	return s;
+	}
+
+
+CBTAccessRequester::CBTAccessRequester(CPhysicalLink& aConnection,
+									   const TBTServiceSecurity& aServiceSecurity,
+ 	  							       const TBTServiceSecurityPerDevice* const aOverride,
+									   MAccessRequestResponseHandler& aRequester,
+									   CBTSecMan& aParent) :
+	iRequester(aRequester),
+	iSecMan(aParent),
+	iBaseband(aConnection),
+	iServiceRequirements(aServiceSecurity),
+	iOverride(aOverride),
+	iIsSubscribedToConnection(EFalse),
+	iDeviceRetrievedFromRegistry(EFalse),
+	iQueLink(this),
+	iAuthenticationInProgress(EFalse)
+	{
+	LOG_FUNC
+	// try to get name for UI dialogs
+	SetDeviceName();
+	}
+
+void CBTAccessRequester::ConstructL()
+	{
+	LOG_FUNC
+	LOG2(_L("sec\tCBTAccessRequester 0x%08x constructed; sizeof %d"), this, sizeof(*this));
+	}
+
+void CBTAccessRequester::SetDeviceName()
+	{
+	LOG_FUNC
+	// not *that* bad - mostly inlines
+
+	iDeviceName = iSecMan.ConnectionsManager().
+					LinkManagerProtocol().InquiryMgr().
+					  DeviceNameFromCache(iBaseband.BDAddr());
+	}
+
+
+const MAccessRequestResponseHandler& CBTAccessRequester::ServiceRequester() const
+	{
+	LOG_FUNC
+	return iRequester;
+	}
+
+const TBTDevAddr& CBTAccessRequester::DeviceAddress() const
+	{
+	LOG_FUNC
+	return (iBaseband.BDAddr());
+	}
+
+void CBTAccessRequester::DoRequest()
+/**
+Subscribe to the baseband link notifier to find out about the link state.
+This will kick off the state machine.
+**/
+	{
+	LOG_FUNC
+	LOG1(_L("sec\tAccessRequester 0x%08x DoRequest"),this);
+	iBaseband.SubscribeLinkObserver(*this);
+	iIsSubscribedToConnection = ETrue;
+
+	// if the link is already up (e.g. we're SecMode 2 for L2CAP/RFCOMM) then proceed
+	if (iBaseband.IsConnected())
+		{
+		TBTBasebandEventNotification event(ENotifyPhysicalLinkUp);
+		PhysicalLinkChange(event); // kick statemachine
+		}
+	// else wait until it is retrieved
+	}
+
+void CBTAccessRequester::CompleteRequest(TInt aResult)
+	{
+	LOG_FUNC
+	LOG2(_L("sec\tAccessRequester 0x%08x CompleteRequest, result %d"),this,aResult);
+	iSecMan.AccessRequestComplete(this, aResult);
+	}
+
+CBTAccessRequester::~CBTAccessRequester()
+	{
+	LOG_FUNC
+	LOG1(_L("sec\tAccessRequester 0x%08x Destroying"),this);
+	if (iIsSubscribedToConnection)
+		{
+		iBaseband.UnsubscribeLinkObserver(*this);
+		}
+	delete iAuthorisor;
+	}
+
+
+void CBTAccessRequester::NewStateL()
+/**
+This access request has entered a new state.  Work out what we should do next.
+**/
+	{
+	LOG_FUNC
+	
+ 	// We should ensure that at each iteration through the state machine the requirements
+ 	// are up-to-date.  This is needed as the physical link may have changed
+ 	// some internal state e.g. as a result of the Registry having been modified.
+ 	iRequirements = OverallRequirements(iServiceRequirements, iBaseband.RemoteDevice());
+ 	
+#ifdef _DEBUG
+	_LIT(KRequestNone,					"Not Request.     ");
+	_LIT(KRequestPending,				"Request Pending. ");
+	_LIT(KRequestFailed,				"Request Failed.  ");
+	_LIT(KRequestComplete,				"Request Complete.");
+
+	TBuf<20> iStateName[4] = 
+		{
+		KRequestNone(),
+		KRequestPending(),
+		KRequestFailed(),
+		KRequestComplete(),
+		};
+
+	TBTDevAddr addr = iBaseband.BDAddr();
+	LOG3(_L("sec\tCBTAccessRequester::NewStateL(Authorised[%S] Authenticated[%S] Encrypted[%S])"),
+		&iStateName[iState.AuthorisationState()],
+		&iStateName[iState.AuthenticationState()], 
+		&iStateName[iState.EncryptionState()]);
+	LOG6(_L("\t\taddr[0x%02x%02x%02x%02x%02x%02x]"),
+		addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
+	LOG4(_L("\t\tRequirements: a[%d] p[%d] e[%d] d[%d]"), iRequirements.AuthenticationRequired(),
+		iRequirements.AuthorisationRequired(), iRequirements.EncryptionRequired(), iRequirements.Denied());
+#endif
+
+	if (iRequirements.AuthenticationRequired() && iRequirements.PasskeyMinLength() &&
+	       iBaseband.Authenticated() )
+		{
+		LOG(_L("sec\tCheck min passkey length")) // for remotly initiated connection
+		TBTPinCode passKey = iBaseband.PassKey();
+		if ( passKey().iLength < iRequirements.PasskeyMinLength())
+			{
+			// the remote device is authenticated, but longer passkey is required
+			LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED) Passkey min length requirement is longer, then current used for authentication"));
+			CompleteRequest(EBTSecManAccessDenied);
+			return;			
+			}		
+		}
+
+	//1. Check for any failures or for "Denied" in iRequirements
+	//If anything that was required has failed, complete the request with EBTManAccessDenied and delete this.
+	if ((iState.AuthenticationState() == TBTAccessRequestState::ERequestFailed && iRequirements.AuthenticationRequired()) 
+		|| (iState.EncryptionState() == TBTAccessRequestState::ERequestFailed && iRequirements.EncryptionRequired())
+		|| (iState.AuthorisationState() == TBTAccessRequestState::ERequestFailed && iRequirements.AuthorisationRequired())
+		|| iRequirements.Denied())
+		{
+		LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED)"));
+		CompleteRequest(EBTSecManAccessDenied);
+		}
+	//2. Check for authentication
+	else if (iRequirements.AuthenticationRequired() && 
+			 !iBaseband.Authenticated() && !iBaseband.Encrypted())
+		{
+		// might have been encrypted by remote side - so no need to reauthenticate
+		// this makes us more interoperable with controllers that don't honour erratum E2244
+		LOG(_L("sec\tAuthentication required..."))
+		//if we don't have a pending authentication request, start authentication.
+		if (iState.AuthenticationState() != TBTAccessRequestState::ERequestPending)
+			{
+			LOG(_L("sec\tStarting Authentication..."));
+			iState.SetAuthenticationState(TBTAccessRequestState::ERequestPending);
+			User::LeaveIfError(iBaseband.Authenticate());
+			}
+		}	
+	//3. Check for encryption
+	else if (iRequirements.EncryptionRequired() && !iBaseband.Encrypted())
+		{
+		LOG(_L("sec\tEncryption required..."))
+
+		if (!iBaseband.IsEncryptionDisabledForRoleSwitch())
+			{
+			//if we don't have a pending encryption request, start encryption.
+			if (iState.EncryptionState() != TBTAccessRequestState::ERequestPending)
+				{
+				LOG(_L("sec\tStarting Encryption..."))
+				iState.SetEncryptionState(TBTAccessRequestState::ERequestPending);
+				User::LeaveIfError(iBaseband.ChangeEncryption(EPointToPointEncryption));
+				}			
+			}
+		}
+	//4. Check for authorisation
+	else if (iRequirements.AuthorisationRequired() && 
+	        (iState.AuthorisationState() == TBTAccessRequestState::ERequestNone || iState.AuthorisationState() == TBTAccessRequestState::ERequestPending))
+		{
+		//if we don't have a pending authorisation request, start authorisation.
+		LOG(_L("sec\tAuthorisation required..."))
+		if (iState.AuthorisationState() != TBTAccessRequestState::ERequestPending)
+			{
+			LOG(_L("sec\tStarting Authorisation..."))
+			__ASSERT_ALWAYS(!iAuthorisor, User::Panic(KBTSecPanic,EBTSecAuthorisationRequestAlreadyExists));
+			iAuthorisor = CBTAuthorisor::NewL(*this, iServiceRequirements.Uid());
+			iState.SetAuthorisationState(TBTAccessRequestState::ERequestPending);
+			}
+		}
+	//5. Before EBTManAccessGranted, doublecheck all the checks were done
+	else
+		{
+		TBool authenticationComplete;
+		TBool encryptionComplete;
+		TBool authorisationComplete;
+		
+		authenticationComplete = !iRequirements.AuthenticationRequired() || 
+		                         (iRequirements.AuthenticationRequired() && 
+		                           (iBaseband.Authenticated() || iBaseband.Encrypted()));
+		                         
+		                         
+		                         
+		encryptionComplete = !iRequirements.EncryptionRequired() ||
+							 (iRequirements.EncryptionRequired() && iBaseband.Encrypted());
+							 
+							 
+		authorisationComplete = !iRequirements.AuthorisationRequired() ||
+								(iRequirements.AuthorisationRequired()  && iState.AuthorisationState()==TBTAccessRequestState::ERequestComplete);
+		
+		                
+		if (authenticationComplete && encryptionComplete && authorisationComplete)
+			{
+			LOG(_L("sec\tEBTManAccessGranted"))
+			CompleteRequest(EBTSecManAccessGranted);	
+			}
+		else
+			{
+			LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED) during double check"));
+			CompleteRequest(EBTSecManAccessDenied);			
+			}
+		}
+	}
+
+TBTAccessRequirements CBTAccessRequester::OverallRequirements(const TBTServiceSecurity& aServiceSecurity,
+														 const TBTNamelessDevice& aDevice)
+/**
+Take the access requirements of the service and compare them with the permissions given 
+to the device by the user.  This results in a list of tasks to be carried out by the 
+access requester before it may let the connection proceed.
+**/
+	{
+	LOG_FUNC
+	//Create the initial stab at the access requirements from the requirements of this service...
+	TBTAccessRequirements req;
+	req.SetAuthentication(aServiceSecurity.AuthenticationRequired());
+	req.SetAuthorisation(aServiceSecurity.AuthorisationRequired());
+	req.SetEncryption(aServiceSecurity.EncryptionRequired());
+	req.SetDenied(aServiceSecurity.Denied());
+	req.SetPasskeyMinLength(aServiceSecurity.PasskeyMinLength());
+
+	//Look at the global security setting of the device...
+	if (aDevice.IsValidGlobalSecurity())
+		{
+		LOG(_L("sec\tDevice has global security settings"));
+		TBTDeviceSecurity devSec = aDevice.GlobalSecurity();
+		if (devSec.Banned())
+			req.SetDenied(ETrue);
+		if (devSec.Encrypt())
+			req.SetEncryption(ETrue);
+		if (devSec.NoAuthenticate())
+			req.SetAuthentication(EFalse);
+		if (devSec.NoAuthorise())
+			req.SetAuthorisation(EFalse);
+		if (devSec.PasskeyMinLength() && devSec.PasskeyMinLength() > req.PasskeyMinLength())
+			req.SetPasskeyMinLength(devSec.PasskeyMinLength());
+		}
+
+	//Check to see if the device has anything specific about this service...
+
+	if (iOverride)
+		{
+		// get the device security from the override (the override points to the correct device)
+		LOG(_L("sec\tSAP has overriden global security settings for device"));
+		const TBTDeviceSecurity& servSec = iOverride->DeviceSecurity();
+		if (servSec.Banned())
+			req.SetDenied(ETrue);
+		if (servSec.Encrypt())
+			req.SetEncryption(ETrue);
+		if (servSec.NoAuthenticate())
+			req.SetAuthentication(EFalse);
+		if (servSec.NoAuthorise())
+			req.SetAuthorisation(EFalse);
+		if (servSec.PasskeyMinLength() && servSec.PasskeyMinLength() > req.PasskeyMinLength() )
+		    req.SetPasskeyMinLength(servSec.PasskeyMinLength());
+		}
+
+	// if only encryption is required, then must need authentication
+	if (req.EncryptionRequired())
+		{
+		req.SetAuthentication(ETrue);
+		}
+
+	return req;
+	}
+
+void CBTAccessRequester::FriendlyNameRetrieved(const TDesC& /*aName*/, TInt /*aResult*/)
+
+	{
+	LOG_FUNC
+	// do nothing for now
+	}
+
+void CBTAccessRequester::DeviceRetrieved(const TBTNamelessDevice& aDevice, TInt aError)
+/**
+	A device has been retrieved from the registry
+	If it's for us we need to continue the access request based on the overrides it has
+
+	This object chooses not to keep a copy of the device, but notes its availability
+**/
+	{
+	LOG_FUNC
+	if (aDevice.Address() != iBaseband.BDAddr())
+		{
+		return; // wasn't for us
+		}
+	
+	TInt err = aError;
+
+	iDeviceRetrievedFromRegistry = ETrue;
+
+	if (err == KErrNone || err == KErrNotFound)
+		{
+		// proceed with the security check...
+		iRequirements = OverallRequirements(iServiceRequirements, aDevice);
+		}
+
+	if (err!=KErrNone)
+		{
+		CompleteRequest(err);
+		}
+	}
+
+
+void CBTAccessRequester::AuthorisationComplete(TBool aResult)
+/**
+We have a new authorisation state.
+**/
+	{
+	LOG_FUNC
+	LOG1(_L("sec\tCBTAccessRequester::AuthorisationComplete(%d)"), aResult);
+	if (aResult)
+		{
+		iState.SetAuthorisationState(TBTAccessRequestState::ERequestComplete);
+		}
+	else
+		{
+		iState.SetAuthorisationState(TBTAccessRequestState::ERequestFailed);
+		}
+	TRAPD(err,NewStateL());
+	if (err)
+		{
+		CompleteRequest(err);
+		}
+	}
+
+
+void CBTAccessRequester::PhysicalLinkChange(const TBTBasebandEventNotification & aEvent, CPhysicalLink& /*aPhysicalLink*/)
+	{
+	LOG_FUNC
+	// only forward events that secman is interested in
+	// linkup, linkdown, encryption, authentication, error
+	// Care needed: other events may harm operation of secman 
+	// and open security hole, such as ENotifySniffMode, ENotifyParkMode
+ 	// and ENotifyHoldMode
+ 	TBTPhysicalLinkStateNotifier secmanEvents = static_cast<TBTPhysicalLinkStateNotifier>
+ 	           (ENotifyPhysicalLinkUp |
+ 	            ENotifyPhysicalLinkDown |
+	            ENotifyPhysicalLinkError |
+	            ENotifyAuthenticationComplete |
+	            ENotifyEncryptionChangeOn |
+ 	            ENotifyEncryptionChangeOff);
+		            
+	if (aEvent.EventType() & secmanEvents)
+		{  
+		PhysicalLinkChange(aEvent); 		}
+ 	// else drop
+	}
+
+void CBTAccessRequester::PhysicalLinkChange(const TBTBasebandEventNotification & aEvent)
+	{
+	LOG_FUNC
+	if(aEvent.EventType() & (ENotifyPhysicalLinkDown | ENotifyPhysicalLinkError))
+		{
+		CompleteRequest(aEvent.ErrorCode());
+		return;
+		}
+
+	if (iState.AuthenticationState() == TBTAccessRequestState::ERequestPending && 
+		aEvent.EventType() == ENotifyAuthenticationComplete)
+		{
+		LOG(_L("sec\tAuthentication complete..."))
+
+		if(aEvent.ErrorCode() != KErrNone)
+			{
+			iState.SetAuthenticationState(TBTAccessRequestState::ERequestFailed);
+			//if Auth fail then ensure any previously stored Linkkey is removed
+			//Don't know which AccessRequester initiated this so can't complete
+			//the request with the error code, only thing to do is trap this error
+			//Deleting a LinkKey is not Critical merely cosmetic
+			LOG(_L("CPhysicalLink: Deleting link key due to failed Authentication"))
+			TRAP_IGNORE(iBaseband.DeleteLinkKeyL());
+			}
+		else
+			{
+			iState.SetAuthenticationState(TBTAccessRequestState::ERequestComplete);
+			}
+		}
+
+	if (iState.EncryptionState() == TBTAccessRequestState::ERequestPending && 
+		(aEvent.EventType() == ENotifyEncryptionChangeOn || aEvent.EventType() == ENotifyEncryptionChangeOff))
+		{
+		LOG(_L("sec\tEncryption Change complete..."))
+		
+		// We can try again as there was a race with some other PHY modification
+		// Curiously, the firmware always return ELMPErrorTransactionCollision (0x23) for both
+		// kinds of transaction collisions (0x23,0x2a), we guard against both situations here
+		// anyway just to be safe.
+		if(aEvent.ErrorCode() == ELMPErrorTransactionCollision || aEvent.ErrorCode() == EDifferentTransactionCollision)
+			{	
+			// This will force the state machine logic to try sending the command again
+			iState.SetEncryptionState(TBTAccessRequestState::ERequestNone);
+			}
+		else if(aEvent.ErrorCode() != KErrNone)
+			{
+			
+			iState.SetEncryptionState(TBTAccessRequestState::ERequestFailed);
+			}
+		else
+			{
+			iState.SetEncryptionState(TBTAccessRequestState::ERequestComplete);
+			}
+		}
+
+	TRAPD(err,NewStateL());
+	if (err)
+		{
+		CompleteRequest(err);
+		}
+	}
+
+
+CBTSecMan& CBTAccessRequester::SecMan()
+	{
+	LOG_FUNC
+	return iSecMan;
+	}
+
+TBool CBTAccessRequester::IsAuthenticationReqPending(const TBTDevAddr& aAddr, TUint& aPasskeyMinLength)
+	{
+	LOG_FUNC
+		if (iState.AuthenticationState() == TBTAccessRequestState::ERequestPending &&
+		    iAuthenticationInProgress && aAddr == iBaseband.BDAddr())
+			{
+			aPasskeyMinLength = iServiceRequirements.PasskeyMinLength();
+			return ETrue;
+			}
+		else 
+			{
+			aPasskeyMinLength = 0;
+			return EFalse;
+			}
+	}
+
+TBool CBTAccessRequester::AuthenticationRequired() const
+	{
+	LOG_FUNC
+	return iRequirements.AuthenticationRequired();
+	}
+
+TBool CBTAccessRequester::AuthenticationInProgress() const
+	{
+	LOG_FUNC
+	return iAuthenticationInProgress;
+	}
+
+void  CBTAccessRequester::SetAuthenticationInProgress()
+	{
+	LOG_FUNC
+	iAuthenticationInProgress = ETrue;
+	}
+	
+	 
+ //------------------------------------------------------------------------//
+ //class CSecNotifierUpdateAO
+ //------------------------------------------------------------------------//
+ 
+CSecNotifierUpdateAO* CSecNotifierUpdateAO::NewL(RNotifier& aNotifier, TUid aNotifierUid)
+ 	{
+	LOG_FUNC
+ 	CSecNotifierUpdateAO* s = CSecNotifierUpdateAO::NewLC(aNotifier, aNotifierUid);
+ 	CleanupStack::Pop();
+ 	return s;
+ 	}
+ 
+CSecNotifierUpdateAO* CSecNotifierUpdateAO::NewLC(RNotifier& aNotifier, TUid aNotifierUid)
+ 	{
+	LOG_FUNC
+ 	LOG(_L("sec\tCSecNotifierUpdateAO::NewLC()"));
+ 	CSecNotifierUpdateAO* s = new(ELeave) CSecNotifierUpdateAO();
+ 	CleanupStack::PushL(s);
+ 	s->ConstructL(aNotifier, aNotifierUid);
+ 	return s;
+ 	}
+ 
+CSecNotifierUpdateAO::CSecNotifierUpdateAO()
+: CActive(EPriorityStandard)
+ 	{
+	LOG_FUNC
+ 	CActiveScheduler::Add(this);
+ 	}
+ 
+CSecNotifierUpdateAO::~CSecNotifierUpdateAO()
+ 	{
+	LOG_FUNC
+ 	Cancel();
+ 	}
+ 
+void CSecNotifierUpdateAO::ConstructL(RNotifier& aNotifier, TUid aNotifierUid)
+ 	{
+	LOG_FUNC
+ 	iNotifier = aNotifier;
+ 	iNotifierUid = aNotifierUid;	
+ 	}
+ 	
+void CSecNotifierUpdateAO::DoUpdate(const TBTNotifierUpdateParamsPckg& aPckg)
+ 	{
+	LOG_FUNC
+ 	//Retain a copy so that it does not go out of memory scope
+ 	iPckg = aPckg;
+ 	
+ 	//we're not expecting an answer...
+	iNotifier.UpdateNotifierAndGetResponse(iStatus, iNotifierUid, iPckg, iAnswer);
+ 	SetActive();	
+ 	}
+ 	
+void CSecNotifierUpdateAO::RunL()
+ 	{
+	LOG_FUNC
+ 	//We can't do anything if an error is returned - just make sure we haven't done anything stupid...	
+ 	__ASSERT_DEBUG((iStatus==KErrNone)||(iStatus==KErrNoMemory)||(iStatus==KErrNotReady), User::Panic(KBTSecPanic, EBTSecBadNotifierUpdate));
+ 	}
+ 	
+void CSecNotifierUpdateAO::DoCancel()
+ 	{
+	LOG_FUNC
+ 	LOG(_L("sec\tCSecNotifierUpdateAO::DoCancel()"));
+	iNotifier.CancelNotifier(iNotifierUid); // no other API on Notifier to just cancel the update; but typically we'll want to cancel the whole notifier at this point(?)
+	}
+ 	
+TInt CSecNotifierUpdateAO::RunError(TInt aError)
+ 	{
+	LOG_FUNC
+ 	LOG1(_L("sec\tCSecNotifierUpdateAO::RunError(%d)"), aError);
+ 	
+ 	#ifndef __FLOGGING__
+ 		aError += 0; //Remove Compiler warning
+ 	#endif
+ 	 
+ 	return KErrNone;	
+ 	}