adaptationlayer/tsy/nokiatsy_dll/src/cmmphonetreceiver.cpp
author mikaruus
Tue, 19 Oct 2010 13:16:20 +0300
changeset 9 8486d82aef45
parent 5 8ccc39f9d787
permissions -rw-r--r--
modemadaptation release 2010wk40

/*
* Copyright (c) 2007-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
*
*/



// INCLUDE FILES
#include <tisi.h>
#include "cmmphonetreceiver.h"
#include <ctsy/serviceapi/mmtsy_defaults.h>

// For logging purposes
#include "tsylogger.h"

// ISA CellMo ATK server
#include <pn_const.h>
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "cmmphonetreceiverTraces.h"
#endif

// EXTERNAL DATA STRUCTURES
    // None

// EXTERNAL FUNCTION PROTOTYPES
    // None

// CONSTANTS
#ifdef OST_TRACE_COMPILER_IN_USE
const TUint8 KBufferForOSTTracing = 100;
#endif
#ifdef TF_LOGGING_ENABLED
const TUint8 KBufferForTFLogging = 130;
#endif

// MACROS
    // None

// LOCAL CONSTANTS AND MACROS
    // None

// MODULE DATA STRUCTURES
    // None

// LOCAL FUNCTION PROTOTYPES
    //None

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

// -----------------------------------------------------------------------------
// TFLOG_PRINT_ISIMESSAGE
// local function for priting ISI message contents
// -----------------------------------------------------------------------------
//
#if defined (TF_LOGGING_ENABLED) || defined (OST_TRACE_COMPILER_IN_USE)
static void TFLOG_PRINT_ISIMESSAGE( const TDesC8& aBuf )
    {
    _LIT( KTraceSpace,           " " );

    // Get the length of the ISI message
    TInt length = aBuf.Length();

#ifdef TF_LOGGING_ENABLED
    TBuf<KBufferForTFLogging> msg;
    _LIT( KTracePhonetReceiver,  "TSY:Phonet Receiver:  [ " );
    _LIT( KTraceBracketClose,    "]" );

    msg.Append( KTracePhonetReceiver );

    if ( 250 < length )
        {
        // Print only 250 first bytes
        length = 250;
        }

    for ( TInt i = 0; i < length; i++ )
        {
        msg.AppendNumFixedWidthUC( aBuf[i], EHex, 2 );
        msg.Append( KTraceSpace );
        if ( KLogSizeOfBuffer-10 < msg.Length() )
            {
            if ( i == ( length - 1 ) )
                {
                // Last number, append "]" character
                msg.Append( KTraceBracketClose );
                }

            TFLOGTEXT( msg );
            msg.SetLength( 0 );
            }
        }

    if ( 0 < msg.Length() )
        {
        // Buffer contains data. Print also last line
        msg.Append( KTraceBracketClose );
        TFLOGTEXT( msg );
        }
#endif // TF_LOGGING_ENABLED

    // Ost tracing
#ifdef OST_TRACE_COMPILER_IN_USE
    TBuf8<KBufferForOSTTracing> trace_msg;
    TUint8 counter = 1;
    TBool firstTime = ETrue;
    for ( TInt i = 0; i < length; i++ )
        {
        trace_msg.AppendNumFixedWidthUC( aBuf[i], EHex, 2 );
        trace_msg.Append( KTraceSpace );
        
        if ( ( counter > 24 ) ||     // 25 bytes / line
           ( ( i + 1 ) == length ) ) // All bytes collected
          {
          if ( ( firstTime ) &&
               ( ( i + 1 ) == length ) ) // All bytes collected and traced
              {
              firstTime = EFalse;
OstTraceExt1( TRACE_NORMAL,  DUP2__TFLOG_PRINT_ISIMESSAGE_TD, "Phonet Receiver: [ %s]", trace_msg );
                trace_msg.SetLength( 0 );
              }
            else if ( firstTime ) // 1st line of the trace
              {
              firstTime = EFalse;
OstTraceExt1( TRACE_NORMAL,  DUP3__TFLOG_PRINT_ISIMESSAGE_TD, "Phonet Receiver: [ %s", trace_msg );
                trace_msg.SetLength( 0 );
              }
            else if ( ( i + 1 ) == length ) // The last line
              {
OstTraceExt1( TRACE_NORMAL,  DUP4_TFLOG_PRINT_ISIMESSAGE_TD, "Phonet Receiver:   %s]", trace_msg );
                trace_msg.SetLength( 0 );
              }
            else // just print bytes
              {
OstTraceExt1( TRACE_NORMAL,  DUP5_TFLOG_PRINT_ISIMESSAGE_TD, "Phonet Receiver:   %s", trace_msg );
                trace_msg.SetLength( 0 );
              }
            counter = 0;
          }
        counter++;
        }    
#endif // OST_TRACE_COMPILER_IN_USE
   
    }

