bluetoothengine/btnotif/btnotifsrv/src/btnotifconnectiontracker.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 14 May 2010 16:01:46 +0300
changeset 19 43824b19ee35
child 31 a0ea99b6fa53
permissions -rw-r--r--
Revision: 201017 Kit: 201019

/*
* ============================================================================
*  Name        : btnotifconnectiontracker.cpp
*  Part of     : bluetoothengine / btnotif
*  Description : Bluetooth connection tracker and manager.
*
*  Copyright © 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:
*  Nokia Corporation
* ============================================================================
* Template version: 4.1
*/

#include "btnotifconnectiontracker.h"
#include <btextnotifiers.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <btextnotifierspartner.h>
#endif

#include "btnotifconnection.h"
#include "btnotifsession.h"
#include "btnotifclientserver.h"
#include "bluetoothtrace.h"

/**  Id for the link key watcher active object. */
const TInt KLinkCountWatcher = 30;
/**  Id for the pairing result watcher active object. */
const TInt KSspResultWatcher = 31;
/**  Id for the registry watcher active object (TEMP!). */
const TInt KRegistryWatcher = 41;
/**  Time window for determining if there are too many requests. */
#ifndef __WINS__
#define KDENYTHRESHOLD TTimeIntervalSeconds(3)
#else   //__WINS__
#define KDENYTHRESHOLD TTimeIntervalSeconds(5)
#endif  //__WINS__


// ======== LOCAL FUNCTIONS ========

// ---------------------------------------------------------------------------
// Checks if the notifier is one launched by the security manager of the
// protocol stack. These notifiers need to be served unless really not possible.
// ---------------------------------------------------------------------------
//
TBool IsStackSecmanNotifier( TInt aUid )
    {
    TBool result = EFalse;
    if( aUid == KBTManAuthNotifierUid.iUid || aUid == KBTManPinNotifierUid.iUid ||
        aUid == KBTPinCodeEntryNotifierUid.iUid || aUid == KBTNumericComparisonNotifierUid.iUid ||
        aUid == KBTPasskeyDisplayNotifierUid.iUid )
        {
        result = ETrue;
        }
    return result;
    }


// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CBTNotifConnectionTracker::CBTNotifConnectionTracker( CBTNotifServer* aServer )
:   iServer( aServer )
    {
    }


// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::ConstructL()
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    // Start watching the number of baseband links.
    TInt err = iLinkCount.Attach( KPropertyUidBluetoothCategory,
                KPropertyKeyBluetoothGetPHYCount );
    // There is not much point to continue if we can't attach to
    // the link count key.
    User::LeaveIfError( err );
    iLinkCountActive = CBtSimpleActive::NewL( *this, KLinkCountWatcher );
    iLinkCount.Subscribe( iLinkCountActive->RequestStatus() );
    iLinkCountActive->GoActive();
    // Open a handle to the registry server
    User::LeaveIfError( iBTRegistrySession.Connect() );
    // Open a handle to the socket server
    User::LeaveIfError( iSockServ.Connect() );
    iPairingServ = new( ELeave ) RBluetoothPairingServer();
    if( iPairingServ->Connect() )
        {
        // Delete in case of error - there is no good other way to keep track.
        delete iPairingServ;
        iPairingServ = NULL;
        }
    else
        {
        iSspResultActive = CBtSimpleActive::NewL( *this, KSspResultWatcher );
        User::LeaveIfError( iSspResultSession.Open( *iPairingServ ) );
        iSspResultSession.SimplePairingResult( iSspResultAddr, iSspResultActive->RequestStatus() );
        iSspResultActive->GoActive();
        }
    iConnMan = CBTEngConnMan::NewL( this );
    iPhyLinks = CBluetoothPhysicalLinks::NewL( *this, iSockServ );
// ToDo: remove this when registry notifications API is available!!
    err = iRegistryChange.Attach( KPropertyUidBluetoothCategory, KPropertyKeyBluetoothRegistryTableChange );
    User::LeaveIfError( err );
    iRegistryActive = CBtSimpleActive::NewL( *this, KRegistryWatcher );
    iRegistryChange.Subscribe( iRegistryActive->RequestStatus() );
    iRegistryActive->GoActive();
