localconnectivityservice/dun/plugins/src/bt/DunBtListen.cpp
branchRCL_3
changeset 19 0aa8cc770c8a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/localconnectivityservice/dun/plugins/src/bt/DunBtListen.cpp	Tue Aug 31 16:03:15 2010 +0300
@@ -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 <btsdp.h>
+#include <e32std.h>
+#include <bt_sock.h>
+#include <btengdiscovery.h>
+#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" ) ));
+    }