diff -r 4096754ee773 -r 52a167391590 localconnectivityservice/dun/plugins/src/bt/DunBtListen.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/localconnectivityservice/dun/plugins/src/bt/DunBtListen.cpp Wed Sep 01 12:20:40 2010 +0100 @@ -0,0 +1,464 @@ +/* +* Copyright (c) 2006-2008 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: DUN Bluetooth plugin's listener +* +*/ + + +#include +#include +#include +#include +#include "DunPlugin.h" +#include "DunBtListen.h" +#include "DunBtPlugin.h" +#include "DunDebug.h" + +const TInt KListenQueSize = 1; +const TInt KDunFixedChannel = 22; // Hack/kludge for Apple Bug ID 6527598 + +//Service Class Bits supported by DUN +static const TUint16 KCoDDunServiceClass = EMajorServiceTelephony | EMajorServiceNetworking; + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +CDunBtListen* CDunBtListen::NewL( MDunServerCallback* aServer, + MDunListenCallback* aParent, + CDunTransporter* aTransporter, + TBtPortEntity& aEntity ) + { + CDunBtListen* self = new (ELeave) CDunBtListen( aServer, + aParent, + aTransporter, + aEntity ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CDunBtListen::~CDunBtListen() + { + FTRACE(FPrint( _L("CDunBtListen::~CDunBtListen()") )); + ResetData(); + FTRACE(FPrint( _L("CDunBtListen::~CDunBtListen() complete") )); + } + +// --------------------------------------------------------------------------- +// Resets data to initial values +// --------------------------------------------------------------------------- +// +void CDunBtListen::ResetData() + { + // APIs affecting this: + // IssueRequestL() + Stop(); + StopServiceAdvertisement(); + // NewL() + iTransporter->FreeAdvertisementMonitor( KDunBtPluginUid, this ); + delete iDiscovery; + iDiscovery = NULL; + if ( iSockServer.Handle() != KNullHandle ) + { + iSockServer.Close(); + } + // Internal + Initialize(); + } + +// --------------------------------------------------------------------------- +// Registers itself to SDP and BT manager, opens a socket +// and starts to listen it. +// --------------------------------------------------------------------------- +// +void CDunBtListen::IssueRequestL() + { + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL()" ) )); + + if ( iListenState == EBtListenStateListening ) + { + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL() (already active) complete" ) )); + User::Leave( KErrNotReady ); + } + + TBool advertise = iTransporter->AdvertisementStatus(); + if ( !advertise ) + { + // Return silently here as CDunTransporter will notify later + return; + } + + TBool inUse = EFalse; + TInt numOfChans = 0; + TInt retTemp = StartServiceAdvertisement( inUse ); + if ( retTemp != KErrNone ) + { + if ( inUse ) + { + numOfChans = iTransporter->GetNumberOfAllocatedChannelsByUid( + KDunBtPluginUid ); + if ( numOfChans == 0) + { + // No channels so parent can't reissue requests of this object + // This is fatal case -> leave. + // NOTE: To add full support for this case a poller (timer) is + // needed that polls for free RFCOMM channel by given interval. + User::Leave( retTemp ); + } + // If in use and parent has channels then just fail silently. + // Let this object to wait until parent finds new resources. + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL() complete" ) )); + return; + } + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL() (failed!) complete" ) )); + User::Leave( retTemp ); + } + + // Not already active here so start listening + // First open blank data socket + retTemp = iEntity.iBTPort.Open( iSockServer ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL() (ERROR) complete (%d)" ), retTemp)); + User::Leave( retTemp ); + } + iStatus = KRequestPending; + iListenSocket.Accept( iEntity.iBTPort, iStatus ); + SetActive(); + iListenState = EBtListenStateListening; + + FTRACE(FPrint( _L( "CDunBtListen::IssueRequestL() complete") )); + } + +// --------------------------------------------------------------------------- +// Stops listening +// --------------------------------------------------------------------------- +// +TInt CDunBtListen::Stop() + { + FTRACE(FPrint( _L( "CDunBtListen::Stop()") )); + if ( iListenState != EBtListenStateListening ) + { + FTRACE(FPrint( _L("CDunBtListen::Stop() (not ready) complete" ))); + return KErrNotReady; + } + iListenSocket.CancelAccept(); + Cancel(); + iListenState = EBtListenStateIdle; + FTRACE(FPrint( _L( "CDunBtListen::Stop() complete") )); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// CDunBtListen::CDunBtListen +// --------------------------------------------------------------------------- +// +CDunBtListen::CDunBtListen( MDunServerCallback* aServer, + MDunListenCallback* aParent, + CDunTransporter* aTransporter, + TBtPortEntity& aEntity ) : + CActive( EPriorityStandard ), + iServer( aServer ), + iParent( aParent ), + iTransporter( aTransporter ), + iEntity( aEntity ) + { + Initialize(); + } + +// --------------------------------------------------------------------------- +// CDunBtListen::ConstructL +// --------------------------------------------------------------------------- +// +void CDunBtListen::ConstructL() + { + FTRACE(FPrint(_L("CDunBtListen::ConstructL()"))); + if ( !iServer || !iParent || !iTransporter ) + { + User::Leave( KErrGeneral ); + } + + CBTEngDiscovery* discovery = CBTEngDiscovery::NewLC(); + FTRACE(FPrint(_L("CDunBtListen::ConstructL: iSockServer.Connect"))); + User::LeaveIfError( iSockServer.Connect() ); + + // Set advertisement monitor + iTransporter->SetAdvertisementMonitorL( KDunBtPluginUid, this ); + + // Then we are ready to start listening and accepting incoming connection + // requests. + CleanupStack::Pop( discovery ); + iDiscovery = discovery; + CActiveScheduler::Add( this ); + FTRACE(FPrint(_L("CDunBtListen::ConstructL() complete"))); + } + +// --------------------------------------------------------------------------- +// Initializes this class +// --------------------------------------------------------------------------- +// +void CDunBtListen::Initialize() + { + // Don't initialize iServer here (it is set through NewL) + // Don't initialize iParent here (it is set through NewL) + // Don't initialize iTransporter here (it is set through NewL) + // Don't initialize iEntity here (it is set through NewL) + iListenState = EBtListenStateIdle; + iDiscovery = NULL; + iChannelNum = 0; + iSDPHandleDun = 0; + } + +// --------------------------------------------------------------------------- +// Starts dialup service advertisement +// --------------------------------------------------------------------------- +// +TInt CDunBtListen::StartServiceAdvertisement( TBool& aInUse ) + { + FTRACE(FPrint( _L( "CDunBtListen::StartServiceAdvertisement()" ) )); + + TInt retTemp = ReserveLocalChannel( iSockServer, + iListenSocket, + iChannelNum, + aInUse ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunBtListen::StartServiceAdvertisement() (ERROR) complete" ) )); + return retTemp; + } + + // Now RFCOMM channel number of the next data socket must be the same as + // the current listener's RFCOMM channel number. Set that now. + iEntity.iChannelNum = iChannelNum; + + // Register SDP record + iSDPHandleDun = 0; + retTemp = iDiscovery->RegisterSdpRecord( KDialUpNetworkingUUID, + iChannelNum, + iSDPHandleDun ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunBtListen::StartServiceAdvertisement() (failed!) complete (%d)" ), retTemp)); + return retTemp; + } + FTRACE(FPrint( _L( "CDunBtListen::StartServiceAdvertisement() complete" ) )); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Stops dialup service advertisement +// --------------------------------------------------------------------------- +// +TInt CDunBtListen::StopServiceAdvertisement() + { + FTRACE(FPrint( _L( "CDunBtListen::StopServiceAdvertisement()" ) )); + if ( !iDiscovery ) + { + FTRACE(FPrint( _L( "CDunBtListen::StopServiceAdvertisement() (iDiscovery) not initialized!" ) )); + return KErrGeneral; + } + if ( iSDPHandleDun != 0 ) + { + TInt retTemp = iDiscovery->DeleteSdpRecord( iSDPHandleDun ); + FTRACE(FPrint( _L( "CDunBtListen::StopServiceAdvertisement() record closed (%d)" ), retTemp )); + iSDPHandleDun = 0; + } + if ( iListenSocket.SubSessionHandle() ) + { + iListenSocket.Close(); + } + FTRACE(FPrint( _L( "CDunBtListen::StopServiceAdvertisement() complete" ) )); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Method which reserves local RFCOMM channel (from possible channels 1-30) +// and returns it to client. +// --------------------------------------------------------------------------- +// +TInt CDunBtListen::ReserveLocalChannel( RSocketServ& aSocketServ, + RSocket& aListenSocket, + TUint& aChannelNum, + TBool& aInUse ) + { + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel()"))); + aInUse = EFalse; + if ( aListenSocket.SubSessionHandle() ) + { + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() (open socket!) complete"))); + return KErrArgument; + } + TInt retTemp; + TProtocolDesc pInfo; + retTemp = aSocketServ.FindProtocol( TProtocolName(KRFCOMMDesC), pInfo ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() (FindProtocol failed) complete (%d)"), retTemp)); + return retTemp; + } + retTemp = aListenSocket.Open( aSocketServ, + pInfo.iAddrFamily, + pInfo.iSockType, + pInfo.iProtocol ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() (Open failed) complete (%d)"), retTemp)); + return retTemp; + } + TRfcommSockAddr addr; + TBTServiceSecurity sec; + sec.SetAuthentication( ETrue ); + sec.SetAuthorisation( ETrue ); + sec.SetEncryption( ETrue ); + sec.SetPasskeyMinLength( 0 ); + addr.SetSecurity( sec ); + addr.SetPort( KRfcommPassiveAutoBind ); + // When fix from Apple, replace the following with + // "retTemp = aListenSocket.Bind( addr );" + retTemp = DoExtendedBind( aListenSocket, addr ); + if ( retTemp != KErrNone ) + { + aListenSocket.Close(); + aInUse = ETrue; + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() Bind() complete (%d)"), retTemp)); + return KErrInUse; + } + aChannelNum = aListenSocket.LocalPort(); + + // We try to set the Telephony and Networking bits in our service class. If this fails we + // ignore it, as it's better to carry on without it than to fail to start listening. + aListenSocket.SetOpt(KBTRegisterCodService, KSolBtRFCOMM, KCoDDunServiceClass); + + retTemp = aListenSocket.Listen( KListenQueSize ); + if ( retTemp != KErrNone ) + { + aListenSocket.Close(); + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() Listen() complete (%d)"), retTemp)); + return retTemp; + } + FTRACE(FPrint(_L("CDunBtListen::ReserveLocalChannel() complete (%d)"), aChannelNum)); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// Tries to bind to a fixed port and if that fails with KRfcommPassiveAutoBind. +// This is for spec breaking solutions like the OSX Leopard. +// --------------------------------------------------------------------------- +// +TInt CDunBtListen::DoExtendedBind( RSocket& aListenSocket, + TRfcommSockAddr& aSockAddr ) + { + FTRACE(FPrint(_L("CDunBtListen::DoExtendedBind()"))); + if ( !aListenSocket.SubSessionHandle() ) + { + FTRACE(FPrint(_L("CDunBtListen::DoExtendedBind() (closed socket!) complete"))); + return KErrGeneral; + } + TRfcommSockAddr fixedAddr = aSockAddr; + fixedAddr.SetPort( KDunFixedChannel ); + TInt retTemp = aListenSocket.Bind( fixedAddr ); + if ( retTemp == KErrNone ) + { + FTRACE(FPrint(_L("CDunBtListen::DoExtendedBind() complete"))); + return KErrNone; + } + TInt retVal = aListenSocket.Bind( aSockAddr ); + FTRACE(FPrint(_L("CDunBtListen::DoExtendedBind() complete"))); + return retVal; + } + +// --------------------------------------------------------------------------- +// From class CActive. +// Called when a service is requested via BT. +// --------------------------------------------------------------------------- +// +void CDunBtListen::RunL() + { + FTRACE(FPrint( _L( "CDunBtListen::RunL()" ) )); + iListenState = EBtListenStateIdle; + + StopServiceAdvertisement(); + + TInt retTemp = iStatus.Int(); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunBtListen::RunL() (ERROR) complete (%d)" ), retTemp)); + iServer->NotifyPluginCloseRequest( KDunBtPluginUid, ETrue ); + return; + } + // Notify new connection + TBool noFreeChans = EFalse; + retTemp = iParent->NotifyChannelAllocate( noFreeChans ); + if ( retTemp != KErrNone ) + { + FTRACE(FPrint( _L( "CDunBtListen::RunL() channel allocation failed! (%d)" ), retTemp)); + // Other error than no free channels, close plugin now + if ( !noFreeChans ) + { + iServer->NotifyPluginCloseRequest( KDunBtPluginUid, ETrue ); + } + return; + } + + // Don't restart listening here. Request is issued via + // NotifyAdvertisementStart() + + FTRACE(FPrint( _L( "CDunBtListen::RunL() complete" ) )); + } + +// --------------------------------------------------------------------------- +// From class CActive. +// Cancel current activity. +// --------------------------------------------------------------------------- +// +void CDunBtListen::DoCancel() + { + } + +// --------------------------------------------------------------------------- +// From class MDunServAdvMon. +// Gets called when advertisement status changes to start. +// --------------------------------------------------------------------------- +// +void CDunBtListen::NotifyAdvertisementStart( TBool aCreation ) + { + FTRACE(FPrint( _L( "CDunBtListen::NotifyAdvertisementStart()" ) )); + // Remove the "if" below when fix comes from Apple + if ( !aCreation ) + { + TRAP_IGNORE( IssueRequestL() ); + } + FTRACE(FPrint( _L( "CDunBtListen::NotifyAdvertisementStart() complete" ) )); + } + +// --------------------------------------------------------------------------- +// From class MDunServAdvMon. +// Gets called when advertisement status changes to end. +// --------------------------------------------------------------------------- +// +void CDunBtListen::NotifyAdvertisementEnd() + { + FTRACE(FPrint( _L( "CDunBtListen::NotifyAdvertisementEnd()" ) )); + Stop(); + StopServiceAdvertisement(); + FTRACE(FPrint( _L( "CDunBtListen::NotifyAdvertisementEnd() complete" ) )); + }