diff -r 000000000000 -r 29b1cd4cb562 bluetoothcommsprofiles/btpan/panagt/panagtremdevstates.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothcommsprofiles/btpan/panagt/panagtremdevstates.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,2246 @@ +// Copyright (c) 2004-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: +// + +/** + @file + @note PAN agent remote connection state implementations +*/ + +#include +#include +#include "panagtremdevstates.h" +#include "panagtpolicy.h" +using namespace PanAgent; + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_PAN_AGENT); +#endif + +// +// CPanRemDevStatePerformingSdpQuery +// + +CPanRemDevStatePerformingSdpQuery::CPanRemDevStatePerformingSdpQuery(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStatePerformingSdpQuery), iPanDevRolesListPtr(StateMachine().RemoteRolesList()) +/** + +*/ + { + } + +void CPanRemDevStatePerformingSdpQuery::OnEntryL() +/** +Check out roles in commdb, perform SDP query if necessary, then check it's OK +@note If SDP queries are inhibited by the setting in commdb, this class will just accept whatever is in there - if there isn't anything then the SDP query will be performed anyway +*/ + { + // perform SDP query to see what the remote device supports + User::LeaveIfError(StateMachine().RemoteSdpQuerier().Query(StateMachine().RemSockAddr().BTAddr(), iPanDevRolesListPtr, StateMachine().Status())); + StateMachine().SetActive(); + } + +void CPanRemDevStatePerformingSdpQuery::AsyncEventCompleteL() +/** +The SDP query has completed +*/ + { + //The PAN spec says a device supporting PANU does not have to advertise that on SDP. + //But the spec is a bit vague on if such a device is connectable, so we try to connect + //to it anyway to maximise interoperability + if(StateMachine().Status()==KErrNone || StateMachine().Status()==KErrNotFound) + { + // If remote doesn't support GN, there is no point try it, so set it to Not WorthTrying + if(!StateMachine().RemoteRolesList().SupportsGn()) + { + StateMachine().RemoteWorthTryingRolesList().SetWorthTryingGn(EFalse); + } + // If remote doesn't support NAP, there is no point try it, so set it to Not WorthTrying + if(!StateMachine().RemoteRolesList().SupportsNap()) + { + StateMachine().RemoteWorthTryingRolesList().SetWorthTryingNap(EFalse); + } + + // check whether FixedRemoteRole is supported by remote according to SDP query result + if(StateMachine().WorthTrying()) + { + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateConnectingSocket(StateMachine()); + LOG1(_L("RemDevState[%x]: - SDP query completed successfully, moving to state"), &StateMachine()); + StateMachine().SetState(*nextState); + delete this; + } + else + { + LOG1(_L("RemDevState[%x]: - SDP query completed successfully, but remote doesn't support the Fixed Remote Role we've specified."), &StateMachine()); + User::Leave(KErrFixRemoteRoleIsNotSupportedByRemote); + } + } + else // error handling + { + LOG1(_L("RemDevState[%x]: - SDP query failed"), &StateMachine()); + User::Leave(StateMachine().Status().Int()); + } + } + +void CPanRemDevStatePerformingSdpQuery::AsyncEventCancelL() +/** +The SDP query has been cancelled +*/ + { + LOG1(_L("RemDevState[%x]: - SDP query cancelled"), &StateMachine()); + + StateMachine().RemoteSdpQuerier().CancelQuery(); + } + +void CPanRemDevStatePerformingSdpQuery::ShutdownL() +/** +The local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStatePerformingSdpQuery::GetState() const + { + return EIdle; + } + +// +// CPanRemDevStateConnectingSocket +// + +CPanRemDevStateConnectingSocket::CPanRemDevStateConnectingSocket(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateConnectingSocket) +/** + +*/ + { } + +void CPanRemDevStateConnectingSocket::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + TBTServiceSecurity securitySettings; + // we override whatever security settings that came with the address with our own + securitySettings.SetAuthentication(KPanOutgoignAuthenticationRequired); + securitySettings.SetAuthorisation(KPanOutgoingAuthorisationRequired); + securitySettings.SetEncryption(KPanOutgoingEncryptionRequired); + securitySettings.SetUid(KBTPanAuthorisationUid); + + StateMachine().RemSockAddr().SetSecurity(securitySettings); + +#if defined(_DEBUG) && defined(SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY) + TInt err = StateMachine().Socket().Open(KBTAddrFamily, KSockSeqPacket, KL2CAP); + __ASSERT_DEBUG(err != KErrBadName, PanicInState(EPanAgtMissingBinding)); + User::LeaveIfError(err); +#else + User::LeaveIfError(StateMachine().Socket().Open(KBTAddrFamily, KSockSeqPacket, KL2CAP)); +#endif + + TUint16 bnepMtu = KBnepMtu; + TPckg mtuBuf(bnepMtu); + + User::LeaveIfError(StateMachine().Socket().SetOpt(KL2CAPInboundMTU, KSolBtL2CAP, mtuBuf)); + User::LeaveIfError(StateMachine().Socket().SetOpt(KL2CAPNegotiatedOutboundMTU, KSolBtL2CAP, mtuBuf)); + // Register the CoD service - Networking bit (especially relevant for WinXP) + User::LeaveIfError(StateMachine().Socket().SetOpt(KBTRegisterCodService, KSolBtSAPBase, EMajorServiceNetworking)); + + StateMachine().Socket().Connect(StateMachine().RemSockAddr(), StateMachine().Status()); + StateMachine().SetActive(); + } + +void CPanRemDevStateConnectingSocket::AsyncEventCompleteL() +/** +Called when the new socket is connected +*/ + { + if(StateMachine().Status()==KErrNone) + { + LOG1(_L("RemDevState[%x]: - async event complete, socket connected successfully"), &StateMachine()); + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + StateMachine().CreateNewBnepConnection(StateMachine().Socket(), TPanMessage::EActivityCreateChannelControllerForOutgoing); +#else + StateMachine().CreateNewBnepConnectionL(StateMachine().Socket()); +#endif + + StateMachine().OpenPhysicalLinkAdapterL(); + + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection(StateMachine()); + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + CleanupStack::PushL(nextState); + CPanRemDevStatePaused* pausedState = new(ELeave)CPanRemDevStatePaused(StateMachine(), *nextState); + CleanupStack::Pop(); + nextState = pausedState; +#endif + + StateMachine().SetState(*nextState); + delete this; + } + else // did not complete the connection to the remote device... + { + LOG2(_L("RemDevState[%x]: - socket connect failed with %d"), &StateMachine(), StateMachine().Status().Int()); + User::Leave(StateMachine().Status().Int()); + } + } + +void CPanRemDevStateConnectingSocket::AsyncEventCancelL() +/** +Cancel the socket connect +*/ + { + LOG1(_L("RemDevState[%x]: - async event cancelled"), &StateMachine()); + + StateMachine().Socket().CancelConnect(); + StateMachine().Socket().Close(); + } + +void CPanRemDevStateConnectingSocket::ShutdownL() +/** +The local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateConnectingSocket::GetState() const + { + return EIdle; + } + +// +// CPanRemDevStateReconnectingSocket +// + +CPanRemDevStateReconnectingSocket::CPanRemDevStateReconnectingSocket(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateConnectingSocket) + { } + +/** + Disconnect the socket for the case where the remote device doesn't +*/ +void CPanRemDevStateReconnectingSocket::OnEntryL() + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + StateMachine().Socket().Close(); + + StateMachine().SetRetryConnect(); + } + +/** + Wait for device to disconnect before attempting reconnect +*/ +void CPanRemDevStateReconnectingSocket::RemoteDeviceDisconnectL(TInt /*aErr*/) + { + LOG1(_L("RemDevState[%x]: - remote device disconnected"), &StateMachine()); + StateMachine().DisconnectBnepChannel(); + TBTServiceSecurity securitySettings; + // we override whatever security settings that came with the address with our own + securitySettings.SetAuthentication(KPanOutgoignAuthenticationRequired); + securitySettings.SetAuthorisation(KPanOutgoingAuthorisationRequired); + securitySettings.SetEncryption(KPanOutgoingEncryptionRequired); + securitySettings.SetUid(KBTPanAuthorisationUid); + + StateMachine().RemSockAddr().SetSecurity(securitySettings); + +#if defined(_DEBUG) && defined(SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY) + TInt err = StateMachine().Socket().Open(KBTAddrFamily, KSockSeqPacket, KL2CAP); + __ASSERT_DEBUG(err != KErrBadName, PanicInState(EPanAgtMissingBinding)); + User::LeaveIfError(err); +#else + User::LeaveIfError(StateMachine().Socket().Open(KBTAddrFamily, KSockSeqPacket, KL2CAP)); +#endif + + TUint16 bnepMtu = KBnepMtu; + TPckg mtuBuf(bnepMtu); + + User::LeaveIfError(StateMachine().Socket().SetOpt(KL2CAPInboundMTU, KSolBtL2CAP, mtuBuf)); + User::LeaveIfError(StateMachine().Socket().SetOpt(KL2CAPNegotiatedOutboundMTU, KSolBtL2CAP, mtuBuf)); + + StateMachine().Socket().Connect(StateMachine().RemSockAddr(), StateMachine().Status()); + StateMachine().SetActive(); + } + +/** +Called when the new socket is connected +*/ +void CPanRemDevStateReconnectingSocket::AsyncEventCompleteL() + { + if(StateMachine().Status()==KErrNone) + { + LOG1(_L("RemDevState[%x]: - async event complete, socket connected successfully"), &StateMachine()); + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + StateMachine().CreateNewBnepConnection(StateMachine().Socket(), TPanMessage::EActivityCreateChannelControllerForOutgoing); +#else + StateMachine().CreateNewBnepConnectionL(StateMachine().Socket()); +#endif + + StateMachine().OpenPhysicalLinkAdapterL(); + + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection(StateMachine()); + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY + CleanupStack::PushL(nextState); + CPanRemDevStatePaused* pausedState = new(ELeave)CPanRemDevStatePaused(StateMachine(), *nextState); + CleanupStack::Pop(); + nextState = pausedState; +#endif + + StateMachine().SetState(*nextState); + delete this; + } + else // did not complete the connection to the remote device... + { + LOG2(_L("RemDevState[%x]: - socket connect failed with %d"), &StateMachine(), StateMachine().Status().Int()); + User::Leave(StateMachine().Status().Int()); + } + } + +/** +Cancel the socket connect +*/ +void CPanRemDevStateReconnectingSocket::AsyncEventCancelL() + { + LOG1(_L("RemDevState[%x]: - async event cancelled"), &StateMachine()); + + StateMachine().Socket().CancelConnect(); + StateMachine().Socket().Close(); + } + +/** +The local device wants us to disconnect +*/ +void CPanRemDevStateReconnectingSocket::ShutdownL() + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateReconnectingSocket::GetState() const + { + return EIdle; + } + +// +// CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection +// + +CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection) +/** + +*/ + { } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::OnEntryL() +/** +Not much to do here - just waiting for role state machine to become available +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // do nothing + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +Oi! We're starting the connection here, so we get to send the role request! +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request received from remote device - they shouldn't be sending them, we're doing the outgoing connection, disconnecting them"), &StateMachine()); + + // drop the role request packet that the remote device has incorrectly sent us + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +Excuse me? We haven't sent a role request yet, so why are they sending a role response? +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response received from remote device before we've sent a request - disconnecting them"), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::RemoteDeviceDisconnectL(TInt aError) +/** +Remote device has obviously given up waiting for us... +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, looks like they've given up waiting for us."), &StateMachine()); + + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::ReadyForRoleRequestL() +/** +Ahh, that's the event we've really been waiting for - role state machine has finished dealing with +another device and is now ready for our role request +*/ + { + LOG1(_L("RemDevState[%x]: - ready for role request received from role state machine"), &StateMachine()); + + TInt err; + CPanRemDevStateBase* nextState = NULL; + StateMachine().RemoteRole() = EPanRoleUnknown; // reset this for outgoing connection - we maybe trying a different role now + err = StateMachine().InitiateOutgoingConnection(StateMachine().LocalRole(), StateMachine().RemoteRole(), StateMachine().RemoteWorthTryingRolesList()); + + switch(err) + { + case KErrNone: + { + err = PerformMasterSlaveSwitchIfNecessary(StateMachine().LocalRole()); + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection(StateMachine()); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + User::Leave(KErrCouldNotBecomePiconetMaster); + return; + } + + LOG1(_L("RemDevState[%x]: - ...M/S switch not required, sending role request."), &StateMachine()); + + nextState = new(ELeave) CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection(StateMachine()); + break; + } + case KErrLocked: + { + PanicInState(ERoleStateMachineCalledReadyForRoleRequestThenReturnedLocked); + break; + } + case KErrInvalidOrUnacceptableRoleCombination: + { + LOG1(_L("RemDevState[%x]: - the roles the remote device proposed were invalid or unacceptable to us, disconnecting them"), &StateMachine()); + User::Leave(KErrInvalidOrUnacceptableRoleCombination); + } + default: + { + // if err is something other than KErrNoMemory that might indicate an internal error in the state + // machine, so ASSERT_DEBUG. We can probably carry on even if the error is unexpected, as leaving + // will cause this connection to shutdown. + __ASSERT_DEBUG(err == KErrNoMemory, PanicInState(EPanAgentRoleStateMachineReturnedUnexpectedErrorCode)); + User::Leave(err); + } + } + + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::ShutdownL() +/** +Local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + User::Leave(KErrLocallyInitiatedDisconnect); // disconnect the remote device + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection::GetState() const + { + return EPendingNegotiation; + } + +// +// CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection +// + +CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection) +/** + +*/ + { } + +CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStateBase(aStateMachine, aStateNumber) +/** +When implementing a new state that derives from this base class, ensure that +it provides a unique number to identify the state from other possible +states. @see PanAgent::TPanAgtStates. +*/ + { } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::OnEntryL() +/** +Not much to do here - just waiting for role state machine to become available +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // do nothing + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request received from remote device, still can't do anything with it until we get access to the role state machine"), &StateMachine()); + + // drop resent role request + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +@note Shouldn't be called for incoming connections, as they should be sending requests, not responses +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response received from remote device, what are they up to? They should be sending requests and we should be sending responses, disconnecting them"), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::RemoteDeviceDisconnectL(TInt aError) +/** +Remote device has obviously given up waiting for us... +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, looks like they've given up waiting for us."), &StateMachine()); + + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::ReadyForRoleRequestL() +/** +Ahh, that's the event we've really been waiting for - role state machine has finished dealing with +another device and is now ready for our role request +*/ + { + LOG1(_L("RemDevState[%x]: - ready for role request received from role state machine"), &StateMachine()); + + TInt err; + CPanRemDevStateBase* nextState = NULL; + err = StateMachine().IncomingConnectionFromPeer(StateMachine().LocalRole(), StateMachine().RemoteRole()); + + switch(err) + { + case KErrNone: + { + err = PerformMasterSlaveSwitchIfNecessary(StateMachine().LocalRole()); + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForIncomingConnection(StateMachine()); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + StateMachine().SendRoleResponse(EConnectionNotAllowed); + User::Leave(KErrCouldNotBecomePiconetMaster); + return; + } + + LOG1(_L("RemDevState[%x]: - ...M/S switch not required, sending successful connect response."), &StateMachine()); + StateMachine().SendRoleResponse(EOperationSuccessful); + + nextState = new(ELeave) CPanRemDevStateIncomingNotification(StateMachine()); + break; + } + case KErrLocked: + { + PanicInState(ERoleStateMachineCalledReadyForRoleRequestThenReturnedLocked); + break; + } + + default: + { + // An error has occured. Send a -ve role response and disconnect. + LOG2(_L("RemDevState[%x]: - Fail with error [%d]. Disconnecting peer"), &StateMachine(), err); + StateMachine().SendRoleResponse(EConnectionNotAllowed); + User::Leave(err); + } + } + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::ShutdownL() +/** +Local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // tell the other end that we're disconnecting + StateMachine().SendRoleResponse(EConnectionNotAllowed); + // and then disconnect the remote device + User::Leave(KErrLocallyInitiatedDisconnect); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection::GetState() const + { + return EPendingNegotiation; + } + +// +// CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest +// + +CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest::CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest(MPanRemDevStateMachineNotify& aStateMachine, TBluetoothPanRole aPendingLocalRole, TBluetoothPanRole aPendingRemoteRole) + : CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection(aStateMachine, EPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest), + iPendingLocalRole(aPendingLocalRole), + iPendingRemoteRole(aPendingRemoteRole) +/** + +*/ + { } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest::OnEntryL() +/** +Not much to do here - just waiting for role state machine to become available +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // do nothing + } + +void CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest::ReadyForRoleRequestL() +/** +Ahh, that's the event we've really been waiting for - role state machine has finished dealing with +another device and is now ready for our role request +*/ + { + LOG1(_L("RemDevState[%x]: - ready for role request received from role state machine"), &StateMachine()); + + TInt err; + CPanRemDevStateBase* nextState = NULL; + err = StateMachine().RoleChangeRequestFromPeer(iPendingLocalRole, iPendingRemoteRole); + switch(err) + { + case KErrNone: + { + err = PerformMasterSlaveSwitchIfNecessary(iPendingLocalRole); + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest(StateMachine(), iPendingLocalRole, iPendingRemoteRole); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + StateMachine().SendRoleResponse(EConnectionNotAllowed); + StateMachine().RoleChangeFailed(); + nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + break; + } + + LOG1(_L("RemDevState[%x]: - ...M/S switch not required, sending successful connect response."), &StateMachine()); + StateMachine().SendRoleResponse(EOperationSuccessful); + + // Store the new roles + StateMachine().LocalRole() = iPendingLocalRole; + StateMachine().RemoteRole() = iPendingRemoteRole; + + // Changing role from GN/NAP to U. If this connection previously had access to the + // uplink this should be revoked. + if(StateMachine().LocalRole() == EPanRoleU) + { + StateMachine().SetUplinkAccessAllowed(EFalse); + } + + nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + break; + } + case KErrLocked: + { + PanicInState(ERoleStateMachineCalledReadyForRoleRequestThenReturnedLocked); + break; + } + case KErrInvalidOrUnacceptableRoleCombination: + { + LOG1(_L("RemDevState[%x]: - the roles the remote device proposed were invalid or unacceptable to us, signalling failure and going to active state"), &StateMachine()); + StateMachine().SendRoleResponse(EConnectionNotAllowed); + nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + break; + } + default: + { + // if err is something other than KErrNoMemory that might indicate an internal error in the state + // machine, so ASSERT_DEBUG. We can probably carry on even if the error is unexpected, as leaving + // will cause this connection to shutdown. + __ASSERT_DEBUG(err == KErrNoMemory, PanicInState(EPanAgentRoleStateMachineReturnedUnexpectedErrorCode)); + User::Leave(err); + } + } + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +// +// CPanRemDevStatePerformingRoleNegotiationForOutgoingBase +// + +CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::CPanRemDevStatePerformingRoleNegotiationForOutgoingBase(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStateBase(aStateMachine, aStateNumber), iRetries(KMaxBnepNegotiationCyles) +/** + +*/ + { } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +Something's gone wrong - we were expecting a response from the remote device - not a request. +Not sure this can/should occur - I think the spec implies that only the initiating device can send role requests. +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request from remote device received - what are they doing? We're supposed to be sending the role requests."), &StateMachine()); + + // drop the role request packet that the remote device has incorrectly sent us + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage aRoleResponseCode) +/** +Woohoo! Our role response has arrived. +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response from remote device received, processing response..."), &StateMachine()); + + StateMachine().Cancel(); + + CPanRemDevStateBase* nextState = NULL; + switch(aRoleResponseCode) + { + case EOperationSuccessful: + { + LOG1(_L("RemDevState[%x]: - ...operation successful, setting device active."), &StateMachine()); + StateMachine().ResetConnectionRetryAttempts(); // reset the connection retry attempts + StateMachine().ResetRetryParameters(); // clears the triedpanu... type flags incase we want to reconnect later + nextState = DoRoleNegotiationSuccessL(); + break; + } + case EConnectionNotAllowed: + { + LOG1(_L("RemDevState[%x]: - ...operation failed, connection not allowed"), &StateMachine()); + TInt retries = StateMachine().IncrementConnectionRetryAttempts(); + // There is a race condition in some PC stacks that can bring us to this point when it shouldn't, so we retry the connection + if (retries - ...operation failed, connection not allowed - retrying connection"), &StateMachine()); + StateMachine().ResetRetryParameters(); // This feeds all the way back so we recalculate the role parameters just the same as we did last time + nextState = new(ELeave) CPanRemDevStateReconnectingSocket(StateMachine()); + + // if the state has left, then it won't have performed the setup of the next state correctly + // this code performs the same actions to cleanup the old state and activate the new, but in + // reverse order - otherwise we'd lose the pointer to the old state when we activated the new + } + /* the intention is to allow the retrys required by the PC stacks above + then if it's worth retrying (we can try new roles) we will + This is particularly useful when we don't have sdp records to suggest + what we should do + */ + else if (StateMachine().WorthTrying()) + { + // if we've still not made a successful connection then we can have a quick try + // at connecting using different remote roles + LOG1(_L("RemDevState[%x]: - ...operation failed, connection not allowed - retrying connection with different roles"), &StateMachine()); + nextState = new(ELeave) CPanRemDevStateReconnectingSocket(StateMachine()); + + // if the state has left, then it won't have performed the setup of the next state correctly + // this code performs the same actions to cleanup the old state and activate the new, but in + // reverse order - otherwise we'd lose the pointer to the old state when we activated the new + } + else + { + LOG1(_L("RemDevState[%x]: - ...operation failed, connection not allowed"), &StateMachine()); + LOG1(_L("RemDevState[%x]: - ...operation failed, unrecognised response code, shutting down"), &StateMachine()); + StateMachine().ResetConnectionRetryAttempts(); // reset the connection retry attempts + StateMachine().ResetRetryParameters(); // clears the triedpanu... type flags incase we want to try connect later + nextState = DoRoleNegotiationFailureL(KErrCouldNotConnect); + } + break; + } + default: + { + // this nested switch avoids the old multiple logging lines which was really nasty + switch(aRoleResponseCode) + { + case EInvalidDestinationServiceUuid: // we've tried to negotiate roles that are unsuitable + { + LOG1(_L("RemDevState[%x]: - ...operation failed, invalid destination service UUID"), &StateMachine()); + break; + } + case EInvalidSourceServiceUuid: + { + LOG1(_L("RemDevState[%x]: - ...operation failed, invalid source service UUID"), &StateMachine()); + break; + } + case EInvalidServiceUuidSize: + { + LOG1(_L("RemDevState[%x]: - ...operation failed, invalid UUID size"), &StateMachine()); + break; + } + default: + { + LOG2(_L("RemDevState[%x]: - ...operation failed, unrecognised response code (%x), shutting down"), &StateMachine(),aRoleResponseCode); + break; + } + } + StateMachine().ResetConnectionRetryAttempts(); // reset the connection retry attempts + StateMachine().ResetRetryParameters(); // clears the triedpanu... type flags incase we want to try connect later + nextState = DoRoleNegotiationFailureL(KErrCouldNotConnect); + break; + } + } + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::RemoteDeviceDisconnectL(TInt aError) +/** +Remote device has disconnected. +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, looks like our requests weren't acceptable, or they just gave up"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::ShutdownL() +/** +Local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); // will shutdown the connection + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::AsyncEventCompleteL() +/** +Timer completion +*/ + { + --iRetries; + + LOG2(_L("RemDevState[%x]: - async event complete, looks like the timer triggered before they responded. %d retries remaining..."), &StateMachine(), iRetries); + + if(iRetries==0) + { + LOG1(_L("RemDevState[%x]: - ...disconnecting."), &StateMachine()); + CPanRemDevStateBase* nextState = DoRoleNegotiationFailureL(KErrRemoteDeviceFailedToRespondToRoleRequests); + StateMachine().SetState(*nextState); + delete this; + return; + } + else // resend the role request, and start the timer running again + { + LOG1(_L("RemDevState[%x]: - ...retrying."), &StateMachine()); + StateMachine().SendRoleRequest(StateMachine().LocalRole(), StateMachine().RemoteRole()); + iTimer.After(StateMachine().Status(), KMaxTimeToWaitForRoleResponse); + StateMachine().SetActive(); + } + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::AsyncEventCancelL() +/** +Timer cancel +@note Typically it will be this state that cancels the async event (timer in this case) when we decide we don't need it any longer +*/ + { + LOG1(_L("RemDevState[%x]: - async event cancelled, cancelling timer"), &StateMachine()); + + iTimer.Cancel(); + iTimer.Close(); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStatePerformingRoleNegotiationForOutgoingBase::GetState() const + { + return EPerformingNegotiation; + } + +// +// CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection +// + +CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection::CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStatePerformingRoleNegotiationForOutgoingBase(aStateMachine, EPanRemDevStatePerformingRoleNegotiationForOutgoingConnection) +/** + +*/ + { } + +CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection::CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStatePerformingRoleNegotiationForOutgoingBase(aStateMachine, aStateNumber) +/** +When implementing a new state that derives from this base class, ensure that +it provides a unique number to identify the state from other possible +states. @see PanAgent::TPanAgtStates. +*/ + { } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection::OnEntryL() +/** +Send the role request to the remote device +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + User::LeaveIfError(iTimer.CreateLocal()); + + TInt err; + CPanRemDevStateBase* nextState = NULL; + StateMachine().RemoteRole() = EPanRoleUnknown; // reset this for outgoing connection - we maybe trying a different role now + err = StateMachine().InitiateOutgoingConnection(StateMachine().LocalRole(), StateMachine().RemoteRole(), StateMachine().RemoteWorthTryingRolesList()); + + switch(err) + { + case KErrNone: + { + LOG3(_L("RemDevState[%x]: - role state machine changed our proposed role; now local: %x, remote: %x"), &StateMachine(), StateMachine().LocalRole(), StateMachine().RemoteRole()); + err = PerformMasterSlaveSwitchIfNecessary(StateMachine().LocalRole()); + + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection(StateMachine()); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + User::Leave(KErrCouldNotBecomePiconetMaster); + return; + } + + StateMachine().SendRoleRequest(StateMachine().LocalRole(), StateMachine().RemoteRole()); + iTimer.After(StateMachine().Status(), KMaxTimeToWaitForRoleResponse); + StateMachine().SetActive(); + return; + } + case KErrLocked: // someone else is negotiating, so go to the wait state + { + LOG1(_L("RemDevState[%x]: - role state machine busy, waiting for notification of its availability"), &StateMachine()); + nextState = new(ELeave) CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForOutgoingConnection(StateMachine()); + break; + } + + case KErrInvalidOrUnacceptableRoleCombination: + LOG1(_L("RemDevState[%x]: - this state generated and proposed an illegal role combination"), &StateMachine()); + // fall through + + default: + LOG1(_L("RemDevState[%x]: - unexpected response from role state machine"), &StateMachine()); + // if err is something other than KErrNoMemory that might indicate an internal error in the state + // machine, so ASSERT_DEBUG. We can probably carry on even if the error is unexpected, as leaving + // will cause this connection to shutdown. + User::Leave(err); + } + + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +CPanRemDevStateBase* CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection::DoRoleNegotiationSuccessL() +/** +Role negotiation succeeded, tell role state machine and go to active state +*/ + { + LOG1(_L("RemDevState[%x]: - role negotiation succeeded, going to active state"), &StateMachine()); + + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + return nextState; + } + +CPanRemDevStateBase* CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection::DoRoleNegotiationFailureL(TInt aError) +/** +Role negotiation failed, disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - role negotiation failed, shutting down connection"), &StateMachine()); + User::Leave(aError); + return NULL; // will never be reached + } + +// +// CPanRemDevStatePerformingRoleNegotiationForIncomingConnection +// + +CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::CPanRemDevStatePerformingRoleNegotiationForIncomingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStatePerformingRoleNegotiationForIncomingConnection) +/** + +*/ + { } + +void CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // do nothing - just wait for incoming role request + } + +void CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::BnepRoleRequestFromRemoteDeviceL(const TUUID& aRequestedLocalRole, const TUUID& aRequestedRemoteRole) +/** +This is what we're waiting for... +*/ + { +// LOG1(_L("RemDevState[%x]: - BNEP role request from remote device, they propose local: %S, remote: %S"), &StateMachine(), (aRequestedLocalRole.ShortestForm()), (aRequestedRemoteRole.ShortestForm())); + + TInt err; + CPanRemDevStateBase* nextState = NULL; + + TBluetoothPanRole localRole; + TBluetoothPanRole remoteRole; + ConvertUuidsToPanRolesL(aRequestedLocalRole, aRequestedRemoteRole, localRole, remoteRole); // will send response to remote device and shutdown the connection if there is an error + + LOG1(_L("RemDevState[%x]: - checking role request..."), &StateMachine()); + + err = StateMachine().IncomingConnectionFromPeer(localRole, remoteRole); + + // store the requested roles, so the next state knows what was asked for + StateMachine().LocalRole() = localRole; + StateMachine().RemoteRole() = remoteRole; + + switch(err) + { + case KErrNone: + { + LOG1(_L("RemDevState[%x]: - ...successful, checking to see if M/S switch necessary..."), &StateMachine()); + + err = PerformMasterSlaveSwitchIfNecessary(localRole); + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForIncomingConnection(StateMachine()); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + StateMachine().SendRoleResponse(EConnectionNotAllowed); + User::Leave(KErrCouldNotBecomePiconetMaster); + return; + } + + LOG1(_L("RemDevState[%x]: - ...M/S switch not required, sending successful connect response."), &StateMachine()); + StateMachine().SendRoleResponse(EOperationSuccessful); + + // Check if the incoming notifier is required. + nextState = new(ELeave) CPanRemDevStateIncomingNotification(StateMachine()); + break; + } + case KErrLocked: + { + LOG1(_L("RemDevState[%x]: - ...role state machine busy, waiting."), &StateMachine()); + // and create the "waiting" state + nextState = new (ELeave) CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingConnection(StateMachine()); + break; + } + + default: + { + // An error has occured. Send a -ve role response and disconnect. + LOG2(_L("RemDevState[%x]: - Fail with error [%d]. Disconnecting peer"), &StateMachine(), err); + StateMachine().SendRoleResponse(EConnectionNotAllowed); + User::Leave(err); + } + } + + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + } + +void CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +Something's gone wrong - we were expecting a request from the remote device - not a response. +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role reponse from remote device - what are they up to? They're supposed to be sending the role request."), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +void CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::RemoteDeviceDisconnectL(TInt aError) +/** +Remote device has disconnected +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, they've obviously given up talking to us."), &StateMachine()); + + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::ShutdownL() +/** +Local device wants us to disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + User::Leave(KErrLocallyInitiatedDisconnect); // will shutdown the connection + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStatePerformingRoleNegotiationForIncomingConnection::GetState() const + { + return EPerformingNegotiation; + } + +// +// CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange +// + +CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange::CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStatePerformingRoleNegotiationForOutgoingBase(aStateMachine, EPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange) +/** + +*/ + { } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange::OnEntryL() + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + User::LeaveIfError(iTimer.CreateLocal()); + + // It may appear as though we ought to do a new SDP query, rather than relying on a potentially + // stale one, but in reality if we're being asked to renegotiate, then the roles are pretty much + // going to be known by the role state machine, so we just ask it + TInt err = StateMachine().PerformLocalRoleChangeRequest(StateMachine().LocalRole(), StateMachine().RemoteRole()); + switch(err) + { + case KErrNone: + { + LOG3(_L("RemDevState[%x]: - role state machine changed our proposed role; now local: %x, remote: %x"), &StateMachine(), StateMachine().LocalRole(), StateMachine().RemoteRole()); + + // send the role request and wait for a response first, so the remote device + // understands that we're trying to role switch the baseband because we want to be GN/NAP, + // and releases their piconet master lock + StateMachine().SendRoleRequest(StateMachine().LocalRole(), StateMachine().RemoteRole()); + + // start waiting for the role response + iTimer.After(StateMachine().Status(), KMaxTimeToWaitForRoleResponse); + StateMachine().SetActive(); + return; + } + case KErrLocked: + // this must not happen - if the state machine requests that we renegotiate, then it needs to be ready for us + { + PanicInState(ERoleStateMachineRequestedWeRenegotiateRolesThenReturnedLocked); + } + case KErrInvalidOrUnacceptableRoleCombination: + // Unable to find a valid role combination to send to the peer device. fall through + + default: + LOG1(_L("RemDevState[%x]: - unexpected response from role state machine"), &StateMachine()); + User::Leave(err); + break; + } + } + +void CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage aRoleResponseCode) +/** +The role response has arrived from the peer device. +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response from remote device received, processing response..."), &StateMachine()); + + StateMachine().Cancel(); + + CPanRemDevStateBase* nextState = NULL; + switch(aRoleResponseCode) + { + case EOperationSuccessful: + { + LOG1(_L("RemDevState[%x]: - ...operation successful, setting device active."), &StateMachine()); + nextState = DoRoleNegotiationSuccessL(); + break; + } + default: + { + LOG2(_L("RemDevState[%x]: - ...operation failed [result code = %d], connection not allowed"), &StateMachine(), aRoleResponseCode); + nextState = DoRoleNegotiationFailureL(KErrCouldNotConnect); + break; + } + }; + + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +CPanRemDevStateBase* CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange::DoRoleNegotiationSuccessL() +/** +Role negotiation succeeded, decide whether we need to perform a baseband role switch +*/ + { + LOG1(_L("RemDevState[%x]: - role negotiation succeeded, deciding whether we need to role switch"), &StateMachine()); + + TInt err; + // start going through the M/S switch routine + err = PerformMasterSlaveSwitchIfNecessary(StateMachine().LocalRole()); + + CPanRemDevStateBase* nextState = NULL; + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest(StateMachine()); + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another + // profile it's using...) + // Whilst the spec says we should return to previous roles when this role upgrade fails, + // we're now in the situation that we're GN/NAP, they're U, and we can't become the piconet + // master - so the best/most reliable/only way to recover is to disconnect them. Not + // entirely according-to-spec, but then they shouldn't let us become master then refuse + // to role switch! + User::Leave(KErrCouldNotBecomePiconetMaster); + } + else + { + nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + } + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + return nextState; + } + +CPanRemDevStateBase* CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange::DoRoleNegotiationFailureL(TInt /*aError*/) +/** +Role negotiation failed, return to active state +*/ + { + LOG1(_L("RemDevState[%x]: - role negotiation failed, returning to active state"), &StateMachine()); + + // let the role state machine know that the role change failed + StateMachine().RoleChangeFailed(); + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + return nextState; + } + +// +// CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection +// + +CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection::CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStatePerformingRoleNegotiationForOutgoingConnection(aStateMachine, EPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection) +/** + +*/ + { } + +void CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection::OnEntryL() +/** +Send the role request and start the timer +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + User::LeaveIfError(iTimer.CreateLocal()); + + // send the role request to the remote device + StateMachine().SendRoleRequest(StateMachine().LocalRole(), StateMachine().RemoteRole()); + iTimer.After(StateMachine().Status(), KMaxTimeToWaitForRoleResponse); + StateMachine().SetActive(); + } + +// +// CPanRemDevStateWaitingForRoleSwitchBase +// + +CPanRemDevStateWaitingForRoleSwitchBase::CPanRemDevStateWaitingForRoleSwitchBase(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStateBase(aStateMachine, aStateNumber) +/** + +*/ + { } + +CPanRemDevStateWaitingForRoleSwitchBase::~CPanRemDevStateWaitingForRoleSwitchBase() +/** +We own an object, so need our own destructor here to delete it +(this is the only state that does/needs this) +*/ + { + delete iTimerHelper; + iTimerHelper = NULL; + } + +void CPanRemDevStateWaitingForRoleSwitchBase::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + //Proceed to the next state if role switch is not required + TUint32 state; + StateMachine().PhysicalLinkAdapter().PhysicalLinkState(state); + if(state & ENotifyMaster) + { + CPanRemDevStateBase *nextState = DoRoleSwitchSuccessL(); + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + + // create and start the timer helper object + iTimerHelper = CPanAgtTimerHelper::NewL(*this); + iTimerHelper->SetTimer(KMaxTimeToWaitForBasebandRoleSwitch); + + // subscribe to the link status change event notification + iEventNotification() = ENotifyMaster; // must set this to the event we're interested in, otherwise we don't see it + StateMachine().PhysicalLinkAdapter().NotifyNextBasebandChangeEvent(iEventNotification, StateMachine().Status()); + StateMachine().SetActive(); + } + +void CPanRemDevStateWaitingForRoleSwitchBase::RemoteDeviceDisconnectL(TInt aError) +/** +Oh well, they've given up +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, they've obviously given up talking to us."), &StateMachine()); + // outstanding async request will be cancelled by state machine when we leave + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStateWaitingForRoleSwitchBase::ShutdownL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); // will shutdown the connection + } + +void CPanRemDevStateWaitingForRoleSwitchBase::AsyncEventCompleteL() +/** +Timer or baseband change event notification completion +*/ + { + CPanRemDevStateBase* nextState = NULL; + if(StateMachine().Status()==KErrNone) + { + if(iTimerCompleted) // work out whether the timer fired or the baseband change event notification completed + { + // timer fired + LOG1(_L("RemDevState[%x]: - timer fired, giving up and disconnecting remote device"), &StateMachine()); + + // cancel the outstanding request for baseband change event notification + // need to cancel it directly, as the timer completes the state machine AO (through + // the TimerComplete() and TimerCancel() methods, so just calling + // StateMachine().Cancel() won't have any effect + StateMachine().PhysicalLinkAdapter().CancelNextBasebandChangeEventNotifier(); + + User::WaitForRequest(StateMachine().Status()); // have to catch the KErrCancel manually, as we're not active at the moment + + nextState = DoRoleSwitchFailureL(KErrNone); + } + else + { + // Baseband change event notification completed + if(iEventNotification().EventType() & ENotifyMaster) + { + LOG2(_L("RemDevState[%x]: - received link event %32b, now piconet master - sending role response and going to active state"), &StateMachine(), iEventNotification().EventType()); + + // cancel the timer + iTimerHelper->Cancel(); // need to cancel it directly, as the state machine AO is + // no longer active, so just cancelling all async events + // through the state machine won't work + + // success - get this state to do it's stuff + // they *must* return the next state + nextState = DoRoleSwitchSuccessL(); + } + else // event that we're not interested in, repost 'baseband change event notification' request + { + iEventNotification() = ENotifyMaster; // must set this to the event we're interested in, otherwise we don't see it + StateMachine().PhysicalLinkAdapter().NotifyNextBasebandChangeEvent(iEventNotification, StateMachine().Status()); + LOG2(_L("RemDevState[%x]: - received link event %32b, throwing away"), &StateMachine(), iEventNotification().EventType()); + StateMachine().SetActive(); + return; + } + } + } + else + { + StateMachine().Cancel(); + nextState = DoRoleSwitchFailureL(StateMachine().Status().Int()); + } + + __ASSERT_ALWAYS(nextState, PanicInState(ENoNextStateSet)); + StateMachine().SetState(*nextState); + delete this; + return; + } + +void CPanRemDevStateWaitingForRoleSwitchBase::AsyncEventCancelL() +/** +Cancel all outstanding async events +@note This only works when the state machine AO is active - ie. calling StateMachine().Cancel() +from other code is only any good when *both* timer and baseband change event notification are outstanding, because the +completion of either will mark the state machine AO as inactive +*/ + { + iTimerHelper->Cancel(); + StateMachine().PhysicalLinkAdapter().CancelNextBasebandChangeEventNotifier(); + } + +void CPanRemDevStateWaitingForRoleSwitchBase::TimerComplete() +/** +The timer has completed +*/ + { + iTimerCompleted = ETrue; + TRequestStatus* status = &StateMachine().Status(); + User::RequestComplete(status, KErrNone); + } + +void CPanRemDevStateWaitingForRoleSwitchBase::TimerError(TInt aError) +/** +There was a problem with the timer +*/ + { + iTimerCompleted = ETrue; + TRequestStatus* status = &StateMachine().Status(); + User::RequestComplete(status, aError); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateWaitingForRoleSwitchBase::GetState() const + { + return EPerformingNegotiation; + } + +// +// CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection +// + +CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateWaitingForRoleSwitchBase(aStateMachine, EPanRemDevStateWaitingForRoleSwitchForOutgoingConnection) +/** + +*/ + { } + +CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStateWaitingForRoleSwitchBase(aStateMachine, aStateNumber) +/** +When implementing a new state that derives from this base class, ensure that +it provides a unique number to identify the state from other possible +states. @see PanAgent::TPanAgtStates. +*/ + { } + +void CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // call base class's method + CPanRemDevStateWaitingForRoleSwitchBase::OnEntryL(); + } + +void CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +Ignore it, we're waiting for them to role switch +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request from remote device: ignoring, as we're waiting for baseband role switch"), &StateMachine()); + + // do nothing + } + +void CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +Err, what's going on? +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response from remote device: huh? What are they up to? This is an incoming connection, so they're supposed to be sending the requests, not the responses"), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::DoRoleSwitchSuccessL() +/** +It worked - now perform the role negotiation +*/ + { + LOG1(_L("RemDevState[%x]: - role switch succeeded"), &StateMachine()); + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStatePerformingDelayedRoleNegotiationForOutgoingConnection(StateMachine()); + return(nextState); + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::DoRoleSwitchFailureL(TInt aError) +/** +Shutdown the connection +*/ + { + LOG1(_L("RemDevState[%x]: - role switch failed, shutting down"), &StateMachine()); + if(aError==KErrNone) // the timer has fired + { + User::Leave(KErrCouldNotBecomePiconetMaster); + } + else // a more general error occured + { + User::Leave(aError); + } + return NULL; // should never be reached + } + + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection::GetState() const + { + return EPendingNegotiation; + } + +// +// CPanRemDevStateWaitingForRoleSwitchForIncomingConnection +// + +CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::CPanRemDevStateWaitingForRoleSwitchForIncomingConnection(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateWaitingForRoleSwitchBase(aStateMachine, EPanRemDevStateWaitingForRoleSwitchForIncomingConnection) +/** + +*/ + { } + +CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::CPanRemDevStateWaitingForRoleSwitchForIncomingConnection(MPanRemDevStateMachineNotify& aStateMachine, TPanRemDevStates aStateNumber) : + CPanRemDevStateWaitingForRoleSwitchBase(aStateMachine, aStateNumber) +/** +When implementing a new state that derives from this base class, ensure that +it provides a unique number to identify the state from other possible +states. @see PanAgent::TPanAgtStates. +*/ + { } + +void CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // call base class's method + CPanRemDevStateWaitingForRoleSwitchBase::OnEntryL(); + } + +void CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +Ignore it, we're waiting for them to role switch +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request from remote device: ignoring, as we're waiting for baseband role switch"), &StateMachine()); + + // ignore resent role request + } + +void CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +Err, what's going on? +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response from remote device: huh? What are they up to? This is an incoming connection, so they're supposed to be sending the requests, not the responses"), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::DoRoleSwitchSuccessL() +/** +It worked - tell the remote device and go to active state +*/ + { + LOG1(_L("RemDevState[%x]: - role switch succeeded"), &StateMachine()); + + // tell the remote device the connection was successful + StateMachine().SendRoleResponse(EOperationSuccessful); + // let the role state machine know that this connection is now active + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateIncomingNotification(StateMachine()); + return(nextState); + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForIncomingConnection::DoRoleSwitchFailureL(TInt aError) +/** +Shutdown the connection +*/ + { + LOG1(_L("RemDevState[%x]: - role switch failed, shutting down"), &StateMachine()); + + //send response DEF068158 + StateMachine().SendRoleResponse(EConnectionNotAllowed); + //and cancel next base band change event notifier DEF067708 + + StateMachine().PhysicalLinkAdapter().CancelNextBasebandChangeEventNotifier(); + + + if(aError==KErrNone) // the timer has fired + { + User::Leave(KErrCouldNotBecomePiconetMaster); + } + else // a more general error occured + { + User::Leave(aError); + } + return NULL; // should never be reached + } + +// +// CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest +// + +CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest::CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateWaitingForRoleSwitchForOutgoingConnection(aStateMachine, EPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest) +/** + +*/ + { } + +void CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // call base class's method + CPanRemDevStateWaitingForRoleSwitchBase::OnEntryL(); + } + +void CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +They've responded to our role request twice! +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response from remote device, they've already replied once - disconnecting them!"), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest::DoRoleSwitchSuccessL() +/** +We've managed to role switch, so the role upgrade has completed successfully +*/ + { + LOG1(_L("RemDevState[%x]: - role switch succeeded, going to active state"), &StateMachine()); + + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + return nextState; + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForOutgoingRoleChangeRequest::DoRoleSwitchFailureL(TInt /*aError*/) +/** +Role switch failed, but this was a role change so return to old role in active state +*/ + { + LOG1(_L("RemDevState[%x]: - role change failed, disconnecting remote device"), &StateMachine()); + + // Whilst the spec says we should return to previous roles when this role upgrade fails, + // we're now in the situation that we're GN/NAP, they're U, and we can't become the piconet + // master - so the best/most reliable/only way to recover is to disconnect them. Not + // entirely according-to-spec, but then they shouldn't let us become master then refuse + // to role switch! + User::Leave(KErrCouldNotBecomePiconetMaster); + return NULL; // will never get here + } + +// +// CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest +// + +CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest::CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest(MPanRemDevStateMachineNotify& aStateMachine, TBluetoothPanRole aPendingLocalRole, TBluetoothPanRole aPendingRemoteRole) + : CPanRemDevStateWaitingForRoleSwitchForIncomingConnection(aStateMachine, EPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest), + iPendingLocalRole(aPendingLocalRole), + iPendingRemoteRole(aPendingRemoteRole) + +/** + +*/ + { } + +void CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + // call base class's method + CPanRemDevStateWaitingForRoleSwitchBase::OnEntryL(); + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest::DoRoleSwitchSuccessL() + { + LOG1(_L("RemDevState[%x]: - role switch succeeded"), &StateMachine()); + + // tell the remote device the connection was successful + StateMachine().SendRoleResponse(EOperationSuccessful); + + // Store the new roles + StateMachine().LocalRole() = iPendingLocalRole; + StateMachine().RemoteRole() = iPendingRemoteRole; + + // Changing role from GN/NAP to U. If this connection previously had access to the + // uplink this should be revoked. + if(StateMachine().LocalRole() == EPanRoleU) + { + StateMachine().SetUplinkAccessAllowed(EFalse); + } + + // let the role state machine know that this connection is now active + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateIncomingNotification(StateMachine()); + return(nextState); + } + +CPanRemDevStateBase* CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest::DoRoleSwitchFailureL(TInt /*aError*/) +/** +Signal the remote device that the role change didn't work, and go back to active state +*/ + { + LOG1(_L("RemDevState[%x]: - role change failed, signalling remote device and going to active state"), &StateMachine()); + // tell the remote device the connection was unsuccessful + StateMachine().SendRoleResponse(EConnectionNotAllowed); + // let the role state machine know that the role change failed + StateMachine().RoleChangeFailed(); + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + return(nextState); + } + +// +// CPanRemDevStateActive +// + +CPanRemDevStateActive::CPanRemDevStateActive(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateActive) +/** + +*/ + { } + +void CPanRemDevStateActive::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + + StateMachine().DeviceActive(); + } + +void CPanRemDevStateActive::BnepRoleRequestFromRemoteDeviceL(const TUUID& aRequestedLocalRole, const TUUID& aRequestedRemoteRole) +/** +Remote device is trying to renegotiate roles with us +@note This may also occur because our role response got lost, so send it again... +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request received from remote device..."), &StateMachine()); + + TInt err; + TBluetoothPanRole proposedLocalRole; + TBluetoothPanRole proposedRemoteRole; + + // Convert the role from the remote device. If these are invalid the connection will be terminated. + ConvertUuidsToPanRolesL(aRequestedLocalRole, aRequestedRemoteRole, proposedLocalRole, proposedRemoteRole); + + if((StateMachine().LocalRole() == proposedLocalRole) && (StateMachine().RemoteRole() == proposedRemoteRole)) // they've lost our response, so resend + { + LOG1(_L("RemDevState[%x]: - ...roles are the same as before, they obviously lost our role response - resending"), &StateMachine()); + StateMachine().SendRoleResponse(EOperationSuccessful); + } + else // it's an attempt to renegotiate roles + { + LOG1(_L("RemDevState[%x]: - ..., they want to renegotiate roles"), &StateMachine()); + + CPanRemDevStateBase* nextState = NULL; + + LOG1(_L("RemDevState[%x]: - checking role request for incoming role upgrade attempt..."), &StateMachine()); + err = StateMachine().RoleChangeRequestFromPeer(proposedLocalRole, proposedRemoteRole); + switch(err) + { + case KErrNone: + { + LOG1(_L("RemDevState[%x]: - ...successful, checking to see if M/S switch necessary..."), &StateMachine()); + + err = PerformMasterSlaveSwitchIfNecessary(proposedLocalRole); + if(err==KErrWaitingForBasebandRoleSwitch) + { + nextState = new(ELeave) CPanRemDevStateWaitingForRoleSwitchForIncomingRoleChangeRequest(StateMachine(), proposedLocalRole, proposedRemoteRole); + break; + } + else if(err) + { + LOG1(_L("RemDevState[%x]: - ...M/S switch required and failed - disconnecting device."), &StateMachine()); + // remote device would not perform role switch (maybe it needs to stay as master for another profile it's using...) + StateMachine().SendRoleResponse(EConnectionNotAllowed); + StateMachine().RoleChangeFailed(); + break; + } + + LOG1(_L("RemDevState[%x]: - ...M/S switch not required, sending successful connect response."), &StateMachine()); + StateMachine().SendRoleResponse(EOperationSuccessful); + + // Store the new roles + StateMachine().LocalRole() = proposedLocalRole; + StateMachine().RemoteRole() = proposedRemoteRole; + + // Changing role from GN/NAP to U. If this connection previously had access to the + // uplink this should be revoked. + if(StateMachine().LocalRole() == EPanRoleU) + { + StateMachine().SetUplinkAccessAllowed(EFalse); + } + + // remain in active state + StateMachine().DeviceActive(); + break; + } + case KErrLocked: + { + LOG1(_L("RemDevState[%x]: - ...role state machine busy, waiting."), &StateMachine()); + + // Create the "waiting" state + nextState = new(ELeave) CPanRemDevStateWaitingForLocalRoleStateMachineAvailabilityForIncomingRoleChangeRequest(StateMachine(), proposedLocalRole, proposedRemoteRole); + break; + } + + case KErrInvalidOrUnacceptableRoleCombination: + LOG1(_L("RemDevState[%x]: - ...Role change request can't be processed."), &StateMachine()); + StateMachine().SendRoleResponse(EConnectionNotAllowed); + // We don't want to change state or inform the state machine as it returned the error. + break; + + default: + // if err is something other than KErrNoMemory that might indicate an internal error in the state + // machine, so ASSERT_DEBUG. We can probably carry on even if the error is unexpected, as leaving + // will cause this connection to shutdown. + __ASSERT_DEBUG(err == KErrNoMemory, PanicInState(EPanAgentRoleStateMachineReturnedUnexpectedErrorCode)); + User::Leave(err); + break; + }; + + if(nextState) // we've decided we need to go to another state to continue + { + StateMachine().SetState(*nextState); + delete this; + return; + } + } + } + +void CPanRemDevStateActive::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage aRoleResponseCode) +/** +Something's gone very wrong - what is the other device doing? +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response received from remote device, what are they up to? We haven't got an outstanding role request (this might change after a refactoring of the remote device states though)"), &StateMachine()); + + // for robustness sake, accept a successful role response, but disconnect them otherwise + if(aRoleResponseCode!=EOperationSuccessful) + { + User::Leave(KErrDodgyResponseFromRemoteDevice); + } + } + +void CPanRemDevStateActive::RemoteDeviceDisconnectL(TInt aError) +/** +Remote device has disconnected +@param aError The reason for the remote device disconnect +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received, they've obviously given up talking to us."), &StateMachine()); + + StateMachine().ResetConnectionRetryAttempts(); // reset the connection retry attempts + + User::Leave(aError); // will shutdown the connection + } + +void CPanRemDevStateActive::ReadyForRoleRequestL() +/** +Indication that the main state machine wants us to renegotiate our roles +@note According to the spec, if we fail to negotiate a new role we remain connected and in our old roles +*/ + { + LOG1(_L("RemDevState[%x]: - ready for role request received from role state machine - it obviously wants us to renegotiate our roles."), &StateMachine()); + + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStatePerformingRoleNegotiationForOutgoingRoleChange(StateMachine()); + StateMachine().SetState(*nextState); + delete this; + return; + } + +void CPanRemDevStateActive::ShutdownL() +/** +Disconnect command from local device +*/ + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received."), &StateMachine()); + + User::Leave(KErrLocallyInitiatedDisconnect); // will shutdown the connection + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateActive::GetState() const + { + return EActive; + } + +// +// CPanRemDevStateShutdown +// + +CPanRemDevStateShutdown::CPanRemDevStateShutdown(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateShutdown) +/** + +*/ + { } + +void CPanRemDevStateShutdown::OnEntryL() +/** + +*/ + { + LOG1(_L("RemDevState[%x]: - entering state"), &StateMachine()); + // put an async break in here to ensure that if we arrive here synchronously from + // the initialising state in the role state machine, that we give the role state + // machine chance to transition to a new state before telling it we're shutting down + TRequestStatus* status = &StateMachine().Status(); + User::RequestComplete(status, KErrNone); + StateMachine().SetActive(); + } + +void CPanRemDevStateShutdown::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +Shouldn't receive this (the remote device should be disconnected) but just in case... +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role request received from remote device, but we're shutting down so just ignore it."), &StateMachine()); + + // better tell them that we're not going to do anything + StateMachine().SendRoleResponse(EConnectionNotAllowed); + } + +void CPanRemDevStateShutdown::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +Shouldn't receive this (the remote device should be disconnected) but just in case... +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role response received from remote device, but we're shutting down so just ignore it."), &StateMachine()); + + // We're disconnecting, so we don't care, just bin it (ie. do nothing...) + } + +void CPanRemDevStateShutdown::RemoteDeviceDisconnectL(TInt /*aError*/) +/** +Shouldn't receive this (the remote device should be disconnected) but just in case... +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received; odd, they should already be disconnected."), &StateMachine()); + + // Again, we're disconnecting, so we don't care, just bin it (ie. do nothing...) + } + +void CPanRemDevStateShutdown::ShutdownL() +/** +Whoops - double disconnect from local device... +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + // It is possible to request a shutdown of a remote device when it is already in + // the shutdown state, so don't panic. This occurs when the last remote device receives a + // disconnect and places itself in the shutdown state but only removes itself from the agent + // after an async event. + // As it was the last remote device this cause LinkLayerDown() to be called on Nifman which + // shuts down the agent. The agent then disconnects all remote devices. If the async event + // has not occured for the last remote device shutdown then the agent calls shutdown on the + // remote device again. This is ok as the async event completing will inform the agent and then + // the agent will do a full shutdown. + + LOG1(_L("RemDevState[%x]: - remote device disconnect request received, already shutting down so nothing to do."), &StateMachine()); + } + +void CPanRemDevStateShutdown::AsyncEventCompleteL() +/** +Async callback to tell us to shutdown the state machine +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + LOG1(_L("RemDevState[%x]: - async event complete, shutting down"), &StateMachine()); + + if(StateMachine().HasBnepChannel()) + { + StateMachine().DisconnectBnepChannel(); + } + + StateMachine().ShutdownComplete(); + // don't "delete this" - this is the terminal state, so the state machine has to delete us + } + +void CPanRemDevStateShutdown::AsyncEventCancelL() +/** + +@note *Must not* leave from here, otherwise we're in an endless loop +*/ + { + TRequestStatus* status = &StateMachine().Status(); + User::RequestComplete(status, KErrCancel); // to keep CActive::Cancel() happy + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateShutdown::GetState() const + { + return EDisconnecting; + } + +// +// CPanRemDevStateIncomingNotification +// + +CPanRemDevStateIncomingNotification::CPanRemDevStateIncomingNotification(MPanRemDevStateMachineNotify& aStateMachine) : + CPanRemDevStateBase(aStateMachine, EPanRemDevStateIncomingNotification) +/** +This state manages the incoming uplink access allowed for local NAP role notifier +*/ + { } + +CPanRemDevStateIncomingNotification::~CPanRemDevStateIncomingNotification() + { + delete iNapUplinkAuthorisationHelper; + } + +void CPanRemDevStateIncomingNotification::OnEntryL() + { + if(StateMachine().LocalRole() == EPanRoleNap) + { + iNapUplinkAuthorisationHelper = new(ELeave) CNapUplinkAuthorisationHelper(*this); + + // Set requested connection address. + TBTDevAddr requestingDeviceAddr = StateMachine().RemSockAddr().BTAddr(); + iNapUplinkAuthorisationHelper->PanConnectionList().SetRequestedConnectionAddr(requestingDeviceAddr); + + // Get current connection information. + StateMachine().GetExistingConnections(iNapUplinkAuthorisationHelper->PanConnectionList()); + + iNapUplinkAuthorisationHelper->PerformNapUplinkAuthorisationL(); + } + else + { + // Notifier not required - go to active state. + CPanRemDevStateBase* nextState = new(ELeave) CPanRemDevStateActive(StateMachine()); + StateMachine().SetState(*nextState); + delete this; + return; // Just to be safe after 'delete this' + } + } + +// From MNapUplinkAuthorisationRequester +void CPanRemDevStateIncomingNotification::MnuarNapUplinkAuthorisationHelperComplete(TInt aError, TNapConnectionResult aResult) + { + if(aError == KErrNone) + { + switch(aResult) + { + case EDisallowNewNapConnection: + // Disconnect this connection by moving into the shutdown state. + ShutdownConnection(KErrAccessDenied); + break; + + case EAcceptNapConnectionAllowUplinkAccess: + // Inform the packet driver that this connection should have access to the NAP service and + // close any connection that currently has access to the uplink. + StateMachine().SetUplinkAccessAllowed(ETrue); + + // Fall onto next case... + + case EAcceptNapConnectionDisallowUplinkAccess: + { + CPanRemDevStateBase* nextState = NULL; + TRAPD(err, nextState = new(ELeave) CPanRemDevStateActive(StateMachine())); + if(err == KErrNone) + { + StateMachine().SetState(*nextState); + delete this; + return; + } + else + { + ShutdownConnection(err); + } + } + break; + + default: + PanicInState(EInvalidResultCodeFromNapUplinkAuthorisationNotifier); + break; + }; + } + else + { + // Disconnect this connection by moving into the shutdown state. + ShutdownConnection(aError); + } + } + +void CPanRemDevStateIncomingNotification::BnepRoleRequestFromRemoteDeviceL(const TUUID& /*aRequestedLocalRole*/, const TUUID& /*aRequestedRemoteRole*/) +/** +A role request from the remote device can't be handled at this time +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role reponse from remote device."), &StateMachine()); + + // drop the role request packet that the remote device has sent us + } + +void CPanRemDevStateIncomingNotification::BnepRoleResponseFromRemoteDeviceL(TBnepSetupConnectionResponseMessage /*aRoleResponseCode*/) +/** +A role request has not been sent by this device. This is unexpected behaviour by the remote. +*/ + { + LOG1(_L("RemDevState[%x]: - BNEP role reponse from remote device."), &StateMachine()); + + // drop the role response packet that the remote device has incorrectly sent us + } + +void CPanRemDevStateIncomingNotification::RemoteDeviceDisconnectL(TInt aError) + { + LOG1(_L("RemDevState[%x]: - remote device disconnect received; odd, they should already be disconnected."), &StateMachine()); + + // Shutdown the connection. + ShutdownConnection(aError); + } + +void CPanRemDevStateIncomingNotification::ShutdownL() + { + LOG1(_L("RemDevState[%x]: - locally requested disconnect received"), &StateMachine()); + + // outstanding async request will be cancelled by state machine when we leave + User::Leave(KErrLocallyInitiatedDisconnect); + } + +// This returns a distilled (i.e., not the actual remote device state) +// version of the current connection status of the remote device +TRemoteDeviceState CPanRemDevStateIncomingNotification::GetState() const + { + return EPerformingNegotiation; + } + +void CPanRemDevStateIncomingNotification::ShutdownConnection(TInt aError) + { + if(!iShutdownCalled) + { + // Shutdown the connection. + iShutdownCalled = ETrue; + StateMachine().Shutdown(aError); + } + } + + +#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY +CPanRemDevStatePaused::CPanRemDevStatePaused(MPanRemDevStateMachineNotify& aStateMachine, CPanRemDevStateBase& aNextState) + : CPanRemDevStateBase(aStateMachine, EPanRemDevStatePaused), iNextState(&aNextState) + { + } + +CPanRemDevStatePaused::~CPanRemDevStatePaused() + { + delete iNextState; + } + +void CPanRemDevStatePaused::OnEntryL() + { + // Does nothing + } + +void CPanRemDevStatePaused::ShutdownL() + { + // We don't know the appropriate action so allow the next states + // ShutdownL to execute. We'll cleanup the next state when the + // RemDevStateMachine deletes us + iNextState->ShutdownL(); + } + +TRemoteDeviceState CPanRemDevStatePaused::GetState() const + { + return iNextState->GetState(); + } + +void CPanRemDevStatePaused::AsyncEventCancelL() + { + // Next state hasn't started yet so no need to cancel it + } + +void CPanRemDevStatePaused::AsyncEventCompleteL() + { + StateMachine().SetState(*iNextState); + + // We no longer own the next state + iNextState = NULL; + delete this; + } + +#endif +// SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY