bluetoothengine/btnotif/btnotifsrv/src/btnotifsession.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        : btnotifsession.cpp
*  Part of     : bluetoothengine / btnotif
*  Description : Session class for handling commands from clients.
*
*  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 "btnotifsession.h"
#include <btextnotifiers.h>
#include "btnotifclientserver.h"
#include "btnotifsettingstracker.h"
#include "btnotifconnectiontracker.h"
#include "btnotifdeviceselector.h"


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

// ---------------------------------------------------------------------------
// Start the server.
// ---------------------------------------------------------------------------
//
void LeaveIfNullL( const TAny* aPtr, TInt aLeaveCode )
    {
    if( aPtr == NULL )
        {
        User::Leave( aLeaveCode );
        }
    }

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

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CBTNotifSession::CBTNotifSession()
:   CSession2()
    {
    }

// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTNotifSession::ConstructL()
    {
    }

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


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTNotifSession::~CBTNotifSession()
    {
    for( TInt i = 0; i < iMessageQ.Count(); i++ )
        {
        // Complete all outstanding messages with error code
        iMessageQ[i].Complete( KErrSessionClosed );
        }
    iMessageQ.Close();  // Cleans up all message objects.
    Server()->RemoveSession();
    }

// ---------------------------------------------------------------------------
// From class CSession2.
// Receives a message from a client.
// ---------------------------------------------------------------------------
//
void CBTNotifSession::ServiceL( const RMessage2& aMessage )
    {
    TInt opCode = aMessage.Function();
    if ( opCode == EBTNotifCancelNotifier || 
         opCode == EBTNotifStartSyncNotifier ||
         opCode == EBTNotifStartAsyncNotifier ||
         opCode == EBTNotifUpdateNotifier )
        {
        TInt uid = aMessage.Int0();
        if( uid == KDeviceSelectionNotifierUid.iUid )
            {
            // Note for implementers:
            // message queue is not used in this notifier handling (due
            // to its drawbacks for exception handlings in various situations). 
            // implementations using message queue will be migrated step
            // by step.
        
            TRAPD( err, {
                CBTNotifDeviceSelector& selector = Server()->DeviceSelectorL();
                selector.DispatchNotifierMessageL( aMessage ); }
                );
            if ( err )
                {
                aMessage.Complete( err );
                }
            // deviceselector takes the ownership of aMessage.
            return;
            }
        }

    // Messages are completed by message handlers, not here.
    // Queue the message already so that handlers can find it from the queue.
    iMessageQ.AppendL( aMessage );
    // The position is assumed to not change during the execution of this function.
    TInt handle = aMessage.Handle();    // Store the handle for de-queueing
    TRAPD( err, DispatchMessageL( aMessage ) );
    if( err || ( aMessage.IsNull() && FindMessageFromHandle( handle ) ) )
        {
        // If the message has been completed by now (handle is null and the message 
        // still in the queue), we remove it again from the queue. Otherwise it 
        // will be completed by the handling handler when it has handled the handling.
        for( TInt i = 0; i < iMessageQ.Count(); i++ )
            {
            // This may be replaced by RArray::Find with appropriate key
            if( iMessageQ[i].Handle() == handle )
                {
                iMessageQ.Remove( i );
                }
            }
        }
    if( err && !aMessage.IsNull() )
        {
        aMessage.Complete( err );
        }
    }


// ---------------------------------------------------------------------------
// From class CSession2.
// Completes construction of the session.
// ---------------------------------------------------------------------------
//
void CBTNotifSession::CreateL()
    {
    Server()->AddSession();
    }

// ---------------------------------------------------------------------------
// Complete a client message from a message handle with given data.
// If a zero-length descriptor is passed, no data will be written back.
// ---------------------------------------------------------------------------
//
TInt CBTNotifSession::CompleteMessage( TInt aHandle, TInt aReason, const TDesC8& aReply )
    {
    TInt err = KErrNotFound;
    // This may be replaced by RArray::Find with appropriate key
    for( TInt i = 0; i < iMessageQ.Count(); i++ )
        {
        if( iMessageQ[i].Handle() == aHandle )
            {
            err = KErrNone;
            if( aReply.Length() )
                {
                // For now, assume a fixed index for the result.
                // Change this if a the client can pass more arguments!
                // ToDo: replace with constant!
                err = iMessageQ[i].Write( EBTNotifSrvReplySlot, aReply );
                // Should the result be passed back to the calller,
                // or used to complete the message?
                }
            iMessageQ[i].Complete( aReason );
            iMessageQ.Remove( i );
            break;
            }
        }
    return err;
    }