#else // TF_LOGGING_ENABLED || OST_TRACE_COMPILER_IN_USE

#define TFLOG_PRINT_ISIMESSAGE(x)

#endif // TF_LOGGING_ENABLED || OST_TRACE_COMPILER_IN_USE

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

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::CMmPhoNetReceiver
// C++ default constructor
// -----------------------------------------------------------------------------
//
CMmPhoNetReceiver::CMmPhoNetReceiver()
        : CActive( 0 ),
        iMessageReceiver( NULL ),
        iMessageBuffer( NULL ),
        iMessageBufferPtr( 0,0 ),
        iNeededBufferLength( 0 ),
        iSatMessHandler( NULL ),
        iSatMessageBuffer( NULL )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::CMmPhoNetReceiver");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_CMMPHONETRECEIVER_TD, "CMmPhoNetReceiver::CMmPhoNetReceiver" );
    //None
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::~CMmPhoNetReceiver
// Destructor
// -----------------------------------------------------------------------------
//
CMmPhoNetReceiver::~CMmPhoNetReceiver()
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::~CMmPhoNetReceiver");
OstTrace0( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_CMMPHONETRECEIVER_TD, "CMmPhoNetReceiver::~CMmPhoNetReceiver" );
    // Shutdown the active scheduler correctly.
    Cancel();
    Deque();

    // Closes the array and frees all memory allocated to the array.
    iMsgReceivers.Close();

    delete iMessageBuffer;
    delete iSatMessageBuffer;
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::NewL
// Two-phased constructor.
// Creates a new CMmPhoNetReceiver object instance. If either
// mmPhone or phonet received as parameters is NULL, CMmPhoNetReceiver will
// not be created and NULL will be returned.
// -----------------------------------------------------------------------------
//
CMmPhoNetReceiver* CMmPhoNetReceiver::NewL
        (
        RIscApi* aPn // Phonet
        )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::NewL");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_NEWL_TD, "CMmPhoNetReceiver::NewL" );

    CMmPhoNetReceiver* phoNetReceiver = NULL;

    if ( NULL != aPn )
        {
        phoNetReceiver = new ( ELeave ) CMmPhoNetReceiver();

        CleanupStack::PushL( phoNetReceiver );
        phoNetReceiver->ConstructL();
        CleanupStack::Pop( phoNetReceiver );

        // Set pointer to the Phonet
        phoNetReceiver->iPhoNet = aPn;

        // Adds the specified active object to the current active scheduler
        CActiveScheduler::Add( phoNetReceiver );
        }

    return phoNetReceiver;
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::ConstructL
// Symbian 2nd phase constructor.
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::ConstructL()
    {
    // Allocate memory for incoming message.
    TFLOGSTRING("TSY: CMmPhoNetReceiver::ConstructL");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_CONSTRUCTL_TD, "CMmPhoNetReceiver::ConstructL" );

    iMessageBuffer = HBufC8::NewL( KDefaultReceiveBufferSize );
    iMessageBufferPtr.Set( iMessageBuffer->Des() );

    TFLOGSTRING2("TSY: iMessageBuffer len=%d", iMessageBuffer->Length());
OstTrace1( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_CONSTRUCTL_TD, "CMmPhoNetReceiver::ConstructL;iMessageBuffer->Length=%d", iMessageBuffer->Length() );
    TFLOGSTRING3("TSY: iMessageBufferPtr len=%d maxlen=%d",
        iMessageBufferPtr.Length(),
        iMessageBufferPtr.MaxLength());