// End ToDo
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }


// ---------------------------------------------------------------------------
// NewL.
// ---------------------------------------------------------------------------
//
CBTNotifConnectionTracker* CBTNotifConnectionTracker::NewL( CBTNotifServer* aServer )
    {
    CBTNotifConnectionTracker* self = new( ELeave ) CBTNotifConnectionTracker( aServer );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTNotifConnectionTracker::~CBTNotifConnectionTracker()
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    iConnArray.ResetAndDestroy();
    iConnArray.Close();
    iDeniedRequests.Close();
    delete iLinkCountActive;
    iLinkCount.Close();
    
    delete iConnMan;
    delete iPhyLinks;
    iSockServ.Close();
    delete iSspResultActive;
    iSspResultSession.Close();
    if( iPairingServ )
        {
        iPairingServ->Close();
        delete iPairingServ;
        }
    delete iRegistryActive;
    iRegistryChange.Close();
    iBTRegistrySession.Close();
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }

// ---------------------------------------------------------------------------
// Process a client message related to notifiers.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::DispatchNotifierMessageL( const RMessage2& aMessage )
    {
    BOstraceFunctionEntryExt ( DUMMY_LIST, this, aMessage.Function() );
    TInt opcode = aMessage.Function();
    TInt uid = aMessage.Int0();
    const RMessage2* message = &aMessage;
    // Use a pointer to the original message, so that we don't duplicate it.
    // Then we avoid any bookkeeping for keeping them in sync.
    if( opcode == EBTNotifCancelNotifier )
        {
        // We only accept a cancel message from the same session as the original
        // request (this is enforced by the RNotifier backend). So we use the
        // session of the cancel request (if this would change, the same way as
        // for updates can be followed).
        // We need to find the original request to identify the handler of the 
        // connection; the uid points to the original request.
        message = ( (CBTNotifSession *) aMessage.Session() )->FindMessageFromUid( uid );
        }
    else if( opcode == EBTNotifUpdateNotifier )
        {
        // We accept a update messages from any client, although in practice,
        // they will all come from the same session (through RNotifier).
        // We need to find the original request to identify the handler of the 
        // connection (the uid points to the original request). Through the 
        // server, we get it from any session.
        message = iServer->FindMessageFromUid( uid );
        }
    if( !message )
        {
        // It's hard to continue if we don't know where to route the message.
        User::Leave( KErrDisconnected );
        }
    TBuf8<0x250> paramsBuf;    // Size needs to be long enough to read all possible parameter sizes.
    CBTNotifConnection* connection = FindConnectionFromMessageL( opcode, *message, paramsBuf );
    if( !connection )
        {
        User::Leave( KErrDisconnected );
        }
    switch( opcode )
        {
        case EBTNotifStartSyncNotifier:
        case EBTNotifStartAsyncNotifier:
            connection->HandleNotifierRequestL( paramsBuf, aMessage );
            break;
        case EBTNotifUpdateNotifier:
            connection->HandleNotifierUpdateL( paramsBuf, aMessage );
            break;
        case EBTNotifCancelNotifier:
            // Complete the cancel message already here, so that the caller can
            // continue, and the next operation can close sessions with the caller.
            aMessage.Complete( KErrNone );
            connection->CancelNotifierRequestL( *message );
            break;
        default:
            break;
        }
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }


// ---------------------------------------------------------------------------
// Handle a request related to pairing.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleBondingRequestL( const RMessage2& aMessage )
    {
    BOstraceFunctionEntryExt ( DUMMY_LIST, this, aMessage.Function() );
    // Bonding is an infrequently occurring operation, so we don't waste memory
    // to keep a copy of the parameters. Instead we read them again when needed.
    TPckgBuf<TBTDevAddr> addrBuf;
    TInt opcode = aMessage.Function();
    if( opcode == EBTEngPairDevice )
        {
        aMessage.ReadL( EBTNotifSrvParamSlot, addrBuf );
        }
    else if( opcode == EBTEngCancelPairDevice )
        {
        const RMessage2* message =
                ( (CBTNotifSession *) aMessage.Session() )->FindMessageFromUid( EBTEngPairDevice );
        message->ReadL( EBTNotifSrvParamSlot, addrBuf );
        }
    BtTraceBtAddr1( TRACE_DEBUG, DUMMY_LIST, "CBTNotifConnectionTracker::HandleBondingRequestL() addr=", addrBuf() );
	TInt err = KErrNotFound;
	CBTNotifConnection* connection = FindConnectionHandler( addrBuf() );
    if( opcode == EBTEngPairDevice )
        {
        if( !connection )
            {
            // Create a connection first, then tell it to bond.
            err = iPhyLinks->CreateConnection( addrBuf() );
            connection = CBTNotifConnection::NewLC( addrBuf(), this );
            iConnArray.AppendL( connection );
            CleanupStack::Pop( connection );
            }
        else
            {
            // There is an existing connection. Care must be taken, the connection
            // _should_ be disconnect first if this device is already paired, so that
            // we are sure that we don't mix up the state of the connection.
            RBTPhysicalLinkAdapter link;
            err = link.Open( iSockServ, addrBuf() );
            TUint32 linkState = 0;
            if( !err )
                {
                err = link.PhysicalLinkState( linkState );
                }
            if( !err && linkState & ( ENotifyAuthenticationComplete | ENotifyEncryptionChangeOn ) )
                {
                // For now, we just reject the request.
                err = KErrAlreadyExists;
                }
            link.Close();
            }
        if( !err )
            {
            // Start bonding immediately so that the connection object is in the right state.
            connection->StartBondingL( aMessage );
            }
        }
    else if( opcode == EBTEngCancelPairDevice && connection )
        {
        connection->CancelBondingL();
        err = KErrNone;
        aMessage.Complete( err );
        }
	// KErrNotFound is returned for a request to cancel pairing that has no connection.
    if( err )
        {
        aMessage.Complete( err );
        }
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }


// ---------------------------------------------------------------------------
// Handle a change in the number of connections.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleLinkCountChangeL()
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    TInt linkCount = 0;
    User::LeaveIfError( iLinkCount.Get( linkCount ) );
    if( linkCount )
        {
        RBTDevAddrArray links;
        CleanupClosePushL( links );
        User::LeaveIfError( iPhyLinks->Enumerate( links, 10 ) );
        __ASSERT_ALWAYS( links.Count(), PanicServer( EBTNotifPanicBadState ) );
        for( TInt i = iConnArray.Count() -1; i >= 0 ; i-- )
            {
            // Loop backwards, as we may remove entries from the array.

            // First check the existing connections, and 
            // remove disconnected links
            TBTDevAddr addr = iConnArray[i]->Address();
            TInt pos = links.Find( addr );
            if( pos > KErrNotFound )
                {
                // The link we know is still connected,
                // remove the watceher from the array.
                links.Remove( pos );
                // ToDo: see comment below!
                }
            else if( iConnArray[i]->CurrentOperation() == CBTNotifConnection::EIdle )
                {
                // This link is no more connected and idle, remove.
                CBTNotifConnection* connection = iConnArray[i];
                iConnArray.Remove( i ); // Does not delete the object.
                delete connection;
                }
            // else we wait for the link to complete its operations.
            }
        // Now we have an array with only the new connections.
        // Add new watchers.
        for( TInt i = 0; i < links.Count(); i++ )
            {
            CBTNotifConnection* connection = CBTNotifConnection::NewLC( links[i], this );
            iConnArray.AppendL( connection );
            CleanupStack::Pop( connection );
            }
        // Close the links RBTDevAddrArray, needed before going out of scope.
        CleanupStack::PopAndDestroy();
        }
    else
        {
        for( TInt i = iConnArray.Count() -1; i >= 0 ; i-- )
            {
            if( iConnArray[i]->CurrentOperation() == CBTNotifConnection::EIdle )
                {
                // This link is now idle, so we can remove it safely.
                CBTNotifConnection* connection = iConnArray[i];
                iConnArray.Remove( i ); // Does not delete the object.
                delete connection;
                }
            }
        if( !iConnArray.Count() )
            {
            // The array is idle, clean up the array resources.
            iConnArray.Reset();
            }
        }
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }


// ---------------------------------------------------------------------------
// Check if this device has been denied a connection already before.
// Also check if a previous connection attempt has just been rejected.
// ---------------------------------------------------------------------------
//
TBool CBTNotifConnectionTracker::UpdateBlockingHistoryL( const CBTDevice* aDevice, 
    TBool aAccepted )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    __ASSERT_ALWAYS( aDevice, PanicServer( EBTNotifPanicBadArgument ) );
    // Check the time since the previous event.
    TBool result = RecordConnectionAttempts( aAccepted );
    TInt pos = iDeniedRequests.Find( aDevice->BDAddr() );
    if( !aAccepted )
        {
        if( pos == KErrNotFound )
            {
            // The user denied the request from a new device, record the device address.
            if( aDevice->IsValidPaired() && aDevice->IsPaired() )
                //[MCL]: && iDevice->LinkKeyType() != ELinkKeyUnauthenticatedUpgradable )
                {
                // Paired devices are allowed one time rejection without a prompt for blocking.
                result = EFalse;
                }
            iDeniedRequests.AppendL( aDevice->BDAddr() );
            }
        // Nothing needed here if the address is already in the array.
        }
    else if( pos > KErrNotFound )
        {
        // The user accepted a request, and it was from a device he/she 
        // previously rejected. Clear the history for this device from the array.
        iDeniedRequests.Remove( pos );
        }
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    return result;
    }


// ---------------------------------------------------------------------------
// From class MBluetoothPhysicalLinksNotifier.
// Handle baseband connection completion.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleCreateConnectionCompleteL( TInt aErr )
    {
    BOstraceFunctionEntryExt ( DUMMY_LIST, this, aErr );
	// We only connect links for starting outgoing bonding.
	const RMessage2* message = iServer->FindMessageFromUid( (TInt) EBTEngPairDevice );
	if( message )
		{
        TPckgBuf<TBTDevAddr> addrBuf;
        message->ReadL( EBTNotifSrvParamSlot, addrBuf );
        CBTNotifConnection* connection = FindConnectionHandler( addrBuf() );
        __ASSERT_ALWAYS( connection, PanicServer( EBTNotifPanicBadState ) );
        if( !aErr && connection->CurrentOperation() == CBTNotifConnection::EIdle )
            {
			TRAP( aErr, connection->StartBondingL( *message ) );
			}
        if( aErr && connection->CurrentOperation() == CBTNotifConnection::EBonding )
            {
            connection->PairingResult( aErr );  // Launch error note
            }
		}
	BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }


// ---------------------------------------------------------------------------
// From class MBluetoothPhysicalLinksNotifier.
// Handle baseband disconnection.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleDisconnectCompleteL( TInt aErr )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
	// We only disconnect links for starting outgoing bonding.
	const RMessage2* message = iServer->FindMessageFromUid( (TInt) EBTEngPairDevice );
	if( message )
		{
        TPckgBuf<TBTDevAddr> addrBuf;
        message->ReadL( EBTNotifSrvParamSlot, addrBuf );
		if( !aErr )
			{
			aErr = iPhyLinks->CreateConnection( addrBuf() );
			}
		if( aErr )
			{
			iServer->CompleteMessage( message->Handle(), aErr, KNullDesC8 );
            CBTNotifConnection* connection = FindConnectionHandler( addrBuf() );
            __ASSERT_ALWAYS( connection, PanicServer( EBTNotifPanicBadState ) );
            connection->PairingResult( aErr );  // Launch error note
			}
    	}
	BOstraceFunctionExit0( DUMMY_DEVLIST );
    }


