/*
* 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;
iListenState = EBtListenStateListening;
iListenSocket.Accept( iEntity.iBTPort, iStatus );
SetActive();
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" ) ));
}