diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/linkmgr/physicallinks.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/btstack/linkmgr/physicallinks.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,4291 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// Implementation of physical links +// +// + +#include +#include "physicallinksmanager.h" +#include "physicallinks.h" +#include "AclDataQController.h" +#include "ACLSAP.h" +#include "SCOSAP.h" +#include "ProxySAP.h" +#include "linkconsts.h" +#include "hcifacade.h" +#include "hostresolver.h" +#include "PhysicalLinkHelper.h" +#include "pairingscache.h" +#include "oobdata.h" +#include "pairingserver.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, LOG_COMPONENT_LINKMGR); +#endif + +#ifdef _DEBUG +PANICCATEGORY("plink"); +#endif + +static const THCIErrorCode KDefaultRejectReason = EHostSecurityRejection; // see spec Error Codes + +#ifdef _DEBUG +#define __CHECK_CONNECTION_HANDLE(aHandle) __ASSERT_DEBUG(aHandle==iHandle, Panic(EBTLinkMgrConnectionEventInWrongSAP)); +#else +#define __CHECK_CONNECTION_HANDLE(aHandle) aHandle=aHandle; // to suppress warnings +#endif + +CPhysicalLink* CPhysicalLink::NewLC(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice) + { + LOG_STATIC_FUNC + CPhysicalLink* s = new(ELeave) CPhysicalLink(aConnectionMan, aRegSess, aDevice); + CleanupStack::PushL(s); + s->ConstructL(); + return s; + } + +CPhysicalLink* CPhysicalLink::NewL(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice) + { + LOG_STATIC_FUNC + CPhysicalLink* s = NewLC(aConnectionMan, aRegSess, aDevice); + CleanupStack::Pop(s); + return s; + } + +CPhysicalLink::CPhysicalLink(CPhysicalLinksManager& aConnectionMan, CRegistrySession& aRegSess, const TBTNamelessDevice& aDevice) + : iLinksMan(aConnectionMan) + , iRegSess(aRegSess) + , iDevice(aDevice) + , iHandle(KHCIBroadcastHandle) + , iRemoteFeatures(KInvalidRemoteFeatures) + , iDeviceResult(KDeviceNotObtained) + , iRegistryHelpers(_FOFF(CBTRegistryHelperBase,iLink)) + , iProxySAPs(_FOFF(CBTProxySAP, iQueueLink)) + , iOverrideParkRequests(EFalse) + , iOverrideLPMRequests(EFalse) + , iLPMOverrideTimerQueued(EFalse) + , iConnectionPacketTypeChanged(EFalse) + , iLowPowModeCtrl(*this, iLinksMan.HCIFacade().CommandQController()) + , iDisconnectCtrl(*this, iLinksMan.HCIFacade().CommandQController()) + , iAuthenticationCtrl(*this, iLinksMan.HCIFacade().CommandQController()) + , iLSTO(0) + , iAutoKeyRefreshQue(_FOFF(XAutoKeyRefreshToken, iQueLink)) + , iRemoteDeviceRecordedByStack(ENull) + { + LOG_FUNC + // don't initialise physical link policy just yet - wait until connection completes + // by then we'll have best information to go on + LOG1(_L("New CPhysicalLink [0x%08x]"), this); + } + +CPhysicalLink::~CPhysicalLink() + { + LOG_FUNC + __ASSERT_DEBUG(iACLLogicalLinks.Count()==0, Panic(EBTACLLogicalLinkBadDebind)); + __ASSERT_DEBUG(iSyncLogicalLink == NULL, Panic(EBTSCOLogicalLinkBadDebind)); + + LOG1(_L("CPhysicalLink Destructing this = 0x%08x"), this); + + // tell ConnectionsManager... + iLinksMan.RemovePhysicalLink(*this); + iACLLogicalLinks.Close(); + + RemoveIdleTimer(); + if (iLPMOverrideTimerQueued) + { + BTSocketTimer::Remove(iOverrideLPMTimerEntry); + iLPMOverrideTimerQueued = EFalse; + } + + LOG(_L("sec\tClosing subscribers...")) + + LOG(_L("sec\tClosing helpers...")) + TSglQueIter iter(iRegistryHelpers); + CBTRegistryHelperBase* helper; + // Detach from any Registry Helpers for which this object + // is the parent. + while(iter) + { + helper = iter++; + helper->DetachParent(); + } + + iLowPowModeCtrl.Abort(); + iDisconnectCtrl.Abort(); + iAuthenticationCtrl.Abort(); + + delete iPhysicalLinkMetrics; + delete iPinRequester; + delete iNumericComparator; + delete iPasskeyEntry; + delete iArbitrationDelay; + delete iRoleSwitchCompleteCallBack; + delete iEncryptionEnforcer; + + DeleteRoleSwitcher(); + } + +void CPhysicalLink::ConstructL() + { + LOG_FUNC + LOG1(_L("CPhysicalLink::ConstructL() this = 0x%08x"), this); + GetDeviceFromRegistryL(); + iArbitrationDelay = CArbitrationDelayTimer::NewL(this); + + TCallBack cb1(RoleSwitchCompleteCallBack, this); + iRoleSwitchCompleteCallBack = new (ELeave)CAsyncCallBack(cb1, EActiveHighPriority); + + TCallBack cb2(OverrideLPMTimeoutCallback, this); + iOverrideLPMTimerEntry.Set(cb2); + + iPhysicalLinkMetrics = CPhysicalLinkMetrics::NewL(*this, iLinksMan.HCIFacade().CommandQController()); + } + +TBool CPhysicalLink::HasHandle(THCIConnHandle aHandle) const + { + LOG_FUNC + if (iHandle == aHandle) + return ETrue; // Only one ACL handle is available. + if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aHandle) + return ETrue; + return EFalse; + } + +TBool CPhysicalLink::HasSyncLink() const + { + LOG_FUNC + return (iSyncLogicalLink != NULL); + } + +void CPhysicalLink::SubscribeLinkObserver(MPhysicalLinkObserver& aObserver) +/** +The subscribed object will be notified whenever a link state change occurs. +**/ + { + LOG_FUNC +#ifdef _DEBUG + //ensure the subscriber isn't already subscribed... + TInt found = 0; + TDblQueIter iter(iBasebandSubscribers); + while (iter) + { + if (iter++ == &aObserver) + { + found++; + } + } + + __ASSERT_DEBUG(found==0, Panic(ELinkMgrBadBasebandArray)); +#endif //_DEBUG + + iBasebandSubscribers.AddFirst(aObserver.ObserverQLink()); + } + +void CPhysicalLink::UnsubscribeLinkObserver(MPhysicalLinkObserver& aObserver) +/** +aSubscriber will no longer be notified of state changes. +**/ + { + LOG_FUNC + aObserver.ObserverQLink().Deque(); + } + +TInt CPhysicalLink::TryToAndThenPreventHostEncryptionKeyRefresh(TAny* aOutToken) + { + LOG_FUNC + TInt err = KErrNone; + // The handling of the TAny* parameter seems a bit wacky - but it makes sense as follows + // this call handles a call from the bluetooth control plane (which passes + // only a TAny* as a parameter). We need to return a value back through as well, so we need + // a pointer to a pointer (so after using the input it can be modified to point to the + // output). We need a Bluetooth device address so a pointer to a pointer to a TBTDevAddr + // is passed down. Then the pointer to a pointer is used to update the pointer to a control + // plane token (which represents a handle preventing host encryption key refreshes). + if (!IsEncryptionPauseResumeSupported()) + { + err = KErrNotSupported; + *reinterpret_cast(aOutToken) = NULL; + } + else + { + if (iAutoKeyRefreshQue.IsEmpty()) + { + TRAP_IGNORE(iLinksMan.HCIFacade().RefreshEncryptionKeyL(iHandle)); + // If we can't refresh the encryption key, there's not much we can do + } + XAutoKeyRefreshToken* token = new XAutoKeyRefreshToken(); + if (token) + { + iAutoKeyRefreshQue.AddLast(*token); + } + else + { + err = KErrNoMemory; + } + *reinterpret_cast(aOutToken) = token; + } + return err; + } + +void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TInt /*aResult*/) +/** + A task has completed where we don't expect a response - just cleanup helper +**/ + { + LOG_FUNC + // remove from array + iRegistryHelpers.Remove(*aHelper); + // delete + delete aHelper; + } + +/** +The completion function of a registry helper returning a remote device entry. +*/ +void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, + const TBTNamelessDevice& aDevice, + TInt aResult) +/** + A registry task has completed where we expect a device to be returned +**/ + { + LOG_FUNC + LOG1(_L("CPhysicalLink: Registry task complete; result %d"), aResult); + + // The TBTNamelessDevice assignment operator only performs an update on valid + // entries, as we need a bitwise copy (since we want to mirror what is in the registry) + // we need to go to these extras steps. + TPckg cachedVersion(iRegistryDevice); + cachedVersion.Copy(TPckg(aDevice)); + + // We have to be careful about information flow from the registry to the actual representation (iDevice). + // Although the values in the returned entry are useful if the stack has not yet got the information, + // if the stack has already gathered values then they will be more up-to-date. Therefore we need to + // assign the current values first if they are valid to build up the latest representation. + TBTNamelessDevice device = aDevice; // Take a local copy to work on. + if(iDevice.IsValidDeviceClass()) + { + device.SetDeviceClass(iDevice.DeviceClass()); + } + if(iDevice.IsValidPageScanRepMode()) + { + device.SetPageScanRepMode(iDevice.PageScanRepMode()); + } + if(iDevice.IsValidPageScanPeriodMode()) + { + device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode()); + } + if(iDevice.IsValidPageScanMode()) + { + device.SetPageScanMode(iDevice.PageScanMode()); + } + if(iDevice.IsValidClockOffset()) + { + device.SetClockOffset(iDevice.ClockOffset()); + } + if(iDevice.IsValidUsed()) + { + device.SetUsed(iDevice.Used()); + } + if(iDevice.IsValidSeen()) + { + device.SetSeen(iDevice.Seen()); + } + + // With the link key (and friends) we need to be even more careful, as there is effectively + // two-way flow of information: + // 1) Link key storage from the stack. + // 2) Unbonding from the clients of the registry. + // Further to this, if the registry entry does not have a link key, it can mean one of several + // things: + // a) No link key has been generated with the remote device yet. + // b) A client of the registry has unbonded the remote device. + // c) The current link key is not to form a bond. + // Taking this into account we make a suitable choice about what to do. + if(!iDevice.IsValidLinkKey()) + { + // If there isn't any link key in the system yet - and so it is safe to update the representation + // to use the value from the registry. + // - see the global update below. + } + else + { + // We currently have a link key.... + if(device.IsValidLinkKey() || iRegistryDevBeingMod.InUse()) + { + // Even if the linkkey in registry is valid, it must be older or same as the current + // link key (as the stack is the only one who should be adding keys). Also, if we are in the + // middle of updating the registry (for any purpose) then we can ignore the registry value as it + // may not be up-to date. So, for both the cases just update with the latest. + // There is an interesting case if client modifies the registry by unpairing/or deleting the device + // and stack also tries to updates the link key in registry at the same time. We will give priority + // to stack's updations by keeping the latest copy as it is. + device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType()); + if(iDevice.IsValidPassKey()) + { + device.SetPassKey(iDevice.PassKey()); + } + } + else + { + // This is the interesting case where a client has unbonded, but we already have a link + // key. What we do here is interesting. + if(IsAuthenticationPending() || LinkState().Authenticated() || SimplePairingMode() == EPhySimplePairingEnabled) + + { + // If we're already authenticated / authenticating then we're already using our link + // key. As such we need to continue using it, but we don't want to update + // Also if the link is SSP enabled then we can re-authenticate - this removes any chance of a + // race condition with the registry. + iPreventLinkKeyUpdateReg = ETrue; // prevent locally cached link key being given back to the registry + } + else + { + // The link key hasn't been used yet - so we can update the representation to not + // have a link key (as requested by the user). + // Currently there is no chance of a race with the registry as combination link keys are + // always form a bond. + iDevice.DeleteLinkKey(); // delete locally cached link key + } + } + } + + // Finally update the representation - we should have manipulated it correctly. + iDevice = device; + + // Store the result of the retrieval for usage later. + iDeviceResult = aResult; + + // the HW asked earlier for a link key - we can respond now + __ASSERT_DEBUG(iDevice.IsValidAddress(), Panic(EBTPhysicalLinksInvalidAddress)); + if(iWaitingForLinkKeyFromRegistry) + { + if (aResult == KErrNone && iDevice.IsValidLinkKey()) + { + if ( iDevice.LinkKeyType() != ELinkKeyCombination) + { + if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug) + { + LOG(_L("CPhysicalLink: Debug mode - Link to debug link key")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address()); + } + else + if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable()) + { + LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address()); + } + else + { + LOG(_L("CPhysicalLink: Issuing link key to HC now")) + iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey()); + } + } + else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled) + { + LOG(_L("CPhysicalLink: Issuing link key to HC now")) + iAuthenticationCtrl.LinkKeyRequestReply(iDevice.Address(), iDevice.LinkKey()); + } + else + { + LOG(_L("CPhysicalLink: Current PIN code too short!")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address()); + } + } + else + { + iAuthenticationCtrl.LinkKeyRequestNegativeReply(iDevice.Address()); + } + } + iRequireAuthenticatedLinkKey = EFalse; + iWaitingForLinkKeyFromRegistry = EFalse; + + RegistryTaskComplete(aHelper, aResult); // cleans up our helper + } + +void CPhysicalLink::RegistryTaskComplete(CBTRegistryHelperBase* aHelper, TRegistryUpdateStatus aRegUpdateStatus, TInt aResult) + { +/** + This method allows the RegistryHelpers to indicate the registry action taking place + +**/ + LOG_FUNC + if(iRemoteDeviceRecordedByStack == EPending && aRegUpdateStatus != ENoChange) + { + if (aResult == KErrNone) + { + iRemoteDeviceRecordedByStack = EComplete; + } + else + { + iRemoteDeviceRecordedByStack = ENull; + } + } + + iRegistryDevBeingMod.RequestCompleted(); + RegistryTaskComplete(aHelper, aResult); // cleans up our helper + } + +void CPhysicalLink::CompletedPackets(THCIConnHandle aConnH, TUint16 aNumPackets) +/** + In this domain we are happy that a valid event has come in telling us that + the buffers in the local controller have been freed + + Since this reflects both local hardware features (buffers) and flow control + we treat them separately + +**/ + { + LOG_FUNC + // firstly report that local hardware buffers free (via inlines!) + iLinksMan.LinkManagerProtocol().LinkMuxer().DataQController().CompletedPackets(aConnH, aNumPackets); + + // we forward this to the thing that sends the packets so that they can do Flow Control + + // could speed up - already done this handle test to get into *this* object + if (iSyncLogicalLink && iSyncLogicalLink->Handle() == aConnH) + { + iSyncLogicalLink->PacketsSent(aConnH, aNumPackets); + } + else + { + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + iACLLogicalLinks[i]->PacketsSent(aConnH, aNumPackets); + } + } + } + +void CPhysicalLink::TryToSend() + { + LOG_FUNC + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + iACLLogicalLinks[i]->TryToSend(); + } + } + +void CPhysicalLink::NewLinkKey(const TBTDevAddr& /*aAddr*/, const TBTLinkKey& aLinkKey, THCILinkKeyType aLinkKeyType) + { + LOG_FUNC + if(iLinkKeyPending) + //user has entered a PIN and a new link key has come in. + { + iLinkKeyPending = EFalse; + iPreventLinkKeyUpdateReg = EFalse; //override previous unpair so that link key will be stored in the registry. + } + + TBTLinkKeyType linkKeyType = ELinkKeyCombination; + + switch (aLinkKeyType) + { + case ECombinationKey: + linkKeyType = ELinkKeyCombination; + break; + case EDebugCombinationKey: + linkKeyType = ELinkKeyDebug; + break; + case EAuthenticatedCombinationKey: + linkKeyType = ELinkKeyAuthenticated; + break; + case EUnauthenticatedCombinationKey: + if (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask) || + (iIOCapability == EIOCapsDisplayOnly) || (iIOCapability == EIOCapsNoInputNoOutput)) + { + linkKeyType = ELinkKeyUnauthenticatedNonUpgradable; + } + else + { + linkKeyType = ELinkKeyUnauthenticatedUpgradable; + } + break; + case EChangedCombinationKey: + linkKeyType = iDevice.LinkKeyType(); // The link key type hasn't changed + break; + + case ELocalUnitKey: + case ERemoteUnitKey: + default: + LOG1(_L("CPhysicalLink: Unexpected link key type (%d)"), aLinkKeyType); + __ASSERT_DEBUG(EFalse, Panic(EBTUnexpectedLinkKeyType)); + break; + } + + if (linkKeyType == ELinkKeyCombination && iNewPinCodeValid) + { + // Only bind a PIN code to the device if we receive a link key based on it. + iNewPinCodeValid = EFalse; + iDevice.SetPassKey(iNewPinCode); + } + + + SetLinkKey(aLinkKey, linkKeyType); // keeps a copy in our 'cache', updates paired list in PHYs mananger + + TRAP_IGNORE(StoreDeviceL(EFalse)); //EFalse: new meaning - do not prevent addition + // if that errored we just have to keep it here - but it won't be in registry + // so it'll be a transient pairing + } + +void CPhysicalLink::UpdateFromInquiryCache() + { + LOG_FUNC + CBTInqResultRecord* juice = NULL; //juice is data from remote which can help/speed up a connection + // see if we can speed up the connection - the inquiry cache may have some juice + juice = iLinksMan.LinkManagerProtocol().InquiryMgr().BasebandParametersFromCache(iDevice.Address()); + if(!juice) + //no juice in inquiry manager + { + return; + } + + TInquiryLogEntry& jle = juice->LogEntry(); + // Inquiry cache may have useful info we can use: put them in our device + // so that we can use this, and update this into the Registry later. + // This will allow Apps to get this to initiate faster connections. + // The update to the Registry doesn't happen here, but at other interesting times + ASSERT_DEBUG(jle.iBdaddr == iDevice.Address()); + //Only update with juice values from inquiry manager if they are have come from HCI + //(i.e. from remote) - records in the inquiry manager are filled with default + //values and then fields are updated as relevant HCI events occur. + // - iDevice may already contain values found in the registry which are + //not default and therefore should have come themselves from the HCI + if(juice->IsPageScanModeFromHCI()) + { + iDevice.SetPageScanMode(jle.iPageScanMode); + } + if(juice->IsPageScanRepModeFromHCI()) + { + iDevice.SetPageScanRepMode(jle.iPageScanRepetitionMode); + } + if(juice->IsClockOffsetFromHCI()) + { + iDevice.SetClockOffset(jle.iClockOffset); + } + } + +void CPhysicalLink::StoreDeviceL( TBool aPreventDeviceAddition ) + { + LOG_FUNC + + UpdateFromInquiryCache(); //check juice has not just come in - update iDevice if so + + //only write back the necessary attributes + TBTNamelessDevice device; + if(iDevice.IsValidAddress()) + { + device.SetAddress(iDevice.Address()); + } + if(iDevice.IsValidDeviceClass()) + { + device.SetDeviceClass(iDevice.DeviceClass()); + } + if(iDevice.IsValidLinkKey() && !iPreventLinkKeyUpdateReg) + { + if(!(iIOCapsReceived && (iAuthenticationRequirement == EMitmNotReqNoBonding || iAuthenticationRequirement == EMitmReqNoBonding))) + { + LOG(_L("!!! Storing Link Key in Registry")); + device.SetLinkKey(iDevice.LinkKey(), iDevice.LinkKeyType()); + } + } + if(iDevice.IsValidPageScanRepMode()) + { + device.SetPageScanRepMode(iDevice.PageScanRepMode()); + } + if(iDevice.IsValidPageScanPeriodMode()) + { + device.SetPageScanPeriodMode(iDevice.PageScanPeriodMode()); + } + if(iDevice.IsValidPageScanMode()) + { + device.SetPageScanMode(iDevice.PageScanMode()); + } + if(iDevice.IsValidClockOffset()) + { + device.SetClockOffset(iDevice.ClockOffset()); + } + if(iDevice.IsValidUsed()) + { + device.SetUsed(iDevice.Used()); + } + if(iDevice.IsValidSeen()) + { + device.SetSeen(iDevice.Seen()); + } + if(iDevice.IsValidPassKey()) + { + device.SetPassKey(iDevice.PassKey()); + } + + // if already exists an outstanding request with the same device info + // is useless to store it again. Just return doing nothing. + if (iRegistryDevBeingMod.InUse() && iRegistryDevBeingMod.IsEqual(device)) + { + return; // do nothing + } + + CBTDeviceModifier* modifier = CBTDeviceModifier::NewL(iRegSess, *this, iLinksMan.LinkManagerProtocol().InquiryMgr()); + iRegistryHelpers.AddLast(*modifier); + TBool allowAdd = EFalse; + + if(!aPreventDeviceAddition && iRemoteDeviceRecordedByStack == ENull) + //Only allow the device to be added to the registry if + // 1) this StoreDeviceL function has not been called + // with its "prevent device addition" parameter set to true + // 2) we believe the cached remote device details have not yet + // been recorded in the registry. + // (If they have and if they are no longer there it is + // because an app has deleted them from the registry... + // so we should not re-add them ) + { + allowAdd = ETrue; + } + modifier->Start(device, allowAdd); + iRegistryDevBeingMod.Begin(device); + + if(allowAdd) + //The physical link object has now made its attempt to add its cached + //remote device details to the registry. + { + iRemoteDeviceRecordedByStack = EPending; + } + + } + +void CPhysicalLink::SetLinkKey(const TBTLinkKey& aLinkKey, TBTLinkKeyType aLinkKeyType) + { + LOG_FUNC + iDevice.SetLinkKey(aLinkKey, aLinkKeyType); // keeps a copy in our 'cache' + } + +void CPhysicalLink::ReadRemoteSupportedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTFeatures& aBitMask) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + if (aErr == KErrNone) + { + iRemoteFeatures = aBitMask; + + if(!iConnectionPacketTypeChanged) + { + //Send change connection packet type HCI command if application hasn't, we have to do this + //because some hardware defaults to using 1-slot packets unless told otherwise by the host. + ChangeConnectionPacketType(EAnyACLPacket); + } + + //If remote host supports extended features, then send request to read these. + if (iRemoteFeatures[ESupportedExtendedFeaturesBit]) + { + TRAP_IGNORE(iLinksMan.HCIFacade().ReadRemoteExtendedFeaturesL(iHandle, KRemoteExtendedFeaturesPage1)); + } + else + { + // If the remote doesn't support extended features, then neither they support SSP + // (no way to indicate the host supported bit). So set it as disabled. + TPhysicalLinkSimplePairingMode previousSetting = SimplePairingMode(); + __ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection)); + iSimplePairingMode = EPhySimplePairingDisabled; + if(SimplePairingMode() != previousSetting) + { + iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr()); + } + } + } + else + { + iRemoteFeatures = TBTFeatures(KInvalidRemoteFeatures); + } + } + +void CPhysicalLink::ReadRemoteExtendedFeaturesComplete(THCIErrorCode aErr, THCIConnHandle aConnH, TUint64 aBitMask, TUint8 aPageNumber, TUint8 /* aMaximumPageNumber */) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + LOG1(_L("CPhysicalLink: ReadRemoteExtendedFeaturesComplete; result %d"), aErr); + + switch (aPageNumber) + { + case KRemoteExtendedFeaturesPage1: + { + TPhysicalLinkSimplePairingMode currentSetting = SimplePairingMode(); + + if (aErr == EOK && aBitMask & (1 << ESecureSimplePairingHostSupportBit) && iLinksMan.SecMan().LocalSimplePairingMode()) + { + __ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection)); + iSimplePairingMode = EPhySimplePairingEnabled; + } + else + { + __ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingDisabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection)); + iSimplePairingMode = EPhySimplePairingDisabled; + } + if(SimplePairingMode()!=currentSetting) + { + iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr()); + } + break; + } + default: + { + // Ignore + } + } + } + +void CPhysicalLink::ReadRemoteVersionInfoComplete(THCIErrorCode aErr, THCIConnHandle aConnH, const TBTDevRemoteHwVersion& aVer) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + if (aErr == KErrNone) + { + iRemoteVersion = aVer; + } + else + { + iRemoteVersion = KInvalidRemoteHwVersion; + } + } + +void CPhysicalLink::AuthenticationComplete(THCIErrorCode aErr, THCIConnHandle aConnH) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + iLinkState.SetAuthenticated(aErr == EOK); + //__ASSERT_DEBUG(iAuthStateMask == EAuthenticationRequestPending,Panic(EUnexpectedAuthenticationState)); + AuthenticationComplete(EAuthenticationRequestPending); + + if (aErr!=EOK) + { + if(aErr == ERemoteUserEndedConnection) + { + HandlePrefetch(); + } + iNewPinCodeValid = EFalse; + delete iPinRequester; + iPinRequester = NULL; + iPinHandler = NULL; + } + else + { + __ASSERT_DEBUG(!iPinHandler, Panic(EBTPhysicalLinkPinHandlerStillPresent)); + } + + iLinksMan.SecMan().AuthenticationComplete(BDAddr(), aErr); + + TBTBasebandEventNotification event(ENotifyAuthenticationComplete, aErr); + NotifyStateChange(event); + } + +void CPhysicalLink::HandlePrefetch() + { + // Here we determine whether we need to handle a pre-fetch case. If there is an + // outbound authentication attempt and we have a PIN notifier we need to hand it + // over to the pre-fetch manager. + // We also note the fact that we've done this for any access requester to know whether + // it should indicate to defer. + if(iPinRequester && iLinksMan.SecMan().IsOutboundAccessRequest(BDAddr()) && iSimplePairingMode != EPhySimplePairingEnabled) + { + TInt err = iLinksMan.PrefetchMan().HandleOverPinRequester(BDAddr(), iPinRequester); + if(err == KErrNone) + { + iPinRequester = NULL; // As far as phys links is concerned the pin requester doesn't exist anymore. + iPinHandler = NULL; // So the handler is also not needed. + } + } + } + +void CPhysicalLink::EncryptionChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBool aEncrypted) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + if(aErr == EOK) + { + // Only change state if it's an actual encryption change - not an error (because if an error + // we should presumably stay the same). + if(aEncrypted) + { + iLinkState.SetEncrypted(ETrue); + if(!iLinkState.Authenticated()) + { + // If a link is encrypted then we know that the link is also authenticated. + iLinkState.SetAuthenticated(ETrue); + } + } + else + { + iLinkState.SetEncrypted(EFalse); + } + } + + TBTBasebandEventNotification event(aEncrypted ? ENotifyEncryptionChangeOn : ENotifyEncryptionChangeOff, aErr); + NotifyStateChange(event); + + // Having the encryption enforcer present means that we have asked for encryption. + // But we should only act if this is a valid encryption change event. + if (iEncryptionEnforcer && aErr == EOK) + { + // if the roleswitch has been requested by us then CRoleSwitcher has already parked / unparked + // the ACL controller. That means that the request is submitted twice. + // This is not supposed to be a problem, but we need to be aware of it. + if (!aEncrypted) + { + iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, ETrue); + iEncryptionEnforcer->EncryptionDisabled(SimplePairingMode()== EPhySimplePairingEnabled); + } + else + { + iLinksMan.LinkManagerProtocol().ACLController().SetParked(iHandle, EFalse); + iEncryptionEnforcer->EncryptionEnabled(); + } + } + } + +TInt CPhysicalLink::ChangeEncryption(THCIEncryptModeFlag aEnable) + { + LOG_FUNC + // no policy to adjust, but test local & remote features + if (!IsEncryptionSupported()) + { + return KErrNotSupported; + } + + TRAPD(err, iLinksMan.HCIFacade().SetEncryptL(Handle(), aEnable)); + if (err == KErrNone) + { + err = ManageEncryptionEnforcement(aEnable); + } + + return err; + } + +TInt CPhysicalLink::Authenticate(TBool aRequireAuthenticatedLinkKey) + { + LOG_FUNC + // no policy to adjust, just check to see if the link is already authenticated + TInt err = KErrNone; + __ASSERT_DEBUG(iSimplePairingMode != EPhySimplePairingUndefined, Panic(EBTPhysicalLinksInvalidArgument)); + + /* If its a dedicated bonding attempt, then we always want to authenticate again in order to generate + * a stronger linkkey if possible + */ + if (!iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()) && iLinkState.Authenticated() && !iLinksMan.LinkManagerProtocol().IsSecureSimplePairingSupportedLocally()) + { + err = KErrAlreadyExists; + } + else + { + iRequireAuthenticatedLinkKey = aRequireAuthenticatedLinkKey; + TRAP(err, iLinksMan.HCIFacade().AuthenticateL(Handle())); + if(err == KErrNone) + { + SetAuthenticationPending(EAuthenticationRequestPending); + } + } + return err; + } + +TInt CPhysicalLink::ChangeLinkKey() + { + LOG_FUNC + __ASSERT_DEBUG(iLinkState.Authenticated(), Panic(EBTPhysicalLinkNotAuthenticated)); + + // Check if the peer device can support the link key regeneration procedure. + if(PeerSupportsLinkKeyRegeneration()) + { + return iLinksMan.HCIFacade().ChangeConnectionLinkKey(Handle()); + } + return KErrNone; + } + +TBool CPhysicalLink::PeerSupportsLinkKeyRegeneration() const + { + LOG_FUNC + // Any future manufacturers or manufacturer versions that do not support + // link key re-generation should be added to this method. + + // See Bluetooth assigned numbers for an explanation of this value. + return (iRemoteVersion.iManufacturerID != 0x000a); + } + +void CPhysicalLink::ACLDataReceived(THCIConnHandle aConnH, TUint8 aFlag, const TDesC8& aData) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + // there may be a race condition in that we have got ACL data but just started + // shutdown procedures... + + // find the ACL wanting this type of data... + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + iACLLogicalLinks[i]->DataReceived(aConnH, aFlag, aData); + } + // else just dump + } + +void CPhysicalLink::SCODataReceived(THCIConnHandle aConnH, const TDesC8& aData) + { + LOG_FUNC + if (iSyncLogicalLink) + iSyncLogicalLink->DataReceived(aConnH, 0, aData); // flags ignored + + } + +void CPhysicalLink::ConnectionComplete(THCIErrorCode aErr, const TBTConnect& aConn) + { + LOG_FUNC + if (aErr == KErrNone) + { + if(aConn.iLinkType == ESCOLink && !iSyncLogicalLink) + { + LOG(_L("Got a ConnectionComplete for a non-existant SCO link")) + //This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for + //a remote device to respond to a connection request. + iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType); + + if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that + { + //The baseband might actually have established a SCO link, so send a Disconnect. + //If no SCO link exists the command will fail gracefully. + TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection)); + } + + return; + } + + // update bb model + iLinksMan.Baseband().UpdateModel(aConn); + if (aConn.iLinkType == EACLLink) + { + // we need to store the physical handle we're on - in BT1.2 this is also taken to be + // the ACL Link handle too... + // do the PHY stuff first + LOG(_L("CPhysicalLink: PHY ready")) + + iHandle = aConn.iConnH; + iDevice.SetAddress(aConn.iBdaddr); + + //Set Default Local Link Policy; + //Get this in early to try and avoid conditions where remote requests + //something we haven't told the controller we allow yet. + SetModesAllowed(iLinksMan.LinkManagerProtocol().ModesSupportedLocally(), + iLinksMan.RoleSwitchAllowed()); + + // try to get details of remote device + TRAP_IGNORE(GetRemoteDetailsL(aConn)); + // ignore error - only optimisations + + // assume we are going with 1 slot packets until notified otherwise, and in activemode + iLinkState.SetLinkState(TBTBasebandLinkState::ELinkUp); + iLinkState.SetMaxSlots(1); + iLinkState.SetLinkMode(EActiveMode); + + // If the peer is in security mode 3 then authenication has completed. + if(iPeerInSecurityMode3) + { + iLinkState.SetAuthenticated(ETrue); + AuthenticationComplete(EAuthenticationRequestPending); + } + + // update bb model for max slots + iLinksMan.Baseband().UpdateModel(aConn.iConnH, 1); + + TBTBasebandEventNotification event(ENotifyPhysicalLinkUp); + NotifyStateChange(event); + + if (aConn.iEncryptMode) + { + // pretend there's been an encryption event + EncryptionChange(aErr, aConn.iConnH, aConn.iEncryptMode); + } + } + + // This is assuming that our stack only allows one synchronous link per phy link. + // SCO (not eSCO) Link getting notified here. + else if(aConn.iLinkType == ESCOLink) + { + TBTBasebandEventNotification event(ENotifySynchronousLinkUp); + NotifyStateChange(event); + } + + // tell the logical links + NotifyLogicalLinkUp(aConn); + iLinksMan.ArbitrateAllPhysicalLinks(); + + // Check to see if we got a disconnect request during the period before the link was + // fully established. + if (iDisconnectRequested) + { + __ASSERT_DEBUG(aConn.iLinkType == EACLLink, Panic(EDisconnectRequestedNotOnACLLink)); + + // We allow the link to come up fully so that the link is not in a strange state between + // pending and up. This also means that the notifications to the logical links all work + // correctly, they just see a link come up and then go down again straight away. + TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection)); + } + else + { + TTime t; + t.UniversalTime(); + iDevice.SetUsed(t); + TRAP_IGNORE(StoreDeviceL(EFalse)); + } + } + else + { + // error occurred - need to see if it's PHY(and so ACL) or SCO that failed + // tell logical links + LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType); + + if(((aConn.iLinkType == ESCOLink)||(aConn.iLinkType == EeSCOLink)) && iSyncLogicalLink && + iSyncLogicalLink->Handle() != KInvalidConnectionHandle && iSyncLogicalLink->Handle() != aConn.iConnH) + { + // This is a secondary SCO link we just rejected or something - don't mess with our own + // The KInvalidConnectionHandle test ensures that we ignore errored outgoing SCO connects. + return; + } + + iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType); + NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr)); + if (aConn.iLinkType == EACLLink) + { + // BT 1.2 says that as the ACL Link goes up and down, so does the physical link + // so if the ACL Link has gone, so has this + // for SCO we remain in place. + TBTBasebandEventNotification event(ENotifyPhysicalLinkError, CHciUtil::SymbianErrorCode(aErr)); + NotifyStateChange(event); + delete this; + } + } + // ***Watchout*** delete this above: careful about code here + } + + +void CPhysicalLink::SynchronousConnectionComplete(THCIErrorCode aErr, + const TBTConnect& aConn, + const TBTSyncConnectOpts& aSyncOpts) + { + LOG_FUNC + __ASSERT_DEBUG(((aConn.iLinkType == ESCOLink) || (aConn.iLinkType == EeSCOLink)), Panic(EBTNonSyncConnectInSyncConnectFunc)); + + if(!iSyncLogicalLink) + { + LOG(_L("Got a SynchronousConnectionComplete when no SCO link is bound")) + //This situation can occur if ESock deletes the iSyncLogicalLink whilst it is waiting for + //a remote device to respond to a connection request. + iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType); + + if(aErr==EOK) // if error, aConn.iConnH will refer to the ACL link used to initialise the SCO link, so dont disconnect that + { + //The baseband might actually have established a SCO link, so send a Disconnect. + //If no SCO link exists the command will fail gracefully. + TRAP_IGNORE(iLinksMan.HCIFacade().DisconnectL(aConn.iConnH, ERemoteUserEndedConnection)); + } + + return; + } + + if (aErr == KErrNone) + { + // update bb model + iLinksMan.Baseband().UpdateModel(aConn); + + // This is assuming that our stack only allows one synchronous link per phy link. + // eSCO (not SCO) Link getting notified here. + TBTBasebandEventNotification event(ENotifySynchronousLinkUp); + NotifyStateChange(event); + + // tell the logical links + NotifyLogicalSyncLinkUp(aConn, aSyncOpts); + iLinksMan.ArbitrateAllPhysicalLinks(); + } + else + { + // error occurred - need to see if it's PHY(and so ACL) or SCO that failed + // tell logical links + LOG2(_L("Physical link: connection complete returned an error on handle %d, type %d"), aConn.iConnH, aConn.iLinkType); + iLinksMan.Baseband().UpdateModelForConnectionError(aConn.iBdaddr, aConn.iLinkType); + + // Before passing error around, check if it's actually for this link... + // A listening link will have a NULL handle, otherwise the handle should match. + // If the eSCO connection fails, the handle is KInvalidConnectionHandle (as initialised in CBTBasebandSAP) + // This avoids the situation where we get notified of a rejection. + if (iSyncLogicalLink && + (iSyncLogicalLink->Handle() == 0 || iSyncLogicalLink->Handle() == KInvalidConnectionHandle || iSyncLogicalLink->Handle() == aConn.iConnH)) + { + NotifyLogicalLinkError(aConn.iLinkType, CHciUtil::SymbianErrorCode(aErr)); + } + } + } + + +void CPhysicalLink::GetRemoteDetailsL(const TBTConnect& aConn) + { + LOG_FUNC + GetDeviceFromRegistryL(); + iLinksMan.HCIFacade().ReadClockOffsetL(aConn.iConnH); + iLinksMan.HCIFacade().ReadRemoteSupportedFeaturesL(aConn.iConnH); + iLinksMan.HCIFacade().ReadRemoteVersionL(aConn.iConnH); + iLinksMan.LinkManagerProtocol().InquiryMgr().ReadRemoteNameL(aConn.iBdaddr); + } + +void CPhysicalLink::LinkSupervisionTimeoutChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewTimeout) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + if(aErr == EOK) + { + iLSTO = aNewTimeout; + } + } + +void CPhysicalLink::PacketTypeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TUint16 aNewPacket) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + + if(aErr==EOK) + { + iLinksMan.Baseband().UpdateModelIfRecordExists(aConnH, aNewPacket); + iLinkState.SetPacketTypes(aNewPacket); + } + + TUint32 eventType = 0; + + if(aNewPacket & EPacketsDM1) + eventType |= ENotifyPacketsDM1; + if(aNewPacket & EPacketsDH1) + eventType |= ENotifyPacketsDH1; + if(aNewPacket & EPacketsDM3) + eventType |= ENotifyPacketsDM3; + if(aNewPacket & EPacketsDH3) + eventType |= ENotifyPacketsDH3; + if(aNewPacket & EPacketsDM5) + eventType |= ENotifyPacketsDM5; + if(aNewPacket & EPacketsDH5) + eventType |= ENotifyPacketsDH5; + + if(aNewPacket & EPacketsHV1) + eventType |= ENotifyPacketsHV1; + if(aNewPacket & EPacketsHV2) + eventType |= ENotifyPacketsHV2; + if(aNewPacket & EPacketsHV3) + eventType |= ENotifyPacketsHV3; + + TBTBasebandEventNotification event(eventType, aErr); + NotifyStateChange(event); + } + +void CPhysicalLink::MaxSlotsChange(THCIConnHandle aConnH, TUint8 aSlots) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + LOG2(_L("Connection Handle 0x%04x, using %d slots"), aConnH, aSlots); + iLinksMan.Baseband().UpdateModel(aConnH, aSlots); //event only for ACL + iLinkState.SetMaxSlots(aSlots); + + TBTBasebandEventNotification event; + switch(aSlots) + { + case 1: + event.SetEventType(ENotifyMaxSlots1); + break; + case 3: + event.SetEventType(ENotifyMaxSlots3); + break; + case 5: + event.SetEventType(ENotifyMaxSlots5); + break; + }; + NotifyStateChange(event); + } + +void CPhysicalLink::ModeChange(THCIErrorCode aErr, THCIConnHandle aConnH, TBTLinkMode aMode, TBasebandTime aInterval) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + LOG3(_L("Connection Handle 0x%04x: ModeChange event %d->%d"), aConnH, iLinkState.LinkMode(), aMode); + + TBTLinkMode oldMode = iLinkState.LinkMode(); + + iLinkState.SetLinkMode(aMode); // remember the state for ourselves + + if (aErr == EOK) + { + if (aMode == EParkMode) + { + iLinksMan.Baseband().ParkLink(aConnH); //remove from model whilst parked + } + + if (aMode == EActiveMode && oldMode==EParkMode) + { + // unparking we must tell the DataQ - we could always get the dataQ to ask us before sending of course + // but that might be performance harming + iLinksMan.Baseband().UnParkLink(aConnH); //NB Max Slots = 1 default + } + + if (aMode == ESniffMode ) + { + // Store the Sniff Interval + iSniffInterval = aInterval; + } + + if (aMode == EActiveMode && iSniffInterval) + { + //Active mode enable and SniffInterval is set. + //Therefore must be leaving sniff mode. + __ASSERT_DEBUG(oldMode==ESniffMode, Panic(EBTPhysicalLinkModeChangeErrorLeavingSniffMode)); + + //When not in sniff mode, sniff interval is obviously 0! + iSniffInterval = 0; + } + } + + // Generate a baseband event, and offer it to the proxySAP's associated + // with this link. + TBTBasebandEventNotification event; + switch(aMode) + { + case EActiveMode: + event.SetEventType(ENotifyActiveMode); + break; + case ESniffMode: + event.SetEventType(ENotifySniffMode); + break; + case EHoldMode: + event.SetEventType(ENotifyHoldMode); + break; + case EParkMode: + event.SetEventType(ENotifyParkMode); + break; + case EScatterMode: + break; // shouldnt be seen yet - if so, ignore for now + }; + event.SetErrorCode(aErr); + NotifyStateChange(event); + } + +void CPhysicalLink::RoleChange(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, TBTBasebandRole aRole) + { + LOG_FUNC + LOG1(_L("CPhysicalLink: Role change - new role %S"), aRole == EMaster ? _S("Master") : _S("Slave")); + + if (aErr == EOK) + { + iLinkState.SetLinkRole(aRole); + } + + // Generate a baseband event, and offer it to the proxySAP's associated + // with this link. + TBTBasebandEventNotification event; + if(aRole == EMaster) + { + event.SetEventType(ENotifyMaster); + } + else + { + event.SetEventType(ENotifySlave); + } + event.SetErrorCode(aErr); + NotifyStateChange(event); + + // if iEncryptionEnforcer is present it means we asked for encryption, so we inform it + // about the roleswitch + if (iEncryptionEnforcer) + { + iEncryptionEnforcer->RoleSwitchEvent(); + } + } + +void CPhysicalLink::WriteLinkPolicySettingsCompleteEvent(THCIErrorCode aErr, THCIConnHandle aConnH) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + // we don't get the things back we set... + if (aErr != EOK) + { + // The command has failed. Clear the cached values and re-arbitrate. + iLinkPolicy.SetModesAllowed(0); + iLinkPolicy.SetSwitchAllowed(EFalse); + Arbitrate(); + } + } + +void CPhysicalLink::ClockOffset(THCIErrorCode aErr, THCIConnHandle aConnH, TBasebandTime aClockOffset) + { + LOG_FUNC + __CHECK_CONNECTION_HANDLE(aConnH); + if (aErr==EOK) + { + // tell inquiry manager - we found this information to help it + iLinksMan.LinkManagerProtocol().InquiryMgr().ClockOffsetResult(BDAddr(), aClockOffset); + } + // we don't do anything more useful knowing this information + } + +void CPhysicalLink::RemoteName(THCIErrorCode aErr, const TBTDevAddr& /*aAddr*/, const TBTDeviceName8& aName) +/** + We don't keep this, but we're interested in persisting it in el Reg + By receiving this we now our attempts have completed in getting name +*/ + { + LOG_FUNC + if (aErr==EOK) + { + // slam it into the Registry - this will be useful to UIs if not us + TRAP_IGNORE(DoUpdateNameL(aName)); + } + } + +void CPhysicalLink::DoUpdateNameL(const TBTDeviceName8& aName) + { + LOG_FUNC + CBTDeviceNameChanger* nameChanger = CBTDeviceNameChanger::NewL(iRegSess, *this); + iRegistryHelpers.AddLast(*nameChanger); + nameChanger->Start(BDAddr(), aName); + } + +void CPhysicalLink::Disconnection(THCIErrorCode aErr, THCIConnHandle aConnH, THCIErrorCode aResult) + { + LOG_FUNC + // tell our socket if it's our handle - otherwise the SCO SAP + + LOG1(_L("Physical link: disconnection on handle %d"), aConnH); + LOG1(_L(" ACL link handle: %d"), Handle()); +#ifdef __FLOG_ACTIVE + if (iSyncLogicalLink) LOG1(_L(" Sync link handle: %d"), iSyncLogicalLink->Handle()); +#endif + + //no matter if it is the socket or SCO SAP the ui dialogs will be cancelled + if (iNumericComparator && iNumericComparator->IsActive()) + { + iNumericComparator->Cancel(); + } + if (iPasskeyEntry && iPasskeyEntry->IsActive()) + { + iPasskeyEntry->Cancel(); + } + + if (aConnH == Handle()) + { + TBTBasebandEventNotification event; + switch(aErr) + { + case EHardwareFail: + { + + if(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp || + iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending) + { + //Only propagate if link is up or pending, otherwise + //we may get disconnection in unexpected states + + //Fall through + } + else + { + // this object must go when no link + delete this; + break; + } + } + case EOK: + { + iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown); + + // PHY went down - so have logical links then + NotifyLogicalLinkDown(EACLLink); + NotifyLogicalLinkDown(ESCOLink); + + event.SetEventType(ENotifyPhysicalLinkDown); + event.SetErrorCode(aResult); + iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink); + HandlePrefetch(); + NotifyStateChange(event); + // this object must go when no link + delete this; + break; + } + case ECommandDisallowed: + { + // The connection is still up, so do nothing, and wait for the next idle timer to fire + break; + } + default: + { + iLinkState.SetLinkState(TBTBasebandLinkState::ELinkDown); + + // there's an error + NotifyLogicalLinkError(EACLLink, aErr); + NotifyLogicalLinkError(ESCOLink, aErr); + event.SetEventType(ENotifyPhysicalLinkError); + event.SetErrorCode(aErr); + // baseband model needs to err on the side of least bandwidth usage - + // a link COULD have come down + iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, EACLLink); + HandlePrefetch(); + NotifyStateChange(event); + // this object must go when no link + delete this; + break; + } + } + } + + else if (iSyncLogicalLink && aConnH == iSyncLogicalLink->Handle()) + { + TBTBasebandEventNotification event; + // just the SCO link is down so we'd better notify + if (aErr) + { + LOG(_L("Physical link: error disconnection on sync link")); + TLinkType type = iSyncLogicalLink->LinkType(); + iSyncLogicalLink->Error(CHciUtil::SymbianErrorCode(aErr)); + event.SetEventType(ENotifySynchronousLinkError); + event.SetErrorCode(aErr); + // baseband model needs to err on the side of least bandwidth usage - + // a sync link COULD have come down + iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type); + } + else + { + LOG(_L("Physical link: normal disconnection on sync link")); + TLinkType type = iSyncLogicalLink->LinkType(); + iSyncLogicalLink->Disconnection(); + event.SetEventType(ENotifySynchronousLinkDown); + event.SetErrorCode(aResult); + iLinksMan.Baseband().UpdateModelForDisconnection(aConnH, type); + } + + NotifyStateChange(event); + } + // NOTE!! delete this above - careful about adding code + } + +void CPhysicalLink::ConnectionRequest(const TBTConnect& aConn) + { + LOG_FUNC + // we must be page scan enable for this to come in + // but still need to see if it got routed a listening SAP + TBool accept = EFalse; + TLogicalLinkListener* listener = NULL; + + __ASSERT_DEBUG(iPendingConnection == EFalse, Panic(EPendingConnectionNotCleared)); + + switch (aConn.iLinkType) + { + case ESCOLink: + case EeSCOLink: + if (!iSyncLogicalLink) + { + // If we already have a sync link active, reject request. + LOG(_L("No current link, checking for listener")); + listener = iLinksMan.FindListener(aConn.iLinkType); + } + break; + + default: + listener = iLinksMan.FindListener(aConn.iLinkType); + break; + } + + if (listener) + { + accept = ETrue; + if (iLinksMan.IsAcceptPairedOnlyMode()) + { + // do not accept if we are only accepting connection + // requests from paired devices and we are not already + // paired with the remote + CBTPairingsCache::TPairingState pairingState = iLinksMan.PairingsCache().IsPaired(aConn.iBdaddr); + + if(pairingState == CBTPairingsCache::EDeferred) + { + // We're still waiting for the Pairing Caches paired device list to be filled. + // We'll respond when this is complete, so store details away for then. + LOG(_L("CPhysicalLink: Waiting for physical link manager's paired device list from Registry!")) + iPendingConnection = ETrue; + iLastPendingConnection.iConnH = aConn.iConnH; + iLastPendingConnection.iBdaddr = aConn.iBdaddr; + iLastPendingConnection.iCoD = aConn.iCoD; + iLastPendingConnection.iLinkType = aConn.iLinkType; + iLastPendingConnection.iEncryptMode = aConn.iEncryptMode; + // Return now as we are waiting and don't want to 'RejectConnection' + return; + } + + // If here then the cache has either informed us that the device is paired + // or not. We only accept paired connections. + accept = (pairingState == CBTPairingsCache::EPaired) ? ETrue : EFalse; + } + if (accept) + { + // this may mean the physical link's acceptance is determined by the + // ACL Logical link - but that's what the spec implies :-) + accept = static_cast(listener->iObserver)->ConnectRequest(aConn, *this); + // since we only support one SCO listener at present we dont need to mark it as the acceptor + // or indeed find a specific one: later we could choose via CoD + } + } + + if (accept) + { + + // store CoD now, not told it ever again! if connection fails this object will go + // Don't update CoD if it is 0 (NULL) as this is the default. + // This also prevents a valid CoD being overwritten. + if (aConn.iCoD != 0) + { + iDevice.SetDeviceClass(aConn.iCoD); + } + + TUint8 roleSwitch = static_cast(iLinksMan.PassiveConnectBecomeMaster() ? 0x00 : 0x01); + + TInt err; + if (aConn.iLinkType == EeSCOLink) + { + TBTeSCOLinkParams options; + + CeSCOLink* eSCO = static_cast(listener->iObserver); + TUint16 packetMask = eSCO->GetPacketMask(); + eSCO->GetExtOptions(options); + + TRAP(err, iLinksMan.HCIFacade().AcceptSynchronousConnectionL(aConn.iBdaddr, + options.iBandwidth.iTransmit, options.iBandwidth.iReceive, + options.iLatency, options.iCoding, options.iRetransmissionEffort, + packetMask + )); + } + else + { + TRAP(err, iLinksMan.HCIFacade().AcceptConnectionRequestL(aConn.iBdaddr, roleSwitch)); + } + + if (err == KErrNone) + { + // assume we're slave until told otherwise on ACL(=PHY) links + if (aConn.iLinkType == EACLLink) + { + // this is a PHY connect request too, so we should store our role + //The handle in 'aConn' is bogus, so.. + iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending); + iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultPacketType, aConn.iLinkType); + iLinkState.SetLinkRole((roleSwitch == 0x01) ? ESlave : EMaster); + + // have to store it now, since the complete wont tell us anything! + } + else + { + //The handle in 'aConn' is bogus, so.. + iLinksMan.Baseband().UpdateModel(aConn.iBdaddr, KHCIDefaultSCOPacketType, aConn.iLinkType); + } + } + else + { + // Out of memory: + // Cause newly created spawned SAP (whether ACL or SCO) + // to die. + // Attempt to reject connection with newly freed memory... + // ...this of course may fail in which case we have + // to rely on supervison timeouts. + if (aConn.iLinkType == EACLLink) + { + TInt last = iACLLogicalLinks.Count() - 1; + if(last>=0) + { + iACLLogicalLinks[last]->Error(err); + } + } + else + { + if(iSyncLogicalLink) + { + iSyncLogicalLink->Error(err); + } + } + RejectConnection(aConn); + } + } + else + { + // reject + RejectConnection(aConn); + } + + // Tell inquiry manager - should have a valid CoD within 'aConn' + // Don't update CoD if it is 0 (NULL) as this is the default. + // This also prevents a valid CoD being overwritten. + if (aConn.iCoD != 0) + { + iLinksMan.LinkManagerProtocol().InquiryMgr().CoDResult(aConn.iBdaddr, aConn.iCoD); + } + } + +void CPhysicalLink::RejectConnection(const TBTConnect& aConn) + { + LOG_FUNC + if (aConn.iLinkType == EeSCOLink) + { + TRAP_IGNORE(iLinksMan.HCIFacade().RejectSynchronousConnectionL(aConn.iBdaddr, KDefaultRejectReason)); + } + else + { + TRAP_IGNORE(iLinksMan.HCIFacade().RejectConnectionRequestL(aConn, KDefaultRejectReason)); + } + } + + +void CPhysicalLink::GetDeviceFromRegistryL() + { + LOG_FUNC + CBTDeviceGetter* getter = CBTDeviceGetter::NewL(iRegSess, *this); + iRegistryHelpers.AddLast(*getter); + getter->Start(BDAddr()); // now get the rest of the details + // just BDAddr? + } + +TInt CPhysicalLink::GetOption(TUint aLevel,TUint aName,TDes8& aOption) const + { + LOG_FUNC + // good stuff here! + if (aLevel == KSolBtLM) + { + switch (aName) + { + case KLMGetBasebandHandle: + { + if (aOption.Length() != sizeof(THCIConnHandle)) + { + return KErrArgument; + } + aOption = TPtrC8(reinterpret_cast(&iHandle), sizeof(THCIConnHandle)); + return KErrNone; + } + + case EBBGetSniffInterval: + if (aOption.Length() != sizeof(TBasebandTime)) + { + return KErrArgument; + } + aOption = TPtrC8(reinterpret_cast(&iSniffInterval), sizeof(TBasebandTime)); + return KErrNone; + + default: + return KErrNotSupported; + } + } + else + { + return KErrNotSupported; + } + } + +TInt CPhysicalLink::Arbitrate(const TBool aImmediately, const TBool aLocalPriority) + { + LOG_FUNC + if (!IsConnected()) + return KErrDisconnected; + + if ( aImmediately ) + { + iArbitrationDelay->Cancel(); + } + else if (iArbitrationDelay->IsActive()) + { + return KErrNone; + } + + //start arbitrate process with what our local controller supports + TUint8 allowedModesMask = EHoldMode | EParkMode | ESniffMode; // local features sorted out later + TBool roleSwitchAllowed = EFalse; + + if (iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally() && iLinksMan.RoleSwitchAllowed()) + { + roleSwitchAllowed = ETrue; + } + + // ask proxies what they want from the PHY + TUint8 requestedModeMask = 0; + TUint8 requestedMode = 0; + TBool activeModeIsRequested = EFalse; + TSglQueIter iter(iProxySAPs); + while (iter) + { + CBTProxySAP* proxy = iter++; + + requestedMode = proxy->GetRequestedModes(); + requestedModeMask |= requestedMode; + + if (requestedMode == EActiveMode && proxy->RequestedActiveMode()) + { + // An Active Mode request will override all other local low power mode requests + // but continue to collect the requirement from the other proxies.. + activeModeIsRequested = ETrue; + } + + allowedModesMask &= proxy->GetAllowedModes(); + roleSwitchAllowed &= proxy->IsRoleSwitchAllowed(); + } + + if (activeModeIsRequested) + { + // Any Active Mode request will override all other low power mode requests, + // so overwrite the requestedModeMask but keep allowedModesMask and roleSwitchAllowed + // as specified by all the local proxies + requestedModeMask = EActiveMode; + } + + // clear out modes not supported by local Controller + allowedModesMask &= iLinksMan.LinkManagerProtocol().ModesSupportedLocally(); + + if(iOverrideParkRequests) + { + // We wish to ensure the physical link is not in PARK mode. + // The only way to guarantee this is to disallow PARK via the link policy settings. + allowedModesMask &= ~EParkMode; + } + + if(allowedModesMask != iLinkPolicy.LowPowerModePolicy() + || roleSwitchAllowed != iLinkPolicy.IsSwitchAllowed()) + { + // Controller policy for the connection needs updating + SetModesAllowed(allowedModesMask, roleSwitchAllowed); + } + + //If OverrideLPM flag is set, we do not disable LP modes via the link policy settings + //This is done because OverrideLPM should not prevent remotes putting us into an LPM + //Later on, when OverrideLPM flag is cancelled, it would allow us to enforce requested LPM + if(iOverrideLPMRequests) + { + // We need to ensure the physical link is in active mode. + allowedModesMask = EActiveMode; + } + + TUint8 modeChangeMask = static_cast(requestedModeMask & allowedModesMask); + TUint8 modeCompareMask = 0; + + if(aLocalPriority) + { + // If we want local priority, we go with what we want to do + // irrespective of what the remote may have previously requested. + modeCompareMask = modeChangeMask; + } + else + { + // This logic allows us to see if the current low power mode has recently + // changed NOT because of a change in the local proxies' requests, but (probably) + // because a remote device has changed it.. + + // modeCompareMask should start only having zero bits where + // requestedModeMask has a zero bit and iPreviousRequestedModeMask does not + // i.e. a mode is newly no longer requested. + modeCompareMask = requestedModeMask | ~iPreviousRequestedModeMask; + + // Remove bits from modeCompareMask that are not in allowedModesMask + // We cannot stay in a power mode that we do not allow. + modeCompareMask &= allowedModesMask; + } + + iPreviousRequestedModeMask = requestedModeMask; // Update previous requested to current value. + + TUint8 currentModeMask = static_cast(iLinkState.LinkMode()); + if(modeCompareMask & currentModeMask) + { + // The current state is the same as the permitted required role(s). + return KErrNone; + } + + if(modeChangeMask == EActiveMode && currentModeMask != EActiveMode) + { + // The current low power mode should be exited. + return RequestActive(); + } + + if(modeChangeMask != EActiveMode) + { + if(currentModeMask != EActiveMode) + { + // The system is currently in a low power mode. Exit this before + // entering the new mode. + TInt rerr = RequestActive(); + if(rerr != KErrNone) + { + return rerr; + } + } + + if(modeChangeMask & EHoldMode) + { + return RequestHold(); + } + if(modeChangeMask & ESniffMode) + { + return RequestSniff(); + } + if(modeChangeMask & EParkMode) + { + return RequestPark(); + } + } + + // This point in the code is reached if the Link Policy settings are + // changed but the mode is not. Return OK error code. + return KErrNone; + } + +void CPhysicalLink::SetPassKey(const TDesC8& aPassKey) + { + LOG_FUNC + // We store the key for use if it succeeds. + iNewPinCode.Copy(aPassKey); + iNewPinCodeValid = ETrue; + } + +const TBTPinCode& CPhysicalLink::PassKey() const + { + LOG_FUNC + return iDevice.PassKey(); + } + +void CPhysicalLink::StartArbitrationTimer() const + { + LOG_FUNC + iArbitrationDelay->Start(); + } + +TInt CPhysicalLink::Connect(TBasebandPageTimePolicy aPolicy) + { + LOG_FUNC + // assume that we will be master until told otherwise + ASSERT_DEBUG(!IsConnected()); + + UpdateFromInquiryCache(); + + TUint8 psrm = iDevice.IsValidPageScanRepMode() ? iDevice.PageScanRepMode() : TUint8(KDefaultBluetoothPageScanRepMode); + TUint8 psm = iDevice.IsValidPageScanMode() ? iDevice.PageScanMode() : TUint8(KDefaultBluetoothPageScanMode); + TUint16 clockOffset = iDevice.IsValidClockOffset() ? iDevice.ClockOffset() : TUint16(KDefaultBluetoothClockOffset); + + TUint8 allowRoleSwitch = static_cast(iLinksMan.ActiveConnectRoleSwitchAllowed()); + + TUint16 pkt = KHCIDefaultPacketType; + + // optimise paging (as a best-effort attempt). + TBasebandTime pagetimeout = CalculatePageTimeout(aPolicy, psrm, clockOffset & KHCIClockOffsetValidBit); + iLinksMan.TryToChangePageTimeout(pagetimeout); + + TRAPD(ret, iLinksMan.HCIFacade().ConnectL(iDevice.Address(), pkt, psrm, psm, clockOffset, allowRoleSwitch)); + if(ret==KErrNone) + { + iLinkState.SetLinkState(TBTBasebandLinkState::ELinkPending); + iLinksMan.Baseband().UpdateModel(iDevice.Address(), pkt, EACLLink); + iLinkState.SetLinkRole(EMaster); + } + + return ret; + } + +TInt CPhysicalLink::SCOConnect() +/** + A utility service provided to the SCO transport + We bring up a "default" SCO link, choosing the packet based on what we + know the controllers can support +*/ + { + LOG_FUNC + return SCOConnect(EAnySCOPacket); + } + +TInt CPhysicalLink::SCOConnect(const TUint16 aUserHVPacketTypes) +/** + A utility service provided to the SCO transport + We bring up a SCO link which allows only the user specified packet types + Implementation of SCOSAP::SetOption guarantees aUserHVPacketTypes contains at + least one valid SCO packet type +*/ + { + LOG_FUNC + TBTSCOPackets scoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SCOPackets(); + + scoPackets &= aUserHVPacketTypes; + // combine with remote supported packets + scoPackets &= iRemoteFeatures.SCOPackets(); + + TInt ret; + + if (!scoPackets) + ret = KErrNotSupported; + else + { + // we put all in for now - if HW doesn't support that, the HCI can be changed + OverridePark(); + + TRAP(ret, iLinksMan.HCIFacade().SCOConnectL(Handle(), scoPackets, BDAddr())); + if(ret==KErrNone) + { + iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, scoPackets, ESCOLink); + } + } + return ret; + } + +TInt CPhysicalLink::SynchronousConnect(TUint aTransmitBandwidth, TUint aReceiveBandwidth, + TUint16 aMaxLatency, TUint16 aVoiceSettings, + TUint8 aRetransmissionEffort, const TBTSyncPacketTypes aUserPacketTypes) +/** + A utility service provided to the eSCO transport + We bring up a eSCO link which allows only the user specified packet types + Implementation of SCOSAP::SetOption guarantees aUserPacketTypes contains at + least one valid SCO packet type +*/ + { + LOG_FUNC + TBTSyncPacketTypes escoPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally().SyncPackets(); + + escoPackets &= aUserPacketTypes; + // combine with remote supported packets + escoPackets &= iRemoteFeatures.SyncPackets(); + + TInt ret; + + if (!escoPackets) + ret = KErrNotSupported; + else + { + // we put all in for now - if HW doesn't support that, the HCI can be changed + // THCIConnHandle aACLHandle, TUint aTransmitBandwidth, TUint aReceiveBandwidth, + // TUint16 aMaxLatency, TUint16 aVoiceSettings, TUint8 aRetransmissionEffort, + // TUint16 aPacketTypeMask + TRAP(ret, iLinksMan.HCIFacade().SetupSynchronousConnectionCommandL( + Handle(), + aTransmitBandwidth, aReceiveBandwidth, + aMaxLatency, aVoiceSettings, aRetransmissionEffort, + escoPackets, + BDAddr() + )); + if(ret==KErrNone) + { + iLinksMan.Baseband().UpdateModel(Handle(), EACLLink, escoPackets, EeSCOLink); + } + } + return ret; + } + +TInt CPhysicalLink::SubscribeProxySAP(CBTProxySAP& aProxySAP) + { + LOG_FUNC + // a new Proxy SAP wishes to attach to this physical SAP +#ifdef _DEBUG + // check to see if the same proxy is already there + TSglQueIter iter(iProxySAPs); + while (iter) + { + ASSERT_DEBUG(iter++!=&aProxySAP); + } +#endif + SubscribeLinkObserver(aProxySAP); + iProxySAPs.AddLast(aProxySAP); + Arbitrate(); + return KErrNone; + } + +void CPhysicalLink::UnsubscribeProxySAP(CBTProxySAP& aProxySAP) + { + LOG_FUNC + ASSERT_DEBUG(!iProxySAPs.IsEmpty()); + iProxySAPs.Remove(aProxySAP); + UnsubscribeLinkObserver(aProxySAP); + Arbitrate(); // good time to see what phy properties should be used + PhysicalLinkUserIdle(); + } + + +TInt CPhysicalLink::AttachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP) + { + // effectively this part of this class encapsulates the Logical Transports + // but they're not really worthy of a class - just some understanding that they're + // not ignored + LOG_FUNC + TInt retVal = KErrNone; + + switch (aLink) + { + case EACLLink: + { + // it is important for error handling at the end of + // CPhysicalLink::ConnectionRequest that this remains an + // append i.e. 'aSAP' is added to the end of the queue + retVal = iACLLogicalLinks.Append(static_cast(&aSAP)); + break; + } + + case ESCOLink: + { + if (!(iRemoteFeatures.IsSCOLinkSupported())) + retVal = KErrNotSupported; + else + { + // Don't attach SCO if already exists, avoids confusion with some h/w. + // .....return the outcome, higher levels depend on this action + if (iSyncLogicalLink) + { + retVal = KErrInUse; + } + else + { + iSyncLogicalLink = static_cast(&aSAP); + } + } + break; + } + + case EeSCOLink: + { + if (!(iRemoteFeatures.IsExtendedSCOLinkSupported())) + retVal = KErrNotSupported; + else + { + if (iSyncLogicalLink) + { + retVal = KErrInUse; + } + else + { + iSyncLogicalLink = static_cast(&aSAP); + } + } + break; + } + + default: + Panic(EBTUnknownLogicalLink); + } + + if (retVal == KErrNone) + { + RemoveIdleTimer(); + } + + return retVal; + } + +void CPhysicalLink::DetachLogicalLink(TLinkType aLink, CBTBasebandSAP& aSAP) + { + LOG_FUNC + LOG3(_L("CPhysicalLink: Logical Link type %d, 0x%08x detaching from PHY 0x%08x"), aLink, &aSAP, this); + switch (aLink) + { + case EACLLink: + { +#ifdef _DEBUG + TInt numFound = 0; +#endif + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + if (iACLLogicalLinks[i] == &aSAP) + { +#ifdef _DEBUG + numFound++; +#endif + iACLLogicalLinks.Remove(i); // don't delete - it's not ours! + } + } + __ASSERT_DEBUG(numFound==1, Panic(EBTACLLogicalLinkBadDebind)); + break; + } + + case ESCOLink: + case EeSCOLink: + { + __ASSERT_DEBUG(iSyncLogicalLink==&aSAP, Panic(EBTSCOLogicalLinkBadDebind)); + iSyncLogicalLink = NULL; + break; + } + + default: + Panic(EBTUnknownLogicalLink); + } + } + +void CPhysicalLink::PhysicalLinkUserIdle() + { + LOG_FUNC + if(IsPhysicalLinkIdle() && iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp) + { + // try to stash device details - juice and last used date + TTime t; + t.UniversalTime(); + iDevice.SetUsed(t); + TRAPD(ignore, StoreDeviceL(ETrue)); //ETrue: new meaning - prevent addition of device in registry + + if (!iIdleTimerQueued) + // we don't want to send the read clock offset command more than one + // here, so just check if we already passed from here. + { + // Take this opportunity to get latest info on clock offset + // so that we can stash it in HR - subsequent connections may then be quicker + TRAP(ignore, iLinksMan.HCIFacade().ReadClockOffsetL(iHandle)); + // ignore error - was an optimisation + + // Disconnect the PHY, it is no longer required. + QueueIdleTimer(KPhysicalLinkIdleTimeout); + } + } + } + + +TBool CPhysicalLink::IsPhysicalLinkIdle() const + { + LOG_FUNC + TBool physicalLinkIdle = ETrue; + // Check ACL and SCO links. If any link is not idle, then the PHY remains + // active. + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + physicalLinkIdle &= iACLLogicalLinks[i]->IsIdle(); + } + + if(iSyncLogicalLink) + { + physicalLinkIdle &= iSyncLogicalLink->IsIdle(); + } + + if (iPinRequester) + { + physicalLinkIdle = EFalse; + } + + if (IsAuthenticationPending()) + { + physicalLinkIdle = EFalse; + } + + return physicalLinkIdle; + } + + +void CPhysicalLink::QueueIdleTimer(TInt aTime) +/** + Queue idle timer entry. + When this timer expires, it'll call TryToClose, which actually + causes the thing to finally close down. +**/ + { + LOG_FUNC + if (!iIdleTimerQueued) + { + TCallBack cb(TerminateCallback, this); + iIdleTimerEntry.Set(cb); + iIdleTimeout = aTime; + BTSocketTimer::Queue(iIdleTimeout * 1000000, iIdleTimerEntry); + iIdleTimerQueued = ETrue; + } + } + + +void CPhysicalLink::RemoveIdleTimer() +/** + Called whenever we're opened. + Checks there are no idle timer entries queued. +**/ + { + LOG_FUNC + if (!iIdleTimerQueued) + { + // it's fine for callers to just try to remove the timer + return; + } + BTSocketTimer::Remove(iIdleTimerEntry); + iIdleTimerQueued = EFalse; + } + +void CPhysicalLink::QueueLPMOverrideTimer(TInt aTimeout) +/** + Queue LPM Override timer entry. + When this timer expires, it'll call UndoLPMOverride. +**/ + { + LOG_FUNC + __ASSERT_DEBUG(aTimeout!=0, Panic(EBTPhysicalLinksInvalidArgument)); + if (iLPMOverrideTimerQueued) + { + BTSocketTimer::Remove(iOverrideLPMTimerEntry); + } + BTSocketTimer::Queue(aTimeout, iOverrideLPMTimerEntry); + iLPMOverrideTimerQueued = ETrue; + } + +void CPhysicalLink::NotifyStateChange(TBTBasebandEventNotification& aEvent) + { + LOG_FUNC + // If this event is 'physical link down' / 'physical link error' then any ProxySAP + // subscribers will unsubscribe as a result of PhysicalLinkChange being called. + // This will result in the entry being removed from the iBasebandSubscribers array. + // Consequently the array must be traversed from end to start. + TDblQueIter iter(iBasebandSubscribers); + TPhysicalLinkObserverQLink* l; + while ((l=iter++)!=NULL) + { + l->Item()->PhysicalLinkChange(aEvent, *this); + } + + if(((aEvent.EventType() & ENotifyActiveMode) || + (aEvent.EventType() & ENotifySniffMode)|| + (aEvent.EventType() & ENotifyParkMode)|| + (aEvent.EventType() & ENotifyHoldMode)) && + (aEvent.ErrorCode() == KErrNone)) + { + iArbitrationDelay->Start(); + } + } + +/*static*/ TInt CPhysicalLink::TerminateCallback(TAny* aCPhysicalLink) + { + LOG_STATIC_FUNC + CPhysicalLink* c = reinterpret_cast(aCPhysicalLink); + c->iIdleTimerQueued = EFalse; + + TInt retVal = EFalse; + + // Queue another idle timer in case this disconnect fails + + // Check if the physical link is still idle after the timer. + if(c->IsPhysicalLinkIdle() && c->iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp) + { + // Increase the idle time for the next callback by just over 50% + TInt newIdleTime = (c->iIdleTimeout * 3)/2 + 1; + if (newIdleTime > KMaxPhysicalLinkIdleTimeout) + { + newIdleTime = KMaxPhysicalLinkIdleTimeout; + } + + c->QueueIdleTimer(newIdleTime); + if (c->Terminate(ERemoteUserEndedConnection) == KErrNone) + { + retVal = EFalse; + } + else + { + retVal = ETrue; + } + } + return retVal; + } + +TInt CPhysicalLink::Terminate(THCIErrorCode aReason) + { + LOG_FUNC + TInt err = KErrNone; + + if (iLinkState.LinkState() == TBTBasebandLinkState::ELinkPending) + { + // If the Link is not yet up then we cannot know the correct connection handle + // to disconnect so remember the request so we disconnect the link straight away + // after it does come up. + iDisconnectRequested = ETrue; + } + else + { + __ASSERT_DEBUG(iLinkState.LinkState() == TBTBasebandLinkState::ELinkUp, Panic(EInvalidLinkStateDuringDisconnect)); + + TRAP(err, iDisconnectCtrl.Disconnect(aReason)); + } + + return err; + } + +void CPhysicalLink::SetModesAllowed(TUint8 aModesAllowed, TBool aRoleSwitchAllowed) + { + LOG_FUNC + TBTLinkModeSet supportedModes = iRemoteFeatures.LinkModes(); + aModesAllowed &= supportedModes; + aRoleSwitchAllowed &= IsRoleSwitchSupported(); + + if(aModesAllowed != iLinkPolicy.LowPowerModePolicy() || + aRoleSwitchAllowed != iLinkPolicy.IsSwitchAllowed()) + { + TLinkPolicy tempPolicy; + tempPolicy.SetModesAllowed(aModesAllowed); + tempPolicy.SetSwitchAllowed(aRoleSwitchAllowed); + + TRAPD(err, iLinksMan.HCIFacade().WriteLinkPolicySettingsL(Handle(), tempPolicy.LinkPolicy())); + + if (err==KErrNone) + { + iLinkPolicy = tempPolicy; + } + } + } + +void CPhysicalLink::NotifyLogicalLinkDown(TPhysicalLinkPort aPort) + { + LOG_FUNC + switch (aPort) + { + case EACLLink: + { + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + // subscribers will unsubscribe as a result of this + // This will result in the entry being removed from the iACLLogicalLinks array. + // Consequently the array must be traversed from end to start. + iACLLogicalLinks[i]->Disconnection(); + } + break; + } + case ESCOLink: + case EeSCOLink: + { + if (iSyncLogicalLink) + { + iSyncLogicalLink->Disconnection(); + } + break; + } + + default: + Panic(EBTPhysicalLinkBadPort); + } + } + +void CPhysicalLink::NotifyLogicalLinkUp(const TBTConnect& aConnect) + { + LOG_FUNC + switch (aConnect.iLinkType) + { + case EACLLink: + { + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + // all of them are open + iACLLogicalLinks[i]->ConnectComplete(aConnect); + } + break; + } + + case ESCOLink: + case EeSCOLink: + { + if (iSyncLogicalLink) + { + iSyncLogicalLink->ConnectComplete(aConnect); + } + break; + } + + default: + Panic(EBTPhysicalLinkBadPort); + } + } + +void CPhysicalLink::NotifyLogicalSyncLinkUp(const TBTConnect& aConnect, const TBTSyncConnectOpts& aSyncOpts) + { + LOG_FUNC + switch (aConnect.iLinkType) + { + case ESCOLink: + case EeSCOLink: + { + if (iSyncLogicalLink) + { + iSyncLogicalLink->SyncConnectComplete(aConnect, aSyncOpts); + } + break; + } + + default: + Panic(EBTPhysicalLinkBadPort); + } + } + + +void CPhysicalLink::NotifyLogicalLinkError(TPhysicalLinkPort aPort, TInt aError) + { + LOG_FUNC + switch (aPort) + { + case EACLLink: + { + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + // all of them are open + iACLLogicalLinks[i]->Error(aError); + } + break; + } + + case ESCOLink: + case EeSCOLink: + { + if (iSyncLogicalLink) + { + iSyncLogicalLink->Error(aError); + } + break; + } + + default: + Panic(EBTPhysicalLinkBadPort); + } + } + + +TBool CPhysicalLink::IsModeSupportedRemotely(TBTLinkMode aMode) const + { + LOG_FUNC + return iRemoteFeatures.LinkModes() & aMode; + } + +TBool CPhysicalLink::IsEncryptionPauseResumeSupported() const + { + LOG_FUNC + return (iLinksMan.LinkManagerProtocol().IsEncryptionPauseResumeSupportedLocally() && iRemoteFeatures.IsEncryptionPauseResumeSupported()); + } + +TBool CPhysicalLink::IsEncryptionSupported() const + { + LOG_FUNC + return (iRemoteFeatures.IsEncryptionSupported() && iLinksMan.LinkManagerProtocol().IsEncryptionSupportedLocally()); + } + +TBool CPhysicalLink::IsRoleSwitchSupported() const + { + LOG_FUNC + // the physical link _can_ switch when encryption is on - see RequestChangeRole + return (iRemoteFeatures.IsRoleSwitchSupported() && iLinksMan.RoleSwitchAllowed()); + } + + +// +// Change the packet types allowed on the link. +// Note that this is only suitable for use on ACL and old-style (pre eSCO) SCO links. +// +TInt CPhysicalLink::ChangeConnectionPacketType(TUint16 aType) + { + LOG_FUNC + TInt rerr = KErrNone; + + // Build a mask of locally supported packet types (we shouldn't care about + // what the remote supports (this is alluded to in the spec) probably + // because they can change too. + + // DM1, DH1 and HV1 packets are always supported. + TUint16 supportedTypes = EPacketsDM1 | EPacketsDH1 | EPacketsHV1; + + // get extra locally-supported packets (our controller may not support things) + TBTFeatures localPackets = iLinksMan.LinkManagerProtocol().PacketsSupportedLocally(); + + // 3 slot ACL packets + if (localPackets[ESupportedThreeSlotPacketsBit]) + { + supportedTypes |= (EPacketsDM3 | EPacketsDH3); + } + + // 5 slot ACL packets + if (localPackets[ESupportedFiveSlotPacketsBit]) + { + supportedTypes |= (EPacketsDM5 | EPacketsDH5); + } + + // HV2 single-slot packets + if (localPackets[ESupportedHV2PacketsBit]) + { + supportedTypes |= EPacketsHV2; + } + + // HV3 single-slot packets + if (localPackets[ESupportedHV3PacketsBit]) + { + supportedTypes |= EPacketsHV3; + } + + // Note: If one of the following EDR bits is set it indicates that the EDR + // packet type shall NOT be used on this physical link. However, we must + // not set any packet types that are not supported, so check first. + + // EDR 2Mbps + if (localPackets[EEDR_ACL_2MbpsModeBit]) + { + supportedTypes |= EPackets2_DH1; + // EDR 3 slot ACL packets + if (localPackets[ESupportedEDRThreeSlotPacketsBit]) + { + supportedTypes |= EPackets2_DH3; + } + // EDR 5 slot ACL packets + if (localPackets[ESupportedEDRFiveSlotPacketsBit]) + { + supportedTypes |= EPackets2_DH5; + } + + // EDR 3Mbps - Have to have 2Mbps to have 3Mbps + if (localPackets[EEDR_ACL_3MbpsModeBit]) + { + supportedTypes |= EPackets3_DH1; + // EDR 3 slot ACL packets + if (localPackets[ESupportedEDRThreeSlotPacketsBit]) + { + supportedTypes |= EPackets3_DH3; + } + // EDR 5 slot ACL packets + if (localPackets[ESupportedEDRFiveSlotPacketsBit]) + { + supportedTypes |= EPackets3_DH5; + } + } + } + + supportedTypes &= aType; + // Check if the new supported types differ from the current configuration. + if(supportedTypes != iLinkState.PacketTypes()) + { + rerr = iLinksMan.HCIFacade().ChangeConnectionPacketType(iHandle, supportedTypes); + } + + if(rerr == KErrNone) + { + iConnectionPacketTypeChanged = ETrue; + } + + return rerr; + } + +TInt CPhysicalLink::ExitMode(TBTLinkMode aMode) + { + LOG_FUNC + return iLowPowModeCtrl.ExitMode(aMode, iHandle); + } + +TInt CPhysicalLink::RequestMode(TBTLinkMode aMode) + { + LOG_FUNC + if (!IsConnected()) + return KErrDisconnected; + + // if active mode is required, try to exit whatever Low Power mode we are in - + // if neither sniff nor park nothing will happen. + if(aMode == EActiveMode) + { + return ExitMode(iLinkState.LinkMode()); + } + + // now request this connection goes to requested mode + return iLowPowModeCtrl.ChangeMode(aMode, iHandle); + } + +TInt CPhysicalLink::RequestChangeRole(TBTBasebandRole aRole) + { + LOG_FUNC + TInt err = KErrNone; + + // Check that the requested role is not the current role. + if(iLinkState.LinkRole() != aRole) + { + if(!iLinkPolicy.IsSwitchAllowed()) + { + err = KErrNotSupported; + } + + // 1) Role switch is not allowed if there is a sync connection + // (or there is a sync connection negotiation in progress) + else if (HasSyncLink() || SyncConnectPending()) + { + err = KErrNotReady; + } + + // 2) Check if role switch is supported on the remote device + else if (!IsRoleSwitchSupported()) + { + err = KErrNotSupported; + } + + // 3) Check if role switch is supported locally + else if (!iLinksMan.LinkManagerProtocol().IsRoleSwitchSupportedLocally()) + { + err = KErrNotSupported; + } + + // Same role request is already with LinkMgr, or most probably running just now + else if (iRoleSwitcher) + { + LOG(_L("CPhysicalLink - Same role change in progress!")) + err = KErrNone; + } + + // ok to change role + // we kick off a role switcher state machine, to remove LPM and encryption, prior to + // role switch + else + { + TRAP(err, iRoleSwitcher = CRoleSwitcher::NewL(iLinksMan, *this, aRole)); + } + } +#ifdef _DEBUG + else + { + LOG(_L("CPhysicalLink We are in requested role!!!")) + } +#endif + + return err; + } + +void CPhysicalLink::AsyncDeleteRoleSwitcher() + { + LOG_FUNC + iRoleSwitchCompleteCallBack->CallBack(); + } + +/*static*/ TInt CPhysicalLink::RoleSwitchCompleteCallBack(TAny* aPhysicalLink) + { + LOG_STATIC_FUNC + CPhysicalLink* physicalLink = static_cast(aPhysicalLink); + physicalLink->DeleteRoleSwitcher(); + return EFalse; + } + +void CPhysicalLink::DeleteRoleSwitcher() + { + LOG_FUNC + delete iRoleSwitcher; + iRoleSwitcher = NULL; + } + +TBool CPhysicalLink::IsEncryptionDisabledForRoleSwitch() const +/** + If link is encrypted, but role switcher temporarily disabled encryption, returns true. +**/ + { + LOG_FUNC + if (iRoleSwitcher) + { + return iRoleSwitcher->IsEncryptionDisabledForRoleSwitch(); + } + return EFalse; + } + +TInt CPhysicalLink::OverridePark() +/** + A request has come in that requires us to ensure we are not parked. + + Try to make it happen +**/ + { + LOG_FUNC + iOverrideParkRequests = ETrue; + + // Only arbitrate if Park mode is currently active. + TInt rerr = KErrNone; + if(iLinkState.LinkMode() & EParkMode) + { + rerr = Arbitrate(); + } + return rerr; + } + +TInt CPhysicalLink::UndoOverridePark() +/** + A need to ensure we are not parked has gone. + + Try to go back to power mode resulting from an unoverriden Arbitrate +**/ + { + LOG_FUNC + iOverrideParkRequests = EFalse; + + //Arbitrate even if there isn't an outstanding local park mode request beacuse + //the remote device may be requesting park mode. + return Arbitrate(); + } + +TInt CPhysicalLink::OverrideLPMWithTimeout(TUint aTimeout) + { + LOG_FUNC + if(aTimeout == 0) + { + return KErrNone; //facility not wanted + } + + TInt rerr = OverrideLPM(); + QueueLPMOverrideTimer(aTimeout); + + return rerr; + } + +TInt CPhysicalLink::OverrideLPM() +/** + A request has come in that requires us to ensure we are not using + an LPM (low power mode). + + Try to make it happen +**/ + { + LOG_FUNC + iOverrideLPMRequests = ETrue; + + return Arbitrate(); + } + +/*static*/ TInt CPhysicalLink::OverrideLPMTimeoutCallback(TAny* aCPhysicalLink) + { + LOG_STATIC_FUNC + CPhysicalLink* c = reinterpret_cast(aCPhysicalLink); + TInt err = c->UndoOverrideLPM(); + //we deliberately ignore this return value because we can't do anything to correct the error situation + if (KErrNone != err) + { + LOG2(_L("Physical link: UndoOverrideLPM returned an error %d on the connection 0x%08x"), err, c); + } + c->iLPMOverrideTimerQueued = EFalse; + return KErrNone; + } + +TInt CPhysicalLink::UndoOverrideLPM() +/** + A need to ensure we are not in LPM has gone. + + Try to go back to power mode resulting from an unoverriden Arbitrate +**/ + { + LOG_FUNC + iOverrideLPMRequests = EFalse; + + // Call Arbitrate(...) to determine if removing the override requires any change to + // the physical link state. + return Arbitrate(); + } + +// security stuff +// connection looks after this as it may be passively initiated and there wont +// be a accessrequester in those situations + +void CPhysicalLink::PinRequest(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aRequester) + { + LOG_FUNC + // If we receive a Pin_Request while another is outstanding then the controller is faulty. + // So we just ignore any multiple requests, as they do not make sense. + // However, in UDEB we panic to raise awareness that the hardware is behaving incorrectly. + __ASSERT_DEBUG(!iPinRequester, Panic(EBTConnectionPINRequestedTwice)); + + SetAuthenticationPending(EPinRequestPending); // if not already set (because the remote initiated authentication). + + if (iPinRequester) + { + return; + } + + __ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress)); + + if(!IsPairable()) + { + aRequester.PINCodeRequestNegativeReply(aAddr); + return; + } + + TBTPinCode pinCode; + if(iLinksMan.PrefetchMan().IsPrefetchAvailable(aAddr, pinCode)) + { + aRequester.PINCodeRequestReply(aAddr, pinCode); + return; + } + + iPinHandler = &aRequester; + + TRAPD(err, DoPinRequestL(aAddr, *this)); // physical links will proxy a request. + + // if there is an error we should reply PIN negative... + if (err) + { + iPinHandler->PINCodeRequestNegativeReply(aAddr); + iPinHandler = NULL; + } + } + +void CPhysicalLink::DoPinRequestL(const TBTDevAddr& aAddr, MPINCodeResponseHandler& aResponseHandler) + { + LOG_FUNC + // Bluetooth is fairly poor at allowing name requests on partially connected links + // this typically occurs in security mode 3 + + // find out is it locally or remotley initiated authentication ... + TBool locallyInitiated; //Authentication + TBool strongKeyRequired; + TUint minPasskeyLength=0; + TUint recommendedPasskeyLength=0; + + iLinksMan.SecMan().GetPassKeyLengthAndOriginator(aAddr, minPasskeyLength, locallyInitiated, strongKeyRequired); + + /* If we have an activeAccessRequester and authentication was not actually required, + * this is a speculative authentication. + * In that case we did not expect to get a PinRequest. + * We leave as the link already exists, causing the PinRequest to get a negative reply. + */ + if(iDevice.IsValidLinkKey()) + { + CBTAccessRequester* activeAccessRequester = iLinksMan.SecMan().FindActiveAccessRequester(aAddr); + if (activeAccessRequester) + { + if (!activeAccessRequester->AuthenticationRequired()) + { + User::Leave(KErrAlreadyExists); + } + } + } + + /* If link is already authenticated with a link key greater than the currently required minimum key length, then store + * the current passkey length as a recommended length. If this recommendation is followed, then the strength of + * the current link key will be maintained. This recommended length is made available via + * TBTPinCodeEntryNotifierParams::RecommendedPinCodeMinLength(). + */ + TUint devicePasskeyLength = iDevice.PassKeyLength(); + if(iDevice.IsValidLinkKey()&&devicePasskeyLength>minPasskeyLength) + { + //Ignore the previous link key length if it is not at least the minimum we require now anyway. + recommendedPasskeyLength=devicePasskeyLength; + } + else + { + //Recommend the currently required minimum length. + recommendedPasskeyLength=minPasskeyLength; + } + + iPinRequester = CBTPinRequester::NewL(BDAddr(), aResponseHandler, iLinksMan.SecMan(), + minPasskeyLength, locallyInitiated, strongKeyRequired, recommendedPasskeyLength); + } + +TBool CPhysicalLink::LinkKeyRequestPending() + { + LOG_FUNC + return iAuthStateMask & ELinkKeyRequestPending; + } + +void CPhysicalLink::SetAuthenticationPending(TUint8 aState) + { + LOG_FUNC + iAuthStateMask |= aState; + LOG1(_L("SetAuthenticationPending: set state [%d] in an authentication state mask"), aState); + } + +void CPhysicalLink::AuthenticationComplete(TUint8 aState) + { + LOG_FUNC + iAuthStateMask &= ~aState; + LOG1(_L("AuthenticationComplete: reset state [%d] in an authentication state mask"), aState); + PhysicalLinkUserIdle(); + } + +TBool CPhysicalLink::IsPairable() const + { + // Determines if pairing can be performed on the physical link; + // it previously was determined by the PIN code notifier, but with + // 2.1 pairable mode has been replaced with bondable mode and + // so being pairable is now a function of the security level + // the device is operating under. + // Here the policy is as follows: + // + // Pairable if... + // 1) Not in paired only connections mode + // OR + // 2) The link is already authenticated + // OR + // 3) A logical link was locally initiated + // OR + // 4) A dedicated bonding attempt is in progress + // (it might be better to migrate dedicated bonding above the logical + // link to remove this special case...) + // + // This policy provides strong security for while not applying + // blanket restricts on explicit user requests (i.e. outgoing + // connections). + TBool locallyInitiatedLogicalLink = EFalse; + for (TInt i=iACLLogicalLinks.Count()-1; i>=0; i--) + { + if(iACLLogicalLinks[i]->IsLocallyInitiated()) + { + locallyInitiatedLogicalLink = ETrue; + } + } + return (!iLinksMan.IsAcceptPairedOnlyMode()) + || iLinkState.Authenticated() + || locallyInitiatedLogicalLink + || iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address()); + } + +void CPhysicalLink::DeleteLinkKeyL() +/** +ensure that the LinkKey is removed from the device in the registry +**/ + { + LOG_FUNC + CBTLinkKeyDeleter* deleter = CBTLinkKeyDeleter::NewL(iRegSess, *this); + iRegistryHelpers.AddLast(*deleter); + deleter->Start(BDAddr()); + } + +TBool CPhysicalLink::IsConnectionRequestPending() const + { + LOG_FUNC + return iPendingConnection; + } + +void CPhysicalLink::PendingConnectionRequest(TInt /*aError*/) + { + LOG_FUNC + __ASSERT_DEBUG(iPendingConnection, Panic(EPhysicalLinkNoConnectionPending)); + + // Note: We could do something clever with the error code returned + // to prevent us thrashing in OOM situations, but it is complicated. + // We want to try again as now the pending connection address may + // now be in the cache even if we have a non-KErrNone error code, + // and so with that we will proceed sucessfully. But if we are + // deferred we may end up here again and again with, perhaps with a + // KErrNoMemory for example ... which is how the thrashing will occur. + // This thrashing will be limited however by the "Connection Accept + // Timeout" imposed by the controller on the host, and for this + // reason we live with it. + iPendingConnection = EFalse; + ConnectionRequest(iLastPendingConnection); + } + +TBool CPhysicalLink::IsPasskeyMinLengthOK() + { + LOG_FUNC +/** + check whether current passkey (retrieved from BTRegistry) is long enough to fulfill user passkey requirement + It must be called only when iDevice is ready and has LinkKey +**/ + __ASSERT_DEBUG(iDeviceResult==KErrNone && iDevice.IsValidLinkKey(), + Panic(EBTPhysicalLinkNotAuthenticated)); + + TUint newMinPasskeyLength=0; + TBool isPasskeyMinLengthOK = ETrue; + + TBool locInit, strongKey; + iLinksMan.SecMan().GetPassKeyLengthAndOriginator( iDevice.Address(), newMinPasskeyLength, locInit, strongKey); + + TBTPinCode currentPassKey = iDevice.PassKey(); + + // check current PIN code length against user requirement + if ( newMinPasskeyLength ) + { + if ( newMinPasskeyLength > currentPassKey().iLength ) + { + // longer PIN code is requested + isPasskeyMinLengthOK = EFalse; + } + } + + return isPasskeyMinLengthOK; + } + +void CPhysicalLink::LinkKeyRequest(const TBTDevAddr& aAddr, MLinkKeyResponseHandler& /*aRequester*/) + { + LOG_FUNC + // we don't keep a copy of the device record - we just leave the one copy with + // the baseband - it can tell us if there's a link key + // we can tell if the baseband has the device record or not + + SetAuthenticationPending(ELinkKeyRequestPending); //authentication process started by remote + + // If the ACL to the peer device is not yet connected, and the peer has initiated + // authentication then it must be in security mode 3. This information is stored and + // if the connection completes the link will be set as authenticated. + if (!IsConnected()) + { + iPeerInSecurityMode3 = ETrue; + } + + if(!iPeerInSecurityMode3 && iLinksMan.SecMan().IsDedicatedBondingAttempted(iDevice.Address())) + { + // If we are doing DedicatedBonding then we should ignore the existing linkkey + // in an attempt to generate a stronger one if possible. + // Security mode 3 is a odd case - because we get what looks like double pairing (the remote + // initiated pairing on connection, then the dedicated bonding pairing). So we have removed + // this feature for security mode 3 devices...they will have to suffer for their transgressions + LOG(_L("CPhysicalLink: Dedicated bonding attempt - Sending link key request negative reply")); + iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr); + iRequireAuthenticatedLinkKey = EFalse; + } + else if (iDeviceResult==KErrNone && iDevice.IsValidLinkKey()) + { + if (iLinksMan.SecMan().DebugMode() && iDevice.LinkKeyType() != ELinkKeyDebug) + { + LOG(_L("CPhysicalLink: Debug mode - Link to debug link key")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr); + } + else + { + if (iDevice.LinkKeyType() != ELinkKeyCombination) + { + if (iRequireAuthenticatedLinkKey && iDevice.LinkKeyType() == ELinkKeyUnauthenticatedUpgradable && IsPairable()) + { + LOG(_L("CPhysicalLink: Requiring Authenticated link key but currently only have unauthenticated")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr); + } + else + { + LOG(_L("CPhysicalLink: Issuing link key to HC now")) + iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey()); + } + } + else if(IsPasskeyMinLengthOK() && SimplePairingMode() != EPhySimplePairingEnabled) + { + LOG(_L("CPhysicalLink: Issuing link key to HC now")) + iAuthenticationCtrl.LinkKeyRequestReply(aAddr, iDevice.LinkKey()); + } + else + { + LOG(_L("CPhysicalLink: Current PIN code too short!")) + iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr); + } + } + iRequireAuthenticatedLinkKey = EFalse; + } + else if (iDeviceResult==KErrNone && !iDevice.IsValidLinkKey() || iDeviceResult==KErrNotFound) + { + LOG(_L("CPhysicalLink: No Link key available for the device")); + iAuthenticationCtrl.LinkKeyRequestNegativeReply(aAddr); + iRequireAuthenticatedLinkKey = EFalse; + } + else + { + LOG(_L("CPhysicalLink: Waiting for link key from Registry!")) + // we're still waiting for the device....we'll respond when it turns up + iWaitingForLinkKeyFromRegistry = ETrue; + } + + } + +TInt CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin) const + { + ASSERT_DEBUG(aDevAddr == this->BDAddr()); + const_cast(this)->PINCodeRequestReply(aDevAddr, aPin); + return KErrNone; + } + +TInt CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr) const + { + ASSERT_DEBUG(aDevAddr == this->BDAddr()); + const_cast(this)->PINCodeRequestNegativeReply(aDevAddr); + return KErrNone; + } + +void CPhysicalLink::PINCodeRequestReply(const TBTDevAddr& aDevAddr, const TDesC8& aPin) + { + SetPassKey(aPin); + PinRequestSent(); + iPinHandler->PINCodeRequestReply(aDevAddr, aPin); + PinRequestComplete(); + } + +void CPhysicalLink::PINCodeRequestNegativeReply(const TBTDevAddr& aDevAddr) + { + iPinHandler->PINCodeRequestNegativeReply(aDevAddr); + PinRequestComplete(); + } + + +void CPhysicalLink::PinRequestSent() + { + LOG_FUNC + iLinkKeyPending = ETrue; + } + +void CPhysicalLink::PinRequestComplete() +/** + Just clean up now that PIN request has completed +**/ + { + LOG_FUNC + delete iPinRequester; + iPinRequester = NULL; + iPinHandler = NULL; + AuthenticationComplete(EPinRequestPending); + } + + +TInt CPhysicalLink::RequestHold() + { + LOG_FUNC + return RequestMode(EHoldMode); + } + +TInt CPhysicalLink::RequestSniff() + { + LOG_FUNC + return RequestMode(ESniffMode); + } + +TInt CPhysicalLink::RequestPark() + { + LOG_FUNC + return RequestMode(EParkMode); + } + +TInt CPhysicalLink::RequestActive() + { + LOG_FUNC + return RequestMode(EActiveMode); + } + +void CPhysicalLink::ReadNewPhysicalLinkMetricValue(TUint aIoctlName, CBTProxySAP& aSAP, TInt aCurrentValue) + { + LOG_FUNC + iPhysicalLinkMetrics->ReadNewPhysicalLinkMetricValue(aIoctlName, aSAP, aCurrentValue); + } + +void CPhysicalLink::GetCurrentBasebandState(TBTBasebandEventNotification & aEvent) + { + LOG_FUNC + // Populate the event with the current baseband state. + TUint32 events = 0; + switch(iLinkState.LinkRole()) + { + case EMaster: + events |= ENotifyMaster; + break; + case ESlave: + events |= ENotifySlave; + break; + case ERoleUnknown: + default: + break; + }; + + switch(iLinkState.LinkMode()) + { + case EActiveMode: + events |= ENotifyActiveMode; + break; + case ESniffMode: + events |= ENotifySniffMode; + break; + case EParkMode: + events |= ENotifyParkMode; + break; + case EHoldMode: + events |= ENotifyHoldMode; + break; + case EScatterMode: + default: + break; + }; + + switch(iLinkState.MaxSlots()) + { + case 1: + events |= ENotifyMaxSlots1; + break; + case 3: + events |= ENotifyMaxSlots3; + break; + case 5: + events |= ENotifyMaxSlots5; + break; + default: + break; + }; + + TUint16 packetTypes = iLinkState.PacketTypes(); + if(packetTypes & EPacketsDM1) + { + events |= ENotifyPacketsDM1; + } + if(packetTypes & EPacketsDM3) + { + events |= ENotifyPacketsDM3; + } + if(packetTypes & EPacketsDM5) + { + events |= ENotifyPacketsDM5; + } + if(packetTypes & EPacketsDH1) + { + events |= ENotifyPacketsDH1; + } + if(packetTypes & EPacketsDH3) + { + events |= ENotifyPacketsDH3; + } + if(packetTypes & EPacketsDH5) + { + events |= ENotifyPacketsDH5; + } + if(packetTypes & EPacketsHV1) + { + events |= ENotifyPacketsHV1; + } + if(packetTypes & EPacketsHV2) + { + events |= ENotifyPacketsHV2; + } + if(packetTypes & EPacketsHV3) + { + events |= ENotifyPacketsHV3; + } + + if(iLinkState.Authenticated()) + { + events |= ENotifyAuthenticationComplete; + } + + if(iLinkState.Encrypted()) + { + events |= ENotifyEncryptionChangeOn; + } + else + { + events |= ENotifyEncryptionChangeOff; + } + + switch(iLinkState.LinkState()) + { + case TBTBasebandLinkState::ELinkUp: + events |= ENotifyPhysicalLinkUp; + + if (iSyncLogicalLink) + { + if (iSyncLogicalLink->IsOpen()) + { + events |= ENotifySynchronousLinkUp; + } + else + { + events |= ENotifySynchronousLinkDown; + } + } + break; + case TBTBasebandLinkState::ELinkDown: + events |= ENotifyPhysicalLinkDown; + break; + default: + break; + }; + + aEvent.SetEventType(events); + aEvent.SetErrorCode(0); + } + +TBool CPhysicalLink::ACLConnectPending() const + { + LOG_FUNC + return !IsConnected(); + } + +TBool CPhysicalLink::SyncConnectPending() const + { + LOG_FUNC + if (!iSyncLogicalLink) + { + return EFalse; + } + + return iSyncLogicalLink->ConnectPending(); + } + +TInt CPhysicalLink::ManageEncryptionEnforcement(THCIEncryptModeFlag aEnable) + { + LOG_FUNC + // Although this code has been written as if this method could be called more then once, + // with different argument values: + // ChangeEncryption() (and then ManageEncryptionEnforcement()) is called only + // once and only with aEnable = EPointToPointEncryption. + // It's called if during the physical link creation the encryption is requested. + // That is because the assertion below. + __ASSERT_DEBUG(aEnable != EEncryptionDisabled, Panic(EBTInvalidEncryptionDisableRequest)); + + // although "&& (iEncryptionEnforcer)" is not necessary, we leave it because + // clarify that we had asked for the encryption, so it's our responsability to + // delete iEncryptionEnforcer. + if ((aEnable == EEncryptionDisabled) && (iEncryptionEnforcer)) + { + delete iEncryptionEnforcer; + iEncryptionEnforcer = NULL; + } + else if ((aEnable != EEncryptionDisabled) && (!iEncryptionEnforcer)) + { + TRAPD(err, iEncryptionEnforcer = CEncryptionEnforcer::NewL(*this, aEnable)); + return err; + } + return KErrNone; + } + +TBasebandTime CPhysicalLink::CalculatePageTimeout(TBasebandPageTimePolicy aPolicy, TUint8 aRepMode, TBool aValidClockOffset) + { + LOG_FUNC + TBasebandTime pageTimeout = TBasebandPolicy::KPageTimeoutR0; + + switch (aRepMode) + { + // PSRM defined in spec + case 0x00: + // already set + break; + + case 0x01: + pageTimeout = TBasebandPolicy::KPageTimeoutR1; + break; + + case 0x02: + // fallthrough + default: + pageTimeout = TBasebandPolicy::KPageTimeoutR2; + break; + } + + if (!aValidClockOffset) + { + pageTimeout*=2; + } + else + { +// normalPageTimeout+=5; //slots + } + + // policy may not be valid - or dontcare/normal + switch (aPolicy) + { + case EPagingBestEffort: + pageTimeout = static_cast(TBasebandPolicy::KBestEffortTimeMultiplier * pageTimeout); + break; + + case EPagingQuick: + pageTimeout = static_cast(TBasebandPolicy::KQuickTimeMultiplier * pageTimeout); + break; + + default: + break; + // leave as is + + } + return pageTimeout; + } + +// CArbitrationDelayTimer + +CArbitrationDelayTimer::CArbitrationDelayTimer(CPhysicalLink* aParent) + :CTimer(CActive::EPriorityStandard), + iParent(aParent) + { + LOG_FUNC + } + +void CArbitrationDelayTimer::ConstructL() + { + LOG_FUNC + CTimer::ConstructL(); + CActiveScheduler::Add(this); + } + +CArbitrationDelayTimer* CArbitrationDelayTimer::NewL(CPhysicalLink* aParent) + { + LOG_STATIC_FUNC + CArbitrationDelayTimer* self = new (ELeave) CArbitrationDelayTimer(aParent); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CArbitrationDelayTimer::Start() + { + LOG_FUNC + if (IsActive()) + { + Cancel(); + } + After(KBTArbitrationDelay); + } + + +void CArbitrationDelayTimer::RunL() +/** +Allow arbitration of low power modes when the timer expires +**/ + { + LOG_FUNC + if (iParent) + { + iParent->Arbitrate(); + } + } + +TInt CPhysicalLink::GetNumPendingHandles(TInt& aConnectionHandles, TLinkType aLinkType) const + { + LOG_FUNC + aConnectionHandles = 0; + + switch(aLinkType) + { + case EeSCOLink: + + if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending()) + { + // Currently there is a maximum of one synclink per + // physical link + + ++aConnectionHandles; + } + + break; + + case ESCOLink: + + if (iSyncLogicalLink && iSyncLogicalLink->ConnectPending()) + { + // Currently there is a maximum of one synclink per + // physical link + + ++aConnectionHandles; + } + + break; + + case EACLLink: + + if (!IsConnected()) + { + // Currently there is a maximum of one asynclink per + // physical link + ++aConnectionHandles; + } + + break; + + default: + return KErrUnknown; + } + + return KErrNone; + } + +TInt CPhysicalLink::GetConnectionHandles(RHCIConnHandleArray& aConnectionHandles, + TLinkType aLinkType) const + { + LOG_FUNC + // Always append to client supplied array - do not clear the + // contents. Only return connected handles. + + switch(aLinkType) + { + case EeSCOLink: + + if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending()) + { + return KErrNone; + } + + if (iSyncLogicalLink->LinkType() == EeSCOLink) + { + // Currently there is a maximum of one synclink per + // physical link + aConnectionHandles.Append(iSyncLogicalLink->Handle()); + } + + break; + + case ESCOLink: + + if (!iSyncLogicalLink || iSyncLogicalLink->ConnectPending()) + { + return KErrNone; + } + + if (iSyncLogicalLink->LinkType() == ESCOLink) + { + // Currently there is a maximum of one synclink per + // physical link + aConnectionHandles.Append(iSyncLogicalLink->Handle()); + } + + break; + + case EACLLink: + // Currently there is a maximum of one asynclink per physical + // link and they share the same handle - this may change in + // the future for QoS. + + if (!IsConnected()) + { + return KErrNone; + } + + aConnectionHandles.Append(iHandle); + break; + + default: + return KErrUnknown; + } + + return KErrNone; + } + +void CPhysicalLink::IOCapabilityAskForResponse(THCIIoCapability aIOCapability, THCIOobDataPresence aOOBDataPresence, THCIAuthenticationRequirement aAuthenticationRequirement) + { + LOG_FUNC + iIOCapsReceived = ETrue; + iIOCapability = aIOCapability; + iOOBDataPresence = aOOBDataPresence; + iAuthenticationRequirement = aAuthenticationRequirement; + + //If we haven't determined the SSP pairing mode till now then enable it and notify the state m/c. + //This condition is to cater the fast remote device which responds very quickly, + //even before we determine whether it supports simple pairing!*/ + __ASSERT_DEBUG(((SimplePairingMode() == EPhySimplePairingEnabled) || (SimplePairingMode() == EPhySimplePairingUndefined)),Panic(EBTSSPModeChangedDuringConnection)); + if(SimplePairingMode() == EPhySimplePairingUndefined) + { + //Since we have received a I/O cap response the simple pairing must be enabled + iSimplePairingMode = EPhySimplePairingEnabled; + iLinksMan.SecMan().SimplePairingSupportDetermined(BDAddr()); + } + } + + +TBool CPhysicalLink::AuthWithMITM() const + { + LOG_FUNC + return (iLocalMITM || (iAuthenticationRequirement & KAuthenticationMitmReqMask)) + && (iIOCapability != EIOCapsNoInputNoOutput); + } + +void CPhysicalLink::SetLocalMITM(TBool aLocalMITM) + { + LOG_FUNC + iLocalMITM = aLocalMITM; + } + +TPhysicalLinkSimplePairingMode CPhysicalLink::SimplePairingMode() const + { + LOG_FUNC + return iSimplePairingMode; + } + +TBool CPhysicalLink::HasRemoteOobData() const + { + LOG_FUNC + return iLinksMan.SecMan().OobDataManager().HasRemoteOobData(BDAddr()); + } + +THCIAuthenticationRequirement CPhysicalLink::AuthenticationRequirement() const + { + LOG_FUNC + return iAuthenticationRequirement; + } + +void CPhysicalLink::NewNumericComparatorL(const TBTDevAddr aAddr, + CBTSecMan& aSecMan, + TUint32 aNumericValue, + TBool aInternallyInitiated) + { + LOG_FUNC + __ASSERT_DEBUG(aAddr == BDAddr(), Panic(EBTConnectionBadDeviceAddress)); + + // Check remote IO capabilities so that the BTNumericComparator can tell the user + TBTNumericComparisonParams::TComparisonScenario compScenario = TBTNumericComparisonParams::ERemoteCanConfirm; + if(iIOCapsReceived) + { + switch(iIOCapability) + { + case EIOCapsDisplayOnly: + compScenario = TBTNumericComparisonParams::ERemoteCannotConfirm; + break; + case EIOCapsDisplayYesNo: + compScenario = TBTNumericComparisonParams::ERemoteCanConfirm; + break; + default: + // If iIOCapability = EIOCapsKeyboardOnly or EIOCapsNoInputNoOutput + // we should not be using a Numeric Comparitor + __ASSERT_DEBUG(EFalse, Panic(EUnexpectedIOCapability)); + break; + } + } + iNumericComparator = CBTNumericComparator::NewL(aAddr, aSecMan, aNumericValue, compScenario, aInternallyInitiated); + } + +CBTNumericComparator* CPhysicalLink::InstanceNumericComparator() const + { + LOG_FUNC + return iNumericComparator; + } + +TBool CPhysicalLink::IsNumericComparatorActive()const + { + LOG_FUNC + return iNumericComparator->IsActive(); + } + +void CPhysicalLink::DeleteNumericComparator() + { + LOG_FUNC + delete iNumericComparator; + iNumericComparator = NULL; + } +void CPhysicalLink::CancelNumericComparator() + { + LOG_FUNC + iNumericComparator->Cancel(); + } + +void CPhysicalLink::NewPasskeyEntryL(const TBTDevAddr aAddr, + CBTSecMan& aSecMan, + TUint32 aNumericValue, + TBool aInternallyInitiated) + { + LOG_FUNC + iPasskeyEntry = CBTPasskeyEntry::NewL(aAddr, aSecMan, aNumericValue, aInternallyInitiated); + } + +CBTPasskeyEntry* CPhysicalLink::InstancePasskeyEntry() const + { + LOG_FUNC + return iPasskeyEntry; + } + +TBool CPhysicalLink::IsPasskeyEntryActive()const + { + LOG_FUNC + return iPasskeyEntry->IsActive(); + } + +void CPhysicalLink::DeletePasskeyEntry() + { + LOG_FUNC + delete iPasskeyEntry; + iPasskeyEntry = NULL; + } + +void CPhysicalLink::CancelPasskeyEntry() + { + LOG_FUNC + iPasskeyEntry->Cancel(); + } + +void CPhysicalLink::PasskeyEntryKeyPressed(THCIPasskeyEntryNotificationType aKey) + { + LOG_FUNC + iPasskeyEntry->KeyPressed(aKey); + } + + +TBasebandTime CPhysicalLink::GetSniffInterval() const + { + return iSniffInterval; + } + +// +// TLowPowModeCmdController +// + +TLowPowModeCmdController::TLowPowModeCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : + iParent(aLink), + iCmdController(aCmdController), + iOutstandingCmd(EFalse) + { + LOG_FUNC + } + +void TLowPowModeCmdController::Abort() + { + LOG_FUNC + LOG(_L("TLowPowModeCmdController::Abort")); + iCmdController.MhcqRemoveAllCommands(*this); + } + +void TLowPowModeCmdController::DoExitModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + LOG2(_L("TLowPowModeCmdController::DoExitModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd); + if (!iOutstandingCmd) + { + switch (aMode) + { + case ESniffMode: + { + ExitSniffL(aConnHandle); + break; + } + case EParkMode: + { + ExitParkL(aConnHandle); + break; + } + // Not possile to prematurely exit hold mode + default: + break; + } + iOutstandingCmd = ETrue; + } + else + { + LOG1(_L("TLowPowModeCmdController::DoExitModeL: cannot exit from mode:%d due to an outstanding command"), aMode); + } + } + +TInt TLowPowModeCmdController::ExitMode(TBTLinkMode aMode, THCIConnHandle aHandle) + { + LOG_FUNC + TRAPD(err, DoExitModeL(aMode, aHandle)); + return err; + } + +void TLowPowModeCmdController::DoChangeModeL(TBTLinkMode aMode, THCIConnHandle aConnHandle) + { + LOG_FUNC + LOG2(_L("TLowPowModeCmdController::DoChangeModeL: mode:%d iOutstandingCmd:%d"), aMode, iOutstandingCmd); + if (!iOutstandingCmd) + { + switch (aMode) + { + case ESniffMode: + { + SniffL(aConnHandle); + break; + } + case EParkMode: + { + ParkL(aConnHandle); + break; + } + case EHoldMode: + { + HoldL(aConnHandle); + break; + } + case EScatterMode: + case EActiveMode: + __ASSERT_DEBUG(0, Panic(EHCICommandBadArgument)); + break; + } + iOutstandingCmd = ETrue; + } + else + { + LOG1(_L("TLowPowModeCmdController::DoChangeModeL: cannot change to mode:%d due to an outstanding command"), aMode); + } + } + +TInt TLowPowModeCmdController::ChangeMode(TBTLinkMode aMode, THCIConnHandle aHandle) + { + LOG_FUNC + TRAPD(err, DoChangeModeL(aMode, aHandle)); + return err; + } + +void TLowPowModeCmdController::SniffL(THCIConnHandle aHandleToRemote) + { + LOG_FUNC + CSniffModeCommand* cmd = CSniffModeCommand::NewL(aHandleToRemote, KBTSniffModeMaxInterval, KBTSniffModeMinInterval, KBTSniffModeAttempt, KBTSniffModeTimeout); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TLowPowModeCmdController::ExitSniffL(THCIConnHandle aHandleToRemote) + { + LOG_FUNC + CExitSniffModeCommand* cmd = CExitSniffModeCommand::NewL(aHandleToRemote); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TLowPowModeCmdController::HoldL(THCIConnHandle aHandle) + { + LOG_FUNC + CHoldModeCommand* cmd = CHoldModeCommand::NewL(aHandle, KBTHoldModeMaxInterval, KBTHoldModeMinInterval); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TLowPowModeCmdController::ParkL(THCIConnHandle aHandle) + { + LOG_FUNC + CParkModeCommand* cmd = CParkModeCommand::NewL(aHandle, KBTParkModeBeaconMaxInterval, KBTParkModeBeaconMinInterval); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TLowPowModeCmdController::ExitParkL(THCIConnHandle aHandle) + { + LOG_FUNC + CExitParkModeCommand* cmd = CExitParkModeCommand::NewL(aHandle); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TLowPowModeCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand) + { + LOG_FUNC + THCIEventCode code = aEvent.EventCode(); + + __ASSERT_DEBUG(!(code != EModeChangeEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent)); // this should never happen + __ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent)); // this should never happen + + if (!aRelatedCommand) // it is not the response to our command - should never happen + return; + + if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK)) + { + LOG3(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode()); + iOutstandingCmd = EFalse; // reset the outstanding flag + iParent.StartArbitrationTimer(); + } + else if (code == EModeChangeEvent) + { + iOutstandingCmd = EFalse; // reset the outstanding flag + + LOG2(_L("TLowPowModeCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode()); + + const TModeChangeEvent& modeChangeEvent = TModeChangeEvent::Cast(aEvent); + TBTLinkMode mode = EActiveMode; + switch(modeChangeEvent.CurrentMode()) + { + // Mode 0, as defined for the Mode Change Event, is Active Mode + case 0: + break; + case 1: + mode = EHoldMode; + break; + case 2: + mode = ESniffMode; + break; + case 3: + mode = EParkMode; + break; + default: + __ASSERT_ALWAYS(EFalse, Panic(EHCICommandBadArgument)); + break; + } + // In the HCI_Facade, in this situation, CPhysicalLinksManager::ModeChanged() is called. + // Since this methods find the CPhysicalLink object (that is iParent) and then call its + // ModeChange method, we can call it directly. + iParent.ModeChange(aEvent.ErrorCode(), modeChangeEvent.ConnectionHandle(), mode, modeChangeEvent.Interval()); + } + } + +void TLowPowModeCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand)) + { + LOG_FUNC + __ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent)); // this should never happen + #ifdef _DEBUG + LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode()); + #endif + iOutstandingCmd = EFalse; // reset the outstanding flag + iParent.StartArbitrationTimer(); + } + + + + +// +// CEncryptionEnforcer +// + +CEncryptionEnforcer::CEncryptionEnforcer (CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode) : + CActive(EPriorityStandard), // Standard priority + iLink(aLink), + iEncryptionMode(aEncryptionMode), + iState(EInactive) + { + LOG_FUNC + } + +CEncryptionEnforcer* CEncryptionEnforcer::NewLC(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode) + { + LOG_STATIC_FUNC + CEncryptionEnforcer* self = new ( ELeave ) CEncryptionEnforcer(aLink, aEncryptionMode); + CleanupStack::PushL (self); + self->ConstructL (); + return self; + } + +CEncryptionEnforcer* CEncryptionEnforcer::NewL(CPhysicalLink& aLink, THCIEncryptModeFlag aEncryptionMode) + { + LOG_STATIC_FUNC + CEncryptionEnforcer* self = CEncryptionEnforcer::NewLC (aLink, aEncryptionMode); + CleanupStack::Pop(self); + return self; + } + +void CEncryptionEnforcer::ConstructL() + { + LOG_FUNC + User::LeaveIfError (iTimer.CreateLocal () ); // Initialize timer + CActiveScheduler::Add (this); // Add to scheduler + } + +CEncryptionEnforcer::~CEncryptionEnforcer() + { + LOG_FUNC + Cancel (); // Cancel any request, if outstanding + iTimer.Close (); // Destroy the RTimer object + } + +void CEncryptionEnforcer::DoCancel () + { + LOG_FUNC + iTimer.Cancel (); + ChangeState(EInactive); + } + +void CEncryptionEnforcer::Start(TInt delay) + { + LOG_FUNC + Cancel (); // Cancel any request, just to be sure + LOG(_L("CEncryptionEnforcer activate timer")) + iTimer.After (iStatus, delay); // Set for later + SetActive (); // Tell scheduler a request is active + } + +void CEncryptionEnforcer::Stop() + { + LOG_FUNC + Cancel(); + } + +void CEncryptionEnforcer::RunL () + { + LOG_FUNC + switch(iState) + { + case ERoleSwitchTimerInProgress: + LOG(_L("CEncryptionEnforcer::RunL() : timer expired after a roleswitch, enforce encryption")) + ChangeState(EForcingInProgress); + User::LeaveIfError(iLink.ChangeEncryption(iEncryptionMode)); + break; + case ENoRoleSwitchTimerInProgress: + LOG(_L("CEncryptionEnforcer::RunL() : timer expired and no roleswitch performed, terminate the link")) + iLink.Terminate(ERemoteUserEndedConnection); + break; + default: + // this should never happen, so we assert in debug so to raise the problem. + // however it doesn't break our behaviour, so we don't care in release. + LOG(_L("CEncryptionEnforcer::RunL() : bad state")) + __ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState)); + break; + } + } + +TInt CEncryptionEnforcer::RunError(TInt IF_FLOGGING(aError)) + { + LOG_FUNC + LOG1(_L("CEncryptionEnforcer::RunError() : error = %d"), aError); + LOG(_L("CEncryptionEnforcer::RunError() : try to terminate the link")); + return iLink.Terminate(ERemoteUserEndedConnection); + } + +void CEncryptionEnforcer::ChangeState(TState aState) + { + LOG_FUNC + LOG2(_L("CEncryptionEnforcer::ChangeState(): old state = %d; new state = %d"), iState, aState); + iState = aState; + } + +void CEncryptionEnforcer::EncryptionEnabled() + { + LOG_FUNC + LOG1(_L("CEncryptionEnforcer::EncryptionEnabled() : current status = %d"), iState); + switch(iState) + { + case EInactive: + // do nothing, it means is the first encryption event as reply to our ChangeEncryption cmd. + break; + case EForcingInProgress: + ChangeState(EInactive); + break; + case ENoRoleSwitchTimerInProgress: + case ERoleSwitchTimerInProgress: + Stop(); + break; + } + } + +void CEncryptionEnforcer::EncryptionDisabled(TBool aSecurityModeFour) + { + LOG_FUNC + LOG1(_L("CEncryptionEnforcer::EncryptionDisabled() : current status = %d"), iState); + + if(aSecurityModeFour) + { + Stop(); + ChangeState(EForcingInProgress); + } + + switch(iState) + { + case EInactive: + LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : start timer")) + ChangeState(ENoRoleSwitchTimerInProgress); + Start(KTimeOutDelay); + break; + case EForcingInProgress: + LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : disconnect the link")) + iLink.Terminate(ERemoteUserEndedConnection); + break; + default: + // this should never happen, so we assert in debug so to raise the problem. + // however it doesn't break our behaviour, so we don't care in release. + LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state")) + __ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState)); + break; + } + } + +void CEncryptionEnforcer::RoleSwitchEvent() + { + LOG_FUNC + + switch(iState) + { + case EInactive: + // This is possible with EPR support - we don't need to do anything in this case. + break; + case ENoRoleSwitchTimerInProgress: + ChangeState(ERoleSwitchTimerInProgress); + break; + default: + // this should never happen, so we assert in debug so to raise the problem. + // however it doesn't break our behaviour, so we don't care in release. + LOG(_L("CEncryptionEnforcer::EncryptionDisabled() : bad state")) + __ASSERT_DEBUG(EFalse, Panic(EEncryptionEnforcerBadState)); + break; + } + } + +void XAutoKeyRefreshToken::Release() + { + LOG_FUNC + iQueLink.Deque(); + delete this; + } + +// +// TRegistryDeviceOutChecker +// +TRegistryDeviceBeingModified::TRegistryDeviceBeingModified() : + iReferenceCount(0) + { + LOG_FUNC + } + +TBool TRegistryDeviceBeingModified::InUse() const + { + LOG_FUNC + return iReferenceCount > 0; + } + +TBool TRegistryDeviceBeingModified::IsEqual(const TBTNamelessDevice& aDevice) const + { + LOG_FUNC + return (iDevice == aDevice); + } + +void TRegistryDeviceBeingModified::Begin(const TBTNamelessDevice& aDevice) + { + LOG_FUNC + iDevice = aDevice; + iReferenceCount++; + } + +void TRegistryDeviceBeingModified::RequestCompleted() + { + LOG_FUNC + iReferenceCount--; + __ASSERT_DEBUG(iReferenceCount >= 0, Panic(ETRegistryDevBeingModUnexpectedCompleted)); + } + +// +// TDisconnectCmdController +// HCI Command Queue Interface for Disconnect Commands +// + +TDisconnectCmdController::TDisconnectCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : + iParent(aLink), + iCmdController(aCmdController) + { + LOG_FUNC + } + +void TDisconnectCmdController::Abort() + { + LOG_FUNC + iCmdController.MhcqRemoveAllCommands(*this); + } + +TInt TDisconnectCmdController::Disconnect(THCIErrorCode aReason) + { + LOG_FUNC + TRAPD(err, DoDisconnectL(aReason)); + return err; + } + +void TDisconnectCmdController::DoDisconnectL(THCIErrorCode aReason) + { + LOG_FUNC + CDisconnectCommand* cmd = CDisconnectCommand::NewL(iParent.Handle(), aReason); + + // Ownership of cmd transfered even if MhcqAddCommandL leaves + iCmdController.MhcqAddCommandL(cmd, *this); + } + +void TDisconnectCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand) + { + LOG_FUNC + THCIEventCode code = aEvent.EventCode(); + + __ASSERT_DEBUG(!(code != EDisconnectionCompleteEvent && code != ECommandStatusEvent), Panic(EDiscCtrlUnexpectedCommandEvent)); // this should never happen + __ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EDiscCtrlUnmatchedCommandEvent)); // this should never happen + + if (!aRelatedCommand) // it is not the response to our command - should never happen + return; + + if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK)) + { + iParent.Disconnection(aEvent.ErrorCode(), iParent.Handle(), aEvent.ErrorCode()); + } + else if (code == EDisconnectionCompleteEvent) + { + const TDisconnectionCompleteEvent& disconnectionCompleteEvent = TDisconnectionCompleteEvent::Cast(aEvent); + // The connection handle in the disconnect complete event should be the same as the physical link's connection handle + __ASSERT_DEBUG(disconnectionCompleteEvent.ConnectionHandle() == iParent.Handle(), Panic(EDiscCtrlUnexpectedConnectionHandle)); + iParent.Disconnection(aEvent.ErrorCode(), disconnectionCompleteEvent.ConnectionHandle(), static_cast(disconnectionCompleteEvent.Reason())); + } + } + +void TDisconnectCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand)) + { + LOG_FUNC + __ASSERT_DEBUG(aCommand, Panic(EDiscCtrlUnmatchedCommandEvent)); // this should never happen + #ifdef _DEBUG + LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode()); + #endif + // Pass this to the physical link as Command Disallowed + iParent.Disconnection(ECommandDisallowed, iParent.Handle(), ECommandDisallowed); + } + + +// +// TAuthenticationCmdController +// + +TAuthenticationCmdController::TAuthenticationCmdController(CPhysicalLink& aLink, MHCICommandQueue& aCmdController) : + iParent(aLink), + iCmdController(aCmdController) + { + LOG_FUNC + } + +void TAuthenticationCmdController::Abort() + { + LOG_FUNC + iCmdController.MhcqRemoveAllCommands(*this); + } + +TInt TAuthenticationCmdController::LinkKeyRequestReply(const TBTDevAddr& aBdaddr, const TDesC8& aLinkKey) const +/** + We have obtained a linkkey, tell HC +**/ + { + CLinkKeyRequestReplyCommand* cmd = NULL; + TBTLinkKey linkkey; + linkkey.Copy(aLinkKey); + + TRAPD(err, cmd = CLinkKeyRequestReplyCommand::NewL(aBdaddr, linkkey)); + if (err == KErrNone) + { + // Ownership of cmd transfered even if MhcqAddCommandL leaves + TRAP(err,iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this))); + } + if(err != KErrNone) + { + iParent.AuthenticationComplete(ELinkKeyRequestPending); + } + return err; + } + +TInt TAuthenticationCmdController::LinkKeyRequestNegativeReply(const TBTDevAddr& aBdaddr) const +/** + We have not obtained a linkkey, tell HC +**/ + { + CLinkKeyRequestReplyNegativeCommand* cmd = NULL; + + TRAPD(err, cmd = CLinkKeyRequestReplyNegativeCommand::NewL(aBdaddr)); + if (err == KErrNone) + { + // Ownership of cmd transfered even if MhcqAddCommandL leaves + TRAP(err, iCmdController.MhcqAddCommandL(cmd, (MHCICommandQueueClient&)(*this))); + } + + if(err != KErrNone) + { + iParent.AuthenticationComplete(ELinkKeyRequestPending); + } + return err; + } + +void TAuthenticationCmdController::MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand) + { + LOG_FUNC + THCIEventCode code = aEvent.EventCode(); + + __ASSERT_DEBUG(!(code != ECommandCompleteEvent && code != ECommandStatusEvent), Panic(EHCIUnknownCommandEvent)); // this should never happen + __ASSERT_DEBUG(aRelatedCommand != NULL, Panic(EHCIUnknownCommandEvent)); // this should never happen + + if (!aRelatedCommand) // it is not the response to our command - should never happen + return; + + if ((code == ECommandStatusEvent) && (aEvent.ErrorCode() != EOK)) + { + LOG3(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x error:0x%x"), code, aRelatedCommand->Opcode(), aEvent.ErrorCode()); + } + else if (code == ECommandCompleteEvent) + { + LOG2(_L("TAuthenticationCmdController::MhcqcCommandEventReceived: event:%d opcode:0x%x"), code, aRelatedCommand->Opcode()); + iParent.AuthenticationComplete(ELinkKeyRequestPending); + } + } + +void TAuthenticationCmdController::MhcqcCommandErrored(TInt __DEBUG_ONLY(aErrorCode), const CHCICommandBase* __DEBUG_ONLY(aCommand)) + { + LOG_FUNC + __ASSERT_DEBUG(aCommand, Panic(EHCIUnknownCommandEvent)); // this should never happen + #ifdef _DEBUG + LOG2(_L("MhcqcCommandErrored: error code:%d opcode:0x%x"), aErrorCode, aCommand->Opcode()); + #endif + iParent.AuthenticationComplete(ELinkKeyRequestPending); + } +