// ---------------------------------------------------------------------------
// From class MBluetoothPhysicalLinksNotifier.
// Handle disconnection of all links.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleDisconnectAllCompleteL( TInt aErr )
    {
    (void) aErr;
    }


// ---------------------------------------------------------------------------
// From class MBTEngConnObserver.
// Handle service-level connection completion.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::ConnectComplete( TBTDevAddr& aAddr, 
    TInt aErr, RBTDevAddrArray* aConflicts )
    {
    (void) aAddr;
    (void) aErr;
    (void) aConflicts;
    }


// ---------------------------------------------------------------------------
// From class MBTEngConnObserver.
// Handle service-level disconnection.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::DisconnectComplete( TBTDevAddr& aAddr, TInt aErr )
    {
    (void) aAddr;
    (void) aErr;
    }


// ---------------------------------------------------------------------------
// From class MBtSimpleActiveObserver.
// Handle the active object completion.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::RequestCompletedL( CBtSimpleActive* aActive,
    TInt aStatus )
    {
    BOstraceFunctionEntryExt ( DUMMY_LIST, this, aActive->RequestId() );
    BOstraceExt2( TRACE_DEBUG, DUMMY_DEVLIST, 
            "CBTNotifConnectionTracker::MBAORequestCompletedL() requestid=%d status=%d", 
            aActive->RequestId(), aStatus);
    if( aActive->RequestId() == KLinkCountWatcher )
        {
        iLinkCount.Subscribe( aActive->RequestStatus() );
        aActive->GoActive();
        if( !aStatus )
            {
            // HandleLinkCountChangeL();
            }
        }
// ToDo: remove this when registry notifications API is available!!
    else if( aActive->RequestId() == KRegistryWatcher )
        {
        // BTRegistry notifies of a change. Check which one.
        iRegistryChange.Subscribe( aActive->RequestStatus() );
        aActive->GoActive();
        TInt tableChanged = 0;
        if( !aStatus && !iRegistryChange.Get( tableChanged ) &&
            tableChanged == KRegistryChangeRemoteTable )
            {
            // A record for a remote device has changed. Tell all 
            // connections to update their record.
            for( TInt i = 0; i < iConnArray.Count(); i++ )
                {
                // Reuse the functionality in the connection
                if( iConnArray[i]->CurrentOperation() < CBTNotifConnection::EReadingRegistry )
                    {
                    iConnArray[i]->RequestCompletedL( aActive, aStatus );
                    }
                }
            }
        }
// End ToDo
    else if( aActive->RequestId() == KSspResultWatcher )
        {
        iSspResultSession.SimplePairingResult( iSspResultAddr, iSspResultActive->RequestStatus() );
        iSspResultActive->GoActive();
        CBTNotifConnection* connection = FindConnectionHandler( iSspResultAddr );
        // ToDo: how to handle a result of a link that already disconnected? 
        if( connection )
            {
            connection->PairingResult( aStatus );
            }
        }
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }


// ---------------------------------------------------------------------------
// From class MBtSimpleActiveObserver.
// Cancel and clean up all requests related to the active object.
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::CancelRequest( TInt aRequestId )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    if( aRequestId == KLinkCountWatcher )
        {
        iLinkCount.Cancel();
        }
    else if( aRequestId == KSspResultWatcher )
        {
        iSspResultSession.CancelSimplePairingResult();
        }
    else if ( aRequestId == KRegistryWatcher )
        {
        iRegistryChange.Cancel();
        }
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }

// ---------------------------------------------------------------------------
// From class MBtSimpleActiveObserver.
// 
// ---------------------------------------------------------------------------
//
void CBTNotifConnectionTracker::HandleError( CBtSimpleActive* aActive, 
        TInt aError )
    {
    (void) aActive;
    (void) aError;
    }