OstTrace1( TRACE_NORMAL,  DUP2_CMMPHONETRECEIVER_CONSTRUCTL_TD, "CMmPhoNetReceiver::ConstructL;iMessageBufferPtr.MaxLength=%d", iMessageBufferPtr.MaxLength() );
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::DoCancel
// Cancels an active receiver request.
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::DoCancel()
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::DoCancel");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_DOCANCEL_TD, "CMmPhoNetReceiver::DoCancel" );
    // Cancel message receiving from Phonet
    iPhoNet->ReceiveCancel();
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::RunError
// Called when RunL leaves.
// Calls message handler's HandleError method,
// iMessageReceiver variable carries pointer to message handler.
// -----------------------------------------------------------------------------
//
TInt CMmPhoNetReceiver::RunError
        (
        TInt aError // Error from RunL or active scheduler
        )
    {
    TFLOGSTRING2("TSY: CMmPhoNetReceiver::RunError - Error code: %d", aError);
OstTrace1( TRACE_NORMAL,  CMMPHONETRECEIVER_RUNERROR_TD, "CMmPhoNetReceiver::RunError;aError=%d", aError );
    if ( iMessageReceiver )
        {
        iMessageReceiver->HandleError( TIsiReceiveC( iMessageBufferPtr ),
            aError );
        iMessageReceiver = NULL;
        }

    if ( NULL == iMessageBuffer )
        {
        TFLOGSTRING("TSY: CMmPhoNetReceiver::RunError\
            -- receive buffer resize failed!");
OstTrace0( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_RUNERROR_TD, "CMmPhoNetReceiver::RunError, receive buffer resize failed!" );
        }

    // Restart message receiving
    TRAPD( result, ReceiveL() );
    return result;
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::RunL
// Handles messages received from PhoNet when receiver request
// is completed by PhoNet
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::RunL()
    {
    TFLOGSTRING3("TSY: CMmPhoNetReceiver::RunL\
        -- iMessageBufferPtr len=%d maxlen=%d",
        iMessageBufferPtr.Length(),
        iMessageBufferPtr.MaxLength());
OstTraceExt2( TRACE_NORMAL,  CMMPHONETRECEIVER_RUNL_TD, "CMmPhoNetReceiver::RunL;iMessageBufferPtr.Length=%d;iMessageBufferPtr.MaxLength=%d", iMessageBufferPtr.Length(), iMessageBufferPtr.MaxLength() );

    if ( KErrNone != iStatus.Int() && KErrOverflow != iStatus.Int() )
        {
        // If message was too large, allocate more memory and re-request
        // message.
        if ( KErrNoMemory == iStatus.Int() )
            {
            TFLOGSTRING2("TSY: CMmPhoNetReceiver::RunL\
                -- Large message received, resizing buffer to %d",
                iNeededBufferLength);
OstTraceExt1( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_RUNL_TD, "CMmPhoNetReceiver::RunL;iNeededBufferLength=%hu", iNeededBufferLength );
            // Request message again
            ReceiveL( iNeededBufferLength );
            }
        else
            {
            // For all other error cases, just start waiting for next message
            TFLOGSTRING2("CMmPhoNetReceiver::Receive - iStatus = %d",
                iStatus.Int());
OstTrace1( TRACE_NORMAL,  DUP2_CMMPHONETRECEIVER_RUNL_TD, "CMmPhoNetReceiver::RunL;iStatus.Int=%d", iStatus.Int() );
            ReceiveL();
            }
        }
    else
        {
        // Print message to log
        TFLOG_PRINT_ISIMESSAGE( iMessageBufferPtr );

        // Dispatch message to receivers
        DispatchL( TIsiReceiveC( iMessageBufferPtr ) );

        // Start waiting for next message
        ReceiveL();
        }
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::RegisterL
// Register messagereceiver to phonetreceiver.
// -1 can be used as aMessageId to mean all messages for resource
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::RegisterL
        (
        MMmMessageReceiver* aReceiver,
        TInt aResource,
        TInt aMessageId
        )
    {
    TFLOGSTRING4("TSY: CMmPhoNetReceiver::RegisterL\
        -- receiver=0x%x resource=0x%x msgid=0x%x",
        (TInt)aReceiver,
        aResource,
        aMessageId);
OstTraceExt3( TRACE_NORMAL,  CMMPHONETRECEIVER_REGISTERL_TD, "CMmPhoNetReceiver::RegisterL;aReceiver=%x;aResource=%x;aMessageId=%d", (TInt)aReceiver, aResource, aMessageId );

    TInt resourceIndex = -1;

    // Check for double registrations.
    for ( TInt i = 0; i < iMsgReceivers.Count(); i++ )
        {
        const TMessageReceiverInfo& ri = iMsgReceivers[i];

        if ( ri.iResource == aResource )
            {
            // Store index where new registration entry is later to be stored
            resourceIndex = i;

            if ( ( ri.iReceiver == aReceiver ) &&
                ( ri.iMessageId == aMessageId ||
                -1 == ri.iMessageId ||
                -1 == aMessageId ) )
                {
                // We want to avoid situations where the same handler receivers
                // a message twice..
                TFLOGSTRING("TSY: CMmPhoNetReceiver::RegisterL\
                    -- Already registered");
OstTrace0( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_REGISTERL_TD, "CMmPhoNetReceiver::RegisterL, ASSERT Already registered" );
                TF_ASSERT_NOT_REACHED();
                return;
                }
            }
        }

    // Register
    TMessageReceiverInfo info;
    info.iReceiver = aReceiver;
    info.iResource = aResource;
    info.iMessageId = aMessageId;

    // Because of binary search in DispatchL method we have to keep objects in
    // the order. For example all PN_CALL requests are located in sequential
    // locations.
    if ( -1 == resourceIndex )
        {
        // This is registration for a new server
        TInt i = iMsgReceivers.Count();

        for ( ; i > 0; i-- )
            {
            const TMessageReceiverInfo& ri = iMsgReceivers[i-1];
            if ( ri.iResource < aResource )
                {
                resourceIndex = i;
                break;
                }
            }

        if ( -1 == resourceIndex )
            {
            // Insert new registration at the start of array
            resourceIndex = 0;
            }
        }

    iMsgReceivers.InsertL( info, resourceIndex );
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::DispatchL
// Get received message from phonet and dispatch it
// to all registered receivers. Cache SAT messages if SAT is not yet initialized
// Binary search is used to find first message handler registered to receive
// the message.
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::DispatchL
        (
        const TIsiReceiveC& aIsiMessage
        )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::DispatchL");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_DISPATCHL_TD, "CMmPhoNetReceiver::DispatchL" );

    // Get resource and message id
    TInt resource( aIsiMessage.Get8bit( ISI_HEADER_OFFSET_RESOURCEID ) );
    TInt messageId( aIsiMessage.Get8bit( ISI_HEADER_OFFSET_MESSAGEID ) );

    TFLOGSTRING3("TSY: CMmPhoNetReceiver::DispatchL - resource: 0x%x, id: 0x%x",
        resource,
        messageId);
OstTraceExt2( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_DISPATCHL_TD, "CMmPhoNetReceiver::DispatchL;resource=%d;messageId=%x", resource, messageId );

    // Get lowest request index using binary search
    TInt highIndex = iMsgReceivers.Count();
    TInt lowIndex = -1;
    TInt probe;
    while ( highIndex - lowIndex > 1 )
        {
        probe = ( highIndex + lowIndex ) / 2;
        const TMessageReceiverInfo& ri = iMsgReceivers[probe];
        if ( ri.iResource < resource )
            {
            lowIndex = probe;
            }
        else
            {
            highIndex = probe;
            }
        }

    // Send ISI message to message handlers
    for ( ; highIndex < iMsgReceivers.Count(); highIndex++ )
        {
        const TMessageReceiverInfo& recInfo = iMsgReceivers[highIndex];
        if ( recInfo.iResource == resource )
            {
            if ( recInfo.iMessageId == messageId || -1 == recInfo.iMessageId )
                {
                iMessageReceiver = recInfo.iReceiver;
                iMessageReceiver->ReceiveMessageL( aIsiMessage );
                }
            }
        else
            {
            // Stop searching
            break;
            }
        }

    // Should be set null here in case SAT receivemessage leaves
    iMessageReceiver = NULL;

    if( iSatMessHandler )
        {
        // Sat message handler takes all messages. So make it fast.
        iSatMessHandler->ReceiveMessageL( aIsiMessage );
        }
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::Receive
// Start waiting for message from Phonet.
// -----------------------------------------------------------------------------
//
void CMmPhoNetReceiver::ReceiveL
        (
        TInt aBufferLength
        )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::Receive");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_RECEIVEL_TD, "CMmPhoNetReceiver::ReceiveL" );

    // Resize receive buffer size if necessary
    if ( NULL == iMessageBuffer ||
        aBufferLength != iMessageBufferPtr.MaxLength() )
        {
        TFLOGSTRING2("TSY: CMmPhoNetReceiver::Receive - resizing buffer to %d",
            aBufferLength);
OstTrace1( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_RECEIVEL_TD, "CMmPhoNetReceiver::ReceiveL;aBufferLength=%d", aBufferLength );

        // Delete old buffer
        delete iMessageBuffer;
        iMessageBuffer = NULL;

        // Allocate new buffer
        iMessageBuffer = HBufC8::NewL( aBufferLength );
        iMessageBufferPtr.Set( iMessageBuffer->Des() );
        TFLOGSTRING2("TSY: iMessageBuffer len=%d", iMessageBuffer->Length());
OstTrace1( TRACE_NORMAL,  DUP2_CMMPHONETRECEIVER_RECEIVEL_TD, "CMmPhoNetReceiver::ReceiveL;iMessageBuffer->Length=%d", iMessageBuffer->Length() );
        }

    // Do asyncronous reqest for message from Phonet
    iMessageBufferPtr.SetLength( 0 );
    iStatus = KRequestPending;
    iPhoNet->Receive( iStatus, iMessageBufferPtr, iNeededBufferLength );
    // Indicate that the active object has issued a
    // request and that it is now outstanding.
    SetActive();
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::SetSatMessHandler
// Set SAT messagehandler pointer, called by SIM_ATK_TSY
// -----------------------------------------------------------------------------
//
TInt CMmPhoNetReceiver::SetSatMessHandler
        (
        MMmMessageReceiver* aSatMessHandler // SAT messagehandler
        )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::SetSatMessHandler");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_SETSATMESSHANDLER_TD, "CMmPhoNetReceiver::SetSatMessHandler" );
    iSatMessHandler = aSatMessHandler;
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::SatMessHandler
// Get SAT messagehandler pointer
// -----------------------------------------------------------------------------
//
MMmMessageReceiver* CMmPhoNetReceiver::SatMessHandler()
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::SatMessHandler");
    OstTrace0( TRACE_NORMAL, CMMPHONETRECEIVER_SATMESSHANDLER_TD, "CMmPhoNetReceiver::SatMessHandler" );
    return iSatMessHandler;
    }

// -----------------------------------------------------------------------------
// CMmPhoNetReceiver::SatReady
// SIM_ATK_TSY requests cached SAT message from NokiaTSY by calling this method
// -----------------------------------------------------------------------------
//
TInt CMmPhoNetReceiver::SatReady
        (
        TUint8 aTypeOfCommandRequested  // Message Id
        )
    {
    TFLOGSTRING("TSY: CMmPhoNetReceiver::SatReady");
OstTrace0( TRACE_NORMAL,  CMMPHONETRECEIVER_SATREADY_TD, "CMmPhoNetReceiver::SatReady" );
    TInt ret( KErrNotFound );

    // If there is cached SAT message that
    // matches request type return it to SIM_ATK_TSY
    if ( iSatMessageBuffer )
        {
        const TDesC8& message( *iSatMessageBuffer );

        // Take typeofcommand from TLV
        TUint8 typeOfCommand = ( 0x81 == message[15] ) ? message[20] : message[19];

        TFLOGSTRING3("TSY: CMmPhoNetReceiver::SatReady\
            -- aTypeOfCommandRequested: 0x%x, typeOfCommand: 0x%x",
            aTypeOfCommandRequested,
            typeOfCommand);
OstTraceExt2( TRACE_NORMAL,  DUP1_CMMPHONETRECEIVER_SATREADY_TD, "CMmPhoNetReceiver::SatReady;aTypeOfCommandRequested=%hhx;typeOfCommand=%hhx", aTypeOfCommandRequested, typeOfCommand );

        if ( typeOfCommand == aTypeOfCommandRequested )
            {
            TFLOGSTRING("TSY: CMmPhoNetReceiver::SatReady -- sending msg to sat");
OstTrace0( TRACE_NORMAL,  DUP2_CMMPHONETRECEIVER_SATREADY_TD, "CMmPhoNetReceiver::SatReady, sending msg to sat" );
            // Call receivemessage and catch possible leave.
            TRAP_IGNORE( iSatMessHandler->ReceiveMessageL(
                TIsiReceiveC( message ) ) );
            // Free used buffer
            delete iSatMessageBuffer;
            iSatMessageBuffer = NULL;
            TFLOGSTRING("TSY: CMmPhoNetReceiver::SatReady\
                -- iSatMessageBuffer deleted");
OstTrace0( TRACE_NORMAL,  DUP3_CMMPHONETRECEIVER_SATREADY_TD, "CMmPhoNetReceiver::SatReady, iSatMessageBuffer deleted" );
            ret = KErrNone;
            }
        }
    return ret;
    }

// ==================== OTHER EXPORTED FUNCTIONS ===============================
    // None

// End of File