bluetooth/btstack/linkmgr/roleswitchhelper.cpp
branchRCL_3
changeset 25 99439b07e980
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/linkmgr/roleswitchhelper.cpp	Tue Sep 14 23:28:24 2010 +0300
@@ -0,0 +1,518 @@
+// Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Symbian Foundation License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+
+#include <bluetooth/logger.h>
+#include "roleswitchhelper.h"
+#include "physicallinksmanager.h"
+#include "AclDataQController.h"
+#include "ProxySAP.h"
+#include "linkmgr.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR);
+#endif
+
+CRoleSwitcher::CRoleSwitcher(CPhysicalLinksManager& aLinkMgr, CPhysicalLink& aLink, TBTBasebandRole aRole)
+	: CPhysicalLinkHelper(aLinkMgr, aLink)
+	, iRole(aRole)
+	, iIsEncryptionDisabledForRoleSwitch(EFalse)
+	{
+	LOG_FUNC
+	iState = &iLinkMgr.RoleSwitcherStateFactory().GetState(CRoleSwitcherStateFactory::EIdle);
+	}
+
+CRoleSwitcher* CRoleSwitcher::NewL(CPhysicalLinksManager& aLinkMgr, CPhysicalLink& aLink, TBTBasebandRole aRole)
+	{
+	LOG_STATIC_FUNC
+	CRoleSwitcher* self = new(ELeave) CRoleSwitcher(aLinkMgr, aLink, aRole);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+void CRoleSwitcher::ConstructL()
+	{
+	LOG_FUNC
+	
+	BaseConstructL();
+	
+	// add ourselves to the list in LinkMgr, LinkMgr will kick off the role change state machine 	
+	iLinkMgr.AddRoleSwitcher(*this);
+	iAddedToLinkMgr = ETrue;
+	}
+	
+CRoleSwitcher::~CRoleSwitcher()
+	{
+	LOG_FUNC
+	if (iAddedToLinkMgr)
+		{
+		iLinkMgr.RemoveRoleSwitcher(*this);
+		}
+	}
+
+void CRoleSwitcher::TimerExpired()
+	{
+	LOG_FUNC
+	
+	iState->TimerExpired(*this);
+	}
+
+void CRoleSwitcher::HandleError( TInt aError)
+	{
+	LOG_FUNC
+	
+	iState->Error(*this, aError);
+	}
+
+void CRoleSwitcher::DisableEncryption()
+  	{
+	LOG_FUNC
+ 	// data traffic suspended
+	iLinkMgr.LinkManagerProtocol().ACLController().SetParked(iLink.Handle(), ETrue);
+	NotifyBasebandEvent(ENotifyEncryptionChangeOff);
+	iLinkMgr.Encrypt(EFalse, iLink);
+
+	// set flag here, it's too late when we receive the event as AccessReqester
+	// might receive the baseband notification earlier then the flag is set!	
+	iIsEncryptionDisabledForRoleSwitch = ETrue;
+  	}
+
+void CRoleSwitcher::EnableEncryption()
+  	{
+	LOG_FUNC  	
+	NotifyBasebandEvent(ENotifyEncryptionChangeOn);
+	iLinkMgr.Encrypt(ETrue, iLink);	
+	// data traffic is enabled in IoctlComplete
+  	}
+   	
+void CRoleSwitcher::ChangeRole()
+	{
+	LOG_FUNC
+	NotifyBasebandEvent(ENotifyAnyRole);
+	iLinkMgr.ChangeRole(iRole, iLink);
+	}
+
+void CRoleSwitcher::StartHelper()
+	{
+	LOG_FUNC
+	iState->Start(*this);
+	}
+		
+void CRoleSwitcher::SaveEncryption()
+	{
+	LOG_FUNC
+	iIsEncrypted = iLink.Encrypted();
+	}
+
+void CRoleSwitcher::LogRoleSwitchSuccessful(TBTBasebandEventNotification& aEvent) const
+	{
+	LOG_FUNC
+	TInt eventType;
+	eventType = (iRole == EMaster ? ENotifyMaster :ENotifySlave);
+	
+	if (aEvent.EventType()==eventType && aEvent.ErrorCode()==KErrNone)
+		{
+		LOG(_L("CRoleSwitcher RoleSwitch OK"));	
+		}
+	else 
+		{
+		LOG(_L("CRoleSwitcher RoleSwitch failed"));
+		}
+	}
+	
+void CRoleSwitcher::EventReceived(TBTBasebandEventNotification& aEvent)
+	{
+	LOG_FUNC
+	iState->EventReceived(*this, aEvent);
+	}
+		
+
+//----------------------------------------------------------------------------------
+// STATE FACTORY
+//----------------------------------------------------------------------------------
+
+CRoleSwitcherStateFactory* CRoleSwitcherStateFactory::NewL()
+	{
+	LOG_STATIC_FUNC
+	CRoleSwitcherStateFactory* ret=new (ELeave) CRoleSwitcherStateFactory();
+	CleanupStack::PushL(ret);
+	ret->ConstructL();
+	CleanupStack::Pop(ret);
+	return ret;
+	}
+
+void CRoleSwitcherStateFactory::ConstructL()
+	{
+	LOG_FUNC	
+	iStates[EIdle]					=new (ELeave) TRSStateIdle(*this);
+	iStates[EDisablingLPM]			=new (ELeave) TRSStateDisablingLPM(*this);
+	iStates[EDisablingEncryption]	=new (ELeave) TRSStateDisablingEncryption(*this);
+	iStates[EChangingRole]			=new (ELeave) TRSStateChangingRole(*this);
+	iStates[EChangingRoleWithEPR]	=new (ELeave) TRSStateChangingRoleWithEPR(*this);
+	iStates[EEnablingEncryption]	=new (ELeave) TRSStateEnablingEncryption(*this);
+	}
+
+CRoleSwitcherStateFactory::CRoleSwitcherStateFactory()
+	{
+	LOG_FUNC
+	iStates.DeleteAll();
+	}
+
+TRoleSwitcherState& CRoleSwitcherStateFactory::GetState(CRoleSwitcherStateFactory::TRoleSwitcherStates aState)
+	{
+	LOG_FUNC
+	__ASSERT_DEBUG(iStates[aState],  Panic(ERoleSwitcherInvalidState));
+	return *iStates[aState];
+	}
+
+TInt CRoleSwitcherStateFactory::StateIndex(const TRoleSwitcherState* aState) const
+	{
+	LOG_FUNC
+	TInt state;
+	for (state = 0; state < ERoleSwitcherMaxState; state++)
+		{
+		if (iStates[state] == aState)
+			{
+			return state;
+			}
+		}
+	
+	return KUnknownState;
+	}
+
+
+//----------------------------------------------------------------------------------
+// STATES
+//----------------------------------------------------------------------------------
+
+TRoleSwitcherState::TRoleSwitcherState(CRoleSwitcherStateFactory& aFactory)
+: iFactory(aFactory)
+	{
+	LOG_FUNC
+	}
+
+void TRoleSwitcherState::PanicInState(TLinkPanic aPanic) const
+	{
+	LOG_FUNC
+	Panic(aPanic, iFactory.StateIndex(this));
+	}
+
+void TRoleSwitcherState::ChangeState(CRoleSwitcher& aContext, CRoleSwitcherStateFactory::TRoleSwitcherStates aState) const
+	{
+	LOG_FUNC
+	
+	aContext.iState->Exit(aContext);
+
+#ifdef __FLOG_ACTIVE
+	TRoleSwitcherState* state=&iFactory.GetState(aState);
+	LOG2(_L("RoleSwitcher: State %S -> %S"), &aContext.iState->iName, &state->iName);
+#endif //__FLOG_ACTIVE
+	aContext.iState=&iFactory.GetState(aState);
+
+	aContext.iState->Enter(aContext);
+	}
+
+void TRoleSwitcherState::Enter(CRoleSwitcher& /*aContext*/) const
+	{
+	LOG_FUNC
+	// do nothing
+	}
+
+void TRoleSwitcherState::Exit(CRoleSwitcher& /*aContext*/) const
+	{
+	LOG_FUNC
+	// do nothing
+	}
+
+void TRoleSwitcherState::Start(CRoleSwitcher& /*aContext*/) const
+	{
+	LOG_FUNC
+	PanicInState(ERoleSwitcherStateMachineInvalidEvent);
+	}
+
+void TRoleSwitcherState::Error(CRoleSwitcher& aContext, TInt /*aErr*/) const
+	{
+	LOG_FUNC
+	aContext.CancelNotify();
+	aContext.RemoveTimer();
+	ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+	}
+
+void TRoleSwitcherState::EventReceived(CRoleSwitcher& /*aContext*/, TBTBasebandEventNotification& /*aEvent*/) const
+	{
+	LOG_FUNC
+	// do nothing
+	}
+
+void TRoleSwitcherState::TimerExpired(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+	}
+
+//----------------------------------------------------------------------------------
+
+TRSStateIdle::TRSStateIdle(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateIdle");
+	}
+
+void TRSStateIdle::Start(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.QueueTimer(KTimeoutRoleSwitch);	// watchdog timer
+	ChangeState(aContext, CRoleSwitcherStateFactory::EDisablingLPM);
+	}	
+
+void TRSStateIdle::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	// async call to delete the helper
+	aContext.iLink.AsyncDeleteRoleSwitcher();
+	}	
+
+//----------------------------------------------------------------------------------
+
+TRSStateDisablingLPM::TRSStateDisablingLPM(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateDisablingLPM");
+	}
+
+void TRSStateDisablingLPM::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	// DisableLPM even if link is active to prevent possible LPM requests during encryption disabling
+
+	if (aContext.iLink.LinkMode() == EActiveMode)
+		{
+		aContext.DisableLPM();
+		if (aContext.IsEPRSupported())
+			{
+			ChangeState(aContext, CRoleSwitcherStateFactory::EChangingRoleWithEPR);
+			}
+		else
+			{
+			ChangeState(aContext, CRoleSwitcherStateFactory::EDisablingEncryption);
+			}
+		// don't wait for notification
+		}
+	else
+		{
+		aContext.NotifyBasebandEvent(ENotifyActiveMode);
+		aContext.DisableLPM();
+		}
+	}
+
+void TRSStateDisablingLPM::EventReceived(CRoleSwitcher& aContext, TBTBasebandEventNotification& aEvent) const
+	{
+	LOG_FUNC
+	if (aEvent.EventType()==ENotifyActiveMode && aEvent.ErrorCode()==KErrNone)
+		{
+		if (aContext.IsEPRSupported())
+			{
+			ChangeState(aContext, CRoleSwitcherStateFactory::EChangingRoleWithEPR);
+			}
+		else
+			{
+			ChangeState(aContext, CRoleSwitcherStateFactory::EDisablingEncryption);
+			}
+		}
+	else 
+		{
+		LOG(_L("CRoleSwitcher RoleSwitch failed in DisableLPM"));
+		// we can quit SM, don't need to rewind
+		ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+		}
+	}
+
+//----------------------------------------------------------------------------------
+TRSStateDisablingEncryption::TRSStateDisablingEncryption(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateDisablingEncryption");
+	}
+
+void TRSStateDisablingEncryption::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.SaveEncryption();
+	if (aContext.iIsEncrypted)
+		{
+		aContext.DisableEncryption();
+		}
+	else
+		{
+		ChangeState(aContext, CRoleSwitcherStateFactory::EChangingRole);
+		}
+	}
+
+void TRSStateDisablingEncryption::EventReceived(CRoleSwitcher& aContext, TBTBasebandEventNotification& aEvent) const
+	{
+	LOG_FUNC
+	if (aEvent.EventType()==ENotifyEncryptionChangeOff && aEvent.ErrorCode()==KErrNone)
+		{
+		ChangeState(aContext, CRoleSwitcherStateFactory::EChangingRole);
+		}
+	else 
+		{
+		LOG(_L("CRoleSwitcher RoleSwitch failed in DisableEncryption"));
+		// before quiting SM , try to enable LPM
+		ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+		}
+	}
+
+void TRSStateDisablingEncryption::TimerExpired(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.CancelNotify();
+	ChangeState(aContext, CRoleSwitcherStateFactory::EEnablingEncryption);
+	}
+
+//----------------------------------------------------------------------------------
+TRSStateChangingRole::TRSStateChangingRole(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateChangingRole");
+	}
+
+void TRSStateChangingRole::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.ChangeRole();
+	}
+
+void TRSStateChangingRole::EventReceived(CRoleSwitcher& aContext, TBTBasebandEventNotification& __DEBUG_ONLY(aEvent)) const
+	{
+	LOG_FUNC
+	aContext.RemoveTimer();	// cancel watchdog timer
+
+	FTRACE(aContext.LogRoleSwitchSuccessful(aEvent));
+	
+
+	ChangeState(aContext, CRoleSwitcherStateFactory::EEnablingEncryption);
+	}
+
+void TRSStateChangingRole::TimerExpired(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.CancelNotify();
+	ChangeState(aContext, CRoleSwitcherStateFactory::EEnablingEncryption);
+	}
+
+//----------------------------------------------------------------------------------
+TRSStateChangingRoleWithEPR::TRSStateChangingRoleWithEPR(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateChangingRoleWithEPR");
+	}
+
+void TRSStateChangingRoleWithEPR::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.ChangeRole();
+	}
+
+void TRSStateChangingRoleWithEPR::EventReceived(CRoleSwitcher& aContext, TBTBasebandEventNotification& __DEBUG_ONLY(aEvent)) const
+	{
+	LOG_FUNC
+	aContext.RemoveTimer();	// cancel watchdog timer
+
+	FTRACE(aContext.LogRoleSwitchSuccessful(aEvent));
+		
+	ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+	}
+
+void TRSStateChangingRoleWithEPR::TimerExpired(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	aContext.CancelNotify();
+	ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+	}
+
+//----------------------------------------------------------------------------------
+TRSStateEnablingEncryption::TRSStateEnablingEncryption(CRoleSwitcherStateFactory& aFactory)
+: TRoleSwitcherState(aFactory)
+	{
+	LOG_FUNC
+	STATENAME("TRSStateEnablingEncryption");
+	}
+
+void TRSStateEnablingEncryption::Enter(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	if (aContext.iIsEncrypted)
+		{
+		aContext.QueueTimer(KTimeoutOneCommand);
+		aContext.EnableEncryption();
+		}
+	else
+		{
+		ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+		}
+	}
+	
+void TRSStateEnablingEncryption::Exit(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	if (aContext.iIsEncrypted)
+		{
+		// enable data traffic
+		aContext.iLinkMgr.LinkManagerProtocol().ACLController().SetParked(aContext.iLink.Handle(), EFalse);
+		}
+	}
+
+void TRSStateEnablingEncryption::EventReceived(CRoleSwitcher& aContext, TBTBasebandEventNotification& aEvent) const
+	{
+	LOG_FUNC
+	aContext.RemoveTimer(); // watchdog timer
+	if (aEvent.EventType()==ENotifyEncryptionChangeOn && aEvent.ErrorCode()==KErrNone)
+		{	
+		ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+		aContext.iIsEncryptionDisabledForRoleSwitch = EFalse;
+		}
+	else 
+		{
+		LOG(_L("CRoleSwitcher SetEncryption failed, disconnect link"));
+		if (aContext.iLink.Terminate(ERemoteUserEndedConnection) != KErrNone) 
+			{
+			LOG(_L("CRoleSwitcher OOM"));
+			}
+		ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+		}
+	}
+
+void TRSStateEnablingEncryption::TimerExpired(CRoleSwitcher& aContext) const
+	{
+	LOG_FUNC
+	LOG(_L("CRoleSwitcher Timeout in EncryptionEnable, disconnect"));
+	aContext.CancelNotify();			
+	if (aContext.iLink.Terminate(ERemoteUserEndedConnection) != KErrNone)
+			{
+			LOG(_L("CRoleSwitcher OOM"));
+			}
+	ChangeState(aContext, CRoleSwitcherStateFactory::EIdle);
+	}
+
+
+