// ---------------------------------------------------------------------------
// Parse the details from a client message and find the associated handler.
// ---------------------------------------------------------------------------
//
CBTNotifConnection* CBTNotifConnectionTracker::FindConnectionFromMessageL(
    TInt aOpcode, const RMessage2& aMessage, TDes8& aBuffer )
    {
    BOstraceFunctionEntry1( DUMMY_DEVLIST, this );
    TInt uid = aMessage.Int0();
    aMessage.ReadL( EBTNotifSrvParamSlot, aBuffer );
    TBTDevAddr addr = ParseAddressL( uid, aBuffer );
    // If this is a 
    CBTNotifConnection* connection = FindConnectionHandler( addr );
    if( !connection && IsStackSecmanNotifier( uid ) &&
        ( aOpcode == EBTNotifStartAsyncNotifier || aOpcode == EBTNotifStartSyncNotifier ) )
        {
        // A notifier from stack. This happens if e.g. the pairing
        // request comes in before the link count changes (like security
        // mode 3). Create the handler and queue the request.
        // And note that 
        connection = CBTNotifConnection::NewLC( addr, this );
        iConnArray.AppendL( connection );
        CleanupStack::Pop( connection );
        }
    BOstraceFunctionExitExt( DUMMY_DEVLIST, this, connection );
    return connection;
    }


// ---------------------------------------------------------------------------
// read the address from a client message.
// ---------------------------------------------------------------------------
//
TBTDevAddr CBTNotifConnectionTracker::ParseAddressL( TInt aUid,
    const TDesC8& aParamsBuf ) const
    {
    BOstraceFunctionEntry1( DUMMY_DEVLIST, this );
    TBTDevAddr addr;
    if( IsStackSecmanNotifier( aUid ) )
        {
        // For all these, the address is the first data member,
        // so can be read using the TBTNotifierParams data structure.
        TBTNotifierParams params;
        TPckgC<TBTNotifierParams> paramsPckg( params );
        paramsPckg.Set( aParamsBuf );
        addr = paramsPckg().iBDAddr;
        }
    //else if(  ) other notifier types
    BOstraceFunctionExitExt( DUMMY_DEVLIST, this, &addr );  
    return addr;
    }


// ---------------------------------------------------------------------------
// Find a specific connection.
// ---------------------------------------------------------------------------
//
CBTNotifConnection* CBTNotifConnectionTracker::FindConnectionHandler(
    const TBTDevAddr& aAddr ) const
    {
    BOstraceFunctionEntry1( DUMMY_DEVLIST, this );
    CBTNotifConnection* conn = NULL;
    if( aAddr != TBTDevAddr() )
        {
        // This may be replaced by RArray::Find with appropriate key
        for( TInt i = 0; i < iConnArray.Count(); i++ )
            {
            if( iConnArray[i]->Address() == aAddr )
                {
                conn = iConnArray[i];
                break;
                }
            }
        }
    BOstraceFunctionExitExt( DUMMY_DEVLIST, this, conn );
    return conn;
    }


// ---------------------------------------------------------------------------
// Record and check the time between connection attempts.
// ---------------------------------------------------------------------------
//
TBool CBTNotifConnectionTracker::RecordConnectionAttempts( TBool aAccepted )
    {
    BOstraceFunctionEntry1( DUMMY_DEVLIST, this );
    TBool result = ETrue;
    TTime now( 0 );
    if( !aAccepted )
        {
        now.UniversalTime();
        if( iLastReject )
            {
            // Check the time between denied connections, that it does not go too fast.
            TTimeIntervalSeconds prev( 0 );
            if( !now.SecondsFrom( TTime( iLastReject ), prev ) )
                {
                if( prev <= KDENYTHRESHOLD )
                    {
                    // We are getting the requests too fast. Present the user with
                    // an option to turn BT off.
                    //iServer->SettingsTracker()->SetPower( EFalse );
                    result = EFalse;
                    }
                }
            }
        }
    // Record the current timestamp.
    // It is reset in case the user accepted the request.
    iLastReject = now.Int64();
    BOstraceFunctionExitExt( DUMMY_DEVLIST, this, result );
    return result;
    }