htiui/HtiCommPlugins/HtiBtCommPlugin/BtEngine/src/BtSerialClient.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 00:17:27 +0200
changeset 0 d6fe6244b863
child 3 2703485a934c
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* Copyright (c) 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:  Bluetooth serial client.
*
*/


// INCLUDE FILES
#include <e32std.h>
#include <btengsettings.h>  // CBTEngSettings

#include "BtSerialClient.h"
#include "MessageServiceSearcher.h"
#include "BtSerialEngine.pan"
#include "HtiBtEngineLogging.h"
#include "socketswriter.h"
#include "socketsreader.h"

const TInt KMaxBtStartWaitLoop   = 5;
const TInt KBtStateQueryInterval = 1000000; // microseconds


// ----------------------------------------------------------------------------
EXPORT_C CBtSerialClient* CBtSerialClient::NewL(MBtSerialClientObserver& aObserver)
    {
    CBtSerialClient* self = new (ELeave) CBtSerialClient(aObserver);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

// ----------------------------------------------------------------------------
CBtSerialClient::CBtSerialClient(MBtSerialClientObserver& aObserver)
: CActive( CActive::EPriorityStandard ), iObserver(aObserver),
  iState( EWaitingToGetDevice ), iCurrentServiceIndex( 0 )
    {
    CActiveScheduler::Add( this );
    }

// ----------------------------------------------------------------------------
EXPORT_C CBtSerialClient::~CBtSerialClient()
    {
    delete iSocketReader;
    delete iSocketWriter;

    Cancel();

    iSocket.Close();
    iSocketServer.Close();

    delete iServiceSearcher;
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::ConstructL()
    {
    LOG_D( "CBtSerialClient::ConstructL()" );

   // Check Bluetooth power state
    TBTPowerStateValue powerState = EBTPowerOff;
    CBTEngSettings* btSettings = CBTEngSettings::NewLC();
    TInt err = btSettings->GetPowerState( powerState );
    if ( err )
        {
        LOGFMT_W( "CBtSerialClient::ConstructL(): GetPowerState error %d", err );
        powerState = EBTPowerOff;
        }

    // If BT not on - try to set it on
    if ( powerState == EBTPowerOff )
        {
        LOG_I( "CBtSerialClient::ConstructL(): BT not on - setting power on" );
        err = btSettings->SetPowerState( EBTPowerOn );
        if ( err )
            {
            LOGFMT_E( "CBtSerialClient::ConstructL(): SetPowerState error %d", err );
            User::Leave( err );
            }

        // Wait until BT is reported to be on (or waiting time exceeds)
        TInt loopCounter = 0;
        while ( powerState == EBTPowerOff && loopCounter < KMaxBtStartWaitLoop )
            {
            btSettings->GetPowerState( powerState );
            LOGFMT_D( "CBtSerialClient::ConstructL(): BT power state %d", powerState );
            User::After( KBtStateQueryInterval );
            loopCounter++;
            }

        if ( powerState == EBTPowerOff )
            {
            LOG_E( "CBtSerialClient::ConstructL(): Could not turn BT on" );
            User::Leave( KErrCouldNotConnect );
            }
        LOG_I( "CBtSerialClient::ConstructL(): Continuing BT connect" );
        }
    CleanupStack::PopAndDestroy(); // btSettings

    iServiceSearcher = CMessageServiceSearcher::NewL();
    User::LeaveIfError( iSocketServer.Connect() );
    iSocketReader = CSocketsReader::NewL( *this, iSocket );
    iSocketWriter = CSocketsWriter::NewL( *this, iSocket );
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::DoCancel()
    {
    LOG_W("CBtSerialClient::DoCancel(): Doing nothing");
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::RunL()
    {
    if ( iStatus != KErrNone )
        {
        switch ( iState )
            {
            case EGettingDevice:
                if ( iStatus == KErrCancel )
                    {
                    LOG_W( "CBtSerialClient: No device selected" );
                    }
                iState = EWaitingToGetDevice;
                iObserver.ConnectedToServer( iStatus.Int() );
                break;
            case EGettingService:
                LOGFMT_W( "CBtSerialClient: Failed to fetch remote service: %d", iStatus.Int() );
                iObserver.ConnectedToServer( iStatus.Int() );
                iState = EWaitingToGetDevice;
                break;
            case EGettingConnection:
                LOGFMT_W( "CBtSerialClient: Failed to connect to remote service: %d", iStatus.Int() );
                if ( iCurrentServiceIndex < iServiceSearcher->ServiceCount() )
                    {
                    // Try another service
                    iCurrentServiceIndex++;
                    ConnectToServerL(); // establish RFComm connection
                    }
                else
                    {
                    iState = EWaitingToGetDevice;
                    iObserver.ConnectedToServer( iStatus.Int() );
                    }
                break;
            case EConnected:
                LOGFMT_I( "CBtSerialClient: Lost connection: %d", iStatus.Int() )
                DisconnectFromServer();
                iState = EDisconnecting;
                break;
            case EDisconnecting:
                LOGFMT_I("CBtSerialClient: Disconnected from server: %d", iStatus.Int() );
                iSocket.Close();
                iState = EWaitingToGetDevice;
                iObserver.DisconnectedFromServer();
                break;
            default:
                Panic( EBTPointToPointInvalidLogicState );
                break;
            }
        }
    else // iStatus = KErrNone
        {
        switch ( iState )
            {
            case EGettingDevice:
                // found a device now search for a suitable service
                LOGFMT_I("CBtSerialClient: Found device: %S. Finding correct service.", &(iServiceSearcher->ResponseParams().DeviceName()) );
                iState = EGettingService;
                iStatus = KRequestPending;  // this means that the RunL can not
                                            // be called until this program
                                            // does something to iStatus
                iBTServerDevice = iServiceSearcher->BTDevAddr();
                iServiceSearcher->FindServiceL( iBTServerDevice, iStatus );
                SetActive();
                break;
            case EGettingService:
                LOGFMT_I("CBtSerialClient: Found %d remote services", iServiceSearcher->ServiceCount());
                iState = EGettingConnection;
                ConnectToServerL(); // establish RFComm connection
                break;
            case EGettingConnection:
                LOG_I( "CBtSerialClient: Connected to remote service" );
                iState = EConnected;
                iObserver.ConnectedToServer( KErrNone );
                break;
            case EDisconnecting:
                LOG_I( "CBtSerialClient: Disconnecting" );
                iSocket.Close();
                iState = EWaitingToGetDevice;
                iObserver.DisconnectedFromServer();
                break;
            default:
                LOGFMT_E( "CBtSerialClient: Invalid logic state in RunL: %d. Will panic", iState );
                Panic( EBTPointToPointInvalidLogicState );
                break;
            };
        }
    }

// ----------------------------------------------------------------------------
EXPORT_C void CBtSerialClient::ConnectL()
    {
    if ( iState == EWaitingToGetDevice && !IsActive() )
        {
        LOG_D( "CBtSerialClient: Connecting by user selection" );
        iState = EGettingDevice;
        iServiceSearcher->SelectDeviceByDiscoveryL( iStatus );
        SetActive();
        }
    else
        {
        LOG_W( "CBtSerialClient: Already connecting or connected" );
        User::Leave( KErrInUse );
        }
    }

// ----------------------------------------------------------------------------
EXPORT_C void CBtSerialClient::ConnectL( const TBTDevAddr aBTServerDevice,
                                         const TInt aPort )
    {
    if ( iState == EWaitingToGetDevice && !IsActive() )
        {
        if ( aPort >= 0 )
            {
            // If there was a valid port given, add it as the first port to try
            iServiceSearcher->AppendPort( aPort );
            }
        LOG_D( "CBtSerialClient: Connecting by address" );
        iBTServerDevice = aBTServerDevice;
        iServiceSearcher->FindServiceL( iBTServerDevice, iStatus );
        iState = EGettingService;
        iStatus = KRequestPending;  // this means that the RunL can not
                                    // be called until this program
                                    // does something to iStatus
        SetActive();
        }
    else
        {
        LOG_W("CBtSerialClient: Already connecting or connected");
        User::Leave( KErrInUse );
        }
    }

// ----------------------------------------------------------------------------
EXPORT_C void CBtSerialClient::ConnectL( const TDesC& aBTServerDeviceName,
                                         const TInt aPort )
    {
    if ( iState == EWaitingToGetDevice && !IsActive() )
        {
        if ( aPort >= 0 )
            {
            // If there was a valid port given, add it as the first port to try
            iServiceSearcher->AppendPort( aPort );
            }
        LOG_D( "CBtSerialClient: Connecting by name" );
        iState = EGettingDevice;
        iServiceSearcher->SelectDeviceByNameL( aBTServerDeviceName, iStatus );
        SetActive();
        }
    else
        {
        LOG_W( "CBtSerialClient: Already connecting or connected" );
        User::Leave( KErrInUse );
        }
    }

// ----------------------------------------------------------------------------
EXPORT_C TBTDevAddr CBtSerialClient::ServerAddressL()
    {
    if ( !Connected() )
        {
        User::Leave( KErrNotReady );
        }
    return iBTServerDevice;
    }

// ----------------------------------------------------------------------------
EXPORT_C void CBtSerialClient::Disconnect()
    {
    if ( iState == EConnected )
        {
        DisconnectFromServer();
        iState = EDisconnecting;
        }
    else
        {
        LOG_W( "CBtSerialClient: Disconnect: Not connected" );
        User::Leave( KErrDisconnected );
        }
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::DisconnectFromServer()
    {
    // Terminate all operations
    iSocket.CancelAll();
    Cancel();
    iSocketReader->Cancel();
    iSocketWriter->CancelSending();

    LOG_I( "CBtSerialClient: Disconnecting from server" );
    iSocket.Shutdown( RSocket::ENormal, iStatus );
    SetActive();
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::ConnectToServerL()
    {
    LOG_I("CBtSerialClient: ConnectToServerL")
    User::LeaveIfError( iSocket.Open( iSocketServer, KStrRFCOMM ) );

    TBTSockAddr address;
    address.SetBTAddr( iServiceSearcher->BTDevAddr() );
    address.SetPort( iServiceSearcher->Port( iCurrentServiceIndex ) );

    LOGFMT_I("CBtSerialClient: ConnectToServerL: Port = %d", address.Port() );
    iSocket.Connect( address, iStatus );

#ifdef __WINS__
    User::After( 1 ); // Needed to allow emulator client to connect to server
#endif

    SetActive();
    }

// ----------------------------------------------------------------------------
EXPORT_C void CBtSerialClient::SendL(const TDesC8& aData)
    {
    if ( !Connected() )
        {
        User::Leave( KErrNotReady );
        }

    LOGFMT_D("CBtSerialClient::SendL: Sending data (max first 32 bytes): \"%S\"", &(aData.Left(32)));
    iSocketWriter->SendL( aData ); // Add to data queue and start sending
    LOG_D("CBtSerialClient::SendL: Sent to socket");
    }

EXPORT_C void CBtSerialClient::ReadAsyncL()
    {
    if ( !Connected() )
        {
        User::Leave( KErrNotReady );
        }
    iSocketReader->ReadAsync();
    }

// ----------------------------------------------------------------------------
EXPORT_C TInt CBtSerialClient::FreeSpaceInSendBuffer()
    {
    return iSocketWriter->FreeSpaceInSendBuffer();
    }

// ----------------------------------------------------------------------------
EXPORT_C TInt CBtSerialClient::SendBufferMaxSize()
    {
    return iSocketWriter->SendBufferMaxSize();
    }

// ----------------------------------------------------------------------------
EXPORT_C TBool CBtSerialClient::Connected()
    {
    return (iState == EConnected);
    }

// ----------------------------------------------------------------------------
EXPORT_C TBool CBtSerialClient::Connecting()
    {
    return ( ( iState == EGettingDevice ) ||
             ( iState == EGettingService ) ||
             ( iState == EGettingConnection ) );
    }

// ----------------------------------------------------------------------------
EXPORT_C TInt CBtSerialClient::ServicePort()
    {
    if ( !Connected() )
        {
        return KErrDisconnected;
        }
    return iServiceSearcher->Port( iCurrentServiceIndex );
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::ReportError( TErrorType aErrorType, TInt aErrorCode )
    {
    LOGFMT_W( "CBtSerialClient::ReportError: %d", aErrorCode );
    // From socket reader or writer
    switch ( aErrorType )
        {
        case EDisconnected:
            {
            LOG_I( "CBtSerialClient: disconnected" );
            }
            break;
        case ETimeOutOnWrite:
            {
            LOG_I( "CBtSerialClient: timout writing data. Disconnecting from server" );
            }
            break;
        case EGeneralReadError:
            {
            LOG_I( "CBtSerialClient: general read error. Disconnecting from server" );
            }
            break;
        case EGeneralWriteError:
            {
            LOG_I( "CBtSerialClient: general write error. Disconnecting from server" );
            }
            break;
        }
    Disconnect();
    aErrorCode = aErrorCode;
    }

// ----------------------------------------------------------------------------
 void CBtSerialClient::NewData( const TDesC8& aData )
    {
    iObserver.DataFromServer( aData );
    }

// ----------------------------------------------------------------------------
void CBtSerialClient::AllBufferedDataSent()
    {
    iObserver.AllBufferedDataSent();
    }

// End of File