// ---------------------------------------------------------------------------
// Find a client message from an RMessage2 handle.
// ---------------------------------------------------------------------------
//
const RMessage2* CBTNotifSession::FindMessageFromHandle( TInt aHandle ) const
    {
    // This may be replaced by RArray::Find with appropriate key
    for( TInt i = 0; i < iMessageQ.Count(); i++ )
        {
        if( iMessageQ[i].Handle() == aHandle )
            {
            return &iMessageQ[i];
            }
        }
    return NULL;
    }


// ---------------------------------------------------------------------------
// Process a client message.
// The processing here relies on RNotifier backend server for queueing 
// notifiers on the same channel. Therefore pairing (SSP and legacy) and 
// authorization notifiers arrive in order, not simultaneously, even if 
// they use arrive on different session instances.
// ---------------------------------------------------------------------------
//
void CBTNotifSession::DispatchMessageL( const RMessage2& aMessage )
    {
    BOstraceFunctionEntryExt( DUMMY_DEVLIST, this, aMessage.Function() );
    CBTNotifSettingsTracker* settTracker = Server()->SettingsTracker();
    CBTNotifConnectionTracker* connTracker = Server()->ConnectionTracker();
    LeaveIfNullL( settTracker, KErrNotReady );
    TInt opcode = aMessage.Function();
    if( opcode < EBTNotifMinValue )
        {
        User::Leave( KErrArgument );
        }
    switch( opcode )
        {
        case EBTNotifCancelNotifier:
        case EBTNotifStartSyncNotifier:
        case EBTNotifStartAsyncNotifier:
        case EBTNotifUpdateNotifier:
            {
            // All these messages get the same treatment: forward 
            // to settings and connection tracker, who will deal with it appropriately.
            // First the settings tracker handles the message.
            settTracker->DispatchNotifierMessageL( aMessage );
            if( connTracker && !aMessage.IsNull() )
                {
                // Pass it on to the connection tracker, if it hasn't been completed yet.
                connTracker->DispatchNotifierMessageL( aMessage );
                }
            else
                {
                // Power is off, can't do this now.
                LeaveIfNullL( connTracker, KErrNotReady );
                }
            if( opcode != EBTNotifStartAsyncNotifier && !aMessage.IsNull() )
                {
                // Nobody has yet completed the message, and it is a synchronous
                // one so we'll do it here to allow the notifier to keep on going.
                aMessage.Complete( KErrNone );
                }
            }
            break;
        case EBTEngPrepareDiscovery:
            {
            // This is functionality only related to existing connections.
            // Can't do when power is off though.
            LeaveIfNullL( connTracker, KErrNotReady );
            //connTracker->HandlePairingRequestL( aMessage );
            }
            break;
        case EBTEngPairDevice:
        case EBTEngCancelPairDevice:
            {
            // This is functionality only related to connections.
            // Can't do when power is off though.
            LeaveIfNullL( connTracker, KErrNotReady );
            connTracker->HandleBondingRequestL( aMessage );
            }
            break;
        default:
            // Communicate result back.
            User::Leave( KErrNotSupported );
            break;
        }
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }


// ---------------------------------------------------------------------------
// Find a client message from an RNotifier UID.
// ---------------------------------------------------------------------------
//
const RMessage2* CBTNotifSession::FindMessageFromUid( TInt aUid ) const
    {
    // This may be replaced by RArray::Find with appropriate key
    for( TInt i = 0; i < iMessageQ.Count(); i++ )
        {
        if( iMessageQ[i].Int0() == aUid )
            {
            return &iMessageQ[i];
            }
        }
    return NULL;
    }