atext/server/src/atextmetadata.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 11:01:00 +0300
branchRCL_3
changeset 22 786b94c6f0a4
parent 16 9f17f914e828
child 23 5b153be919d4
permissions -rw-r--r--
Revision: 201031 Kit: 201033

/*
* Copyright (c) 2008-2010 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:  Metadata for ATEXT
*
*/


/*
 * Here is the plugin managing logic for the AT commands and hopefully also for
 * future needs:
 *
 * Three types of support:
 * 1) Master (M): Sends to all O if support found. Panics if > 1 M.
 * 2) Partial (P): Sends to all O if support found. Replies "ERROR" if two P supports.
 * 3) Observer (O): Process the command and give or not give reply, based on
 *    the following logic:
 *
 * => [If] M found, handle command and send reply + send to N O {no reply}, stop, [else]
 * [If] P found, handle command and send reply + send to N O {no reply}, stop, [else]
 * [If] > 1 O found, send to N O {no reply}, stop, [else]
 * [If] only 1 O found, handle command and send reply, stop, [else]
 * Write "ERROR" to client, complete message with KErrNone
 *
 * When incoming reply:
 * => If reply from M, write to client, stop, [else]
 * If reply from P, write to client, stop, [else]
 * If reply from O and M, P nor other O exist, write to client, stop, [else]
 * Complete message with KErrNone and empty string
 *
 * Note: Empty string and "ERROR" string are managed already in HandleCommand()
 */

#include <ecom/ecom.h>
#include <ecom/implementationinformation.h>
#include <atextpluginbase.h>
#include "atextclientsrv.h"
#include "atextmetadata.h"
#include "atextlisten.h"
#include "utils.h"
#include "debug.h"

const TInt  KGranularity           = 4;
const TInt8 KDefaultCarriageReturn = 13;
const TInt8 KDefaultLineFeed       = 10;
const TInt  KErrorMsgLen           = 9;  // 2+"ERROR"+2

// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CATExtMetadata* CATExtMetadata::NewL( REComSession& aEComSession,
                                      CATExtListen* aListener,
                                      MATExtPluginObserver& aObserver )
    {
    CATExtMetadata* self = NewLC( aEComSession, aListener, aObserver );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CATExtMetadata* CATExtMetadata::NewLC( REComSession& aEComSession,
                                       CATExtListen* aListener,
                                       MATExtPluginObserver& aObserver )
    {
    CATExtMetadata* self = new (ELeave) CATExtMetadata( aEComSession,
                                                        aListener,
                                                        aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CATExtMetadata::~CATExtMetadata()
    {
    ResetData();
    }

// ---------------------------------------------------------------------------
// Resets data to initial values
// ---------------------------------------------------------------------------
//
void CATExtMetadata::ResetData()
    {
    TRACE_FUNC_ENTRY
    iShutdown = ETrue;
    DestroyPlugindata();
    DestroySupportdata( iSupport );
    DestroySupportdata( iSupportAux );
    iConnectionName.Close();
    iShutdown = EFalse;
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Creates new implementation metadata based on a given interface UID and
// connection. Uses AddImplementationL() to add new implementations for the
// interface and sets connection identification name for reporting it to the
// plugins on instantiation time.
// ---------------------------------------------------------------------------
//
void CATExtMetadata::CreateImplementationMetadataL( TUid& aIfUid,
                                                    const TDesC8& aName )
    {
    TRACE_FUNC_ENTRY
    if ( iPluginData || iSupport || iSupportAux )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    iIfUid = aIfUid;
    iConnectionName.CreateL( aName );
    RImplInfoPtrArray implementations;
    CleanupResetDestroyClosePushL( implementations );
    iEComSession.ListImplementationsL( iIfUid, implementations );
    const TUint implCount = implementations.Count();
    TRACE_INFO((_L("Number of AT Ext: %d"), implCount))
    iSupport = new (ELeave) CArrayFixFlat<TATExtAtCmdSupport>( KGranularity );
    iSupportAux = new (ELeave) CArrayFixFlat<TATExtAtCmdSupport>( KGranularity );
    iPluginData = new (ELeave) CArrayFixFlat<TATExtPluginEntry>( KGranularity );
    // As iSupport, iSupportAux and iPluginData are needed by
    // AddImplementationL(), no ease way to use temporary variables here.
    // If error occurs here then deletion of this class is required.
    for ( TUint i=0; i<implCount; i++ )
        {
        AddImplementationL( implementations[i] );
        }
    CleanupStack::PopAndDestroy( &implementations );
#if defined(_DEBUG) && defined( PRJ_PRINT_SUPPORT_DATA )
    PrintSupportData( iSupport );
    PrintSupportData( iSupportAux );
#endif
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Adds an implementation to the metadata based on a given ECOM implementation
// information. This is used by the function CreateImplementationMetadataL()
// and also by CATExtSession when its ECOM notification listener detects an
// addition for a the same ECOM interface.
// ---------------------------------------------------------------------------
//
void CATExtMetadata::AddImplementationL( CImplementationInformation* aImplInfo )
    {
    TRACE_FUNC_ENTRY
    if ( !aImplInfo )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    DoAddImplementationL( aImplInfo, aImplInfo->DataType() );
    DoAddImplementationL( aImplInfo, aImplInfo->OpaqueData() );
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Removes an implementation from the support data by a given plugin UID.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::RemoveImplementation( TUid& aPluginUid,
                                           TBool aInstanceExists )
    {
    TRACE_FUNC_ENTRY
    if ( iShutdown )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    DoRemoveImplementation( aPluginUid, iSupport );
    DoRemoveImplementation( aPluginUid, iSupportAux );
    TInt retVal = RemoveOnePlugindata( aPluginUid, aInstanceExists );
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Handles an AT command
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::HandleCommand( const RMessage2& aMessage,
                                    TATExtCompletionInfo& aComplInfo )
    {
    TRACE_FUNC_ENTRY
    iCmdData.iCmdMessage = aMessage;
    if ( IsCommandHandling() )
        {
        TRACE_FUNC_EXIT
        return KErrInUse;
        }
    // Next pass the entry to command reader
    TInt retTemp = ReadCommandFromMessage( aMessage );
    if ( retTemp != KErrNone )
        {
        TRACE_FUNC_EXIT
        return retTemp;
        }
    // First check if in editor mode
    if ( iCmdData.iEditorHandler )
        {
        iCmdData.iReplyExpected = EFalse;
        iCmdData.iHandler = iCmdData.iEditorHandler;
        iCmdData.iHandler->iInstance->HandleCommand( iCmdData.iCmdBuffer,
                                                     iCmdData.iCmdReplyBuffer,
                                                     EFalse );
        aComplInfo.iProcessed = ETrue;
        aComplInfo.iReplyExpected = ETrue;
        // Note: The aComplInfo.iReplyExpected is used only for normal mode and
        // is set to ETrue here to skip a check in CATExtSession::IpcHandleCommand().
        TRACE_FUNC_EXIT
        return KErrNone;
        }
    // Not in editor so handle in normal mode
    TRACE_INFO(( _L8("Received command '%S'"), &iCmdData.iCmdBuffer ));
    // Now the command exists. Load the plugins for a command and check support.
    TRAPD( retTrap, CreateAndFindSupportL(iCmdData.iCmdBuffer,
                                          aMessage,
                                          aComplInfo ) );
    TRACE_FUNC_EXIT
    return retTrap;
    }

// ---------------------------------------------------------------------------
// Cancels an active handle command operation. Uses IsCommandHandling() and
// CancelCommandOperation().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CancelHandleCommand()
    {
    TRACE_FUNC_ENTRY
    if ( !IsCommandHandling() )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    CancelCommandOperation( KErrCancel, ETrue );
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Gets the next part of a reply for HandleCommand(). Length of returned reply
// must be the same as the one reported from NextReplyPartLength() for the
// current reply.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::GetNextPartOfReply( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    if ( !iCmdData.iHandler || !iCmdData.iHandler->iInstance )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    CATExtPluginBase& plugin = *iCmdData.iHandler->iInstance;
    TInt retTemp = plugin.GetNextPartOfReply( iCmdData.iCmdReplyBuffer );
    if ( retTemp!=KErrNone || iCmdData.iCmdReplyBuffer.Length()<=0 )
        {
        iCmdData.iCmdReplyBuffer.Close();
        TRACE_FUNC_EXIT
        return retTemp;
        }
    retTemp = WriteReplyBufferToClient(
        iCmdData.iCmdReplyBuffer,
        EATExtGetNextPartOfReplyParamReply,
        aMessage,
        ETrue,
        EATExtGetNextPartOfReplyParamLength );
    if ( retTemp != KErrNone )
        {
        iCmdData.iCmdReplyBuffer.Close();
        TRACE_FUNC_EXIT
        return retTemp;
        }
    aMessage.Complete( KErrNone );
    iCmdData.iCmdReplyBuffer.Close();
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Completes an URC processing message by a given plugin UID. Completion code
// for the client message is the return code from a write with
// WriteReplyBufferToClient()
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CompleteUrcMessage( const TDesC8& aAtCmd,
                                         CATExtPluginBase* aPlugin )
    {
    TRACE_FUNC_ENTRY
    TATExtPluginEntry* foundEntry = FindInstanceFromPlugindata( aPlugin );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    if ( !foundEntry->iUrcMessage.Handle() )
        {
        TRACE_FUNC_EXIT
        return KErrBadHandle;
        }
    TInt retVal = WriteReplyBufferToClient( aAtCmd,
                                            EATExtReceiveUrcCmdParamBuf,
                                            foundEntry->iUrcMessage );
    foundEntry->iUrcMessage.Complete( retVal );
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Completes an AT command handling message. Also clears internal initialized
// command hanlder data; see ClearInitializedCmdHandlerData().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CompleteCommandMessage( CATExtPluginBase* aPlugin,
                                             TInt aError,
                                             TBool aErrorReply,
                                             TATExtensionReplyType aReplyType,
                                             TBool aMultiPart )
    {
    TRACE_FUNC_ENTRY
    // First check if aPlugin is set (the call comes from a plugin and not from
    // ATEXT) and if it is the instance is different than the currently
    // operating one.
    if ( aPlugin && iCmdData.iHandler && iCmdData.iHandler->iInstance!=aPlugin )
        {
        TRACE_FUNC_EXIT
        return KErrInUse;
        }
    if ( !iCmdData.iCmdMessage.Handle() )
        {
        TRACE_FUNC_EXIT
        return KErrBadHandle;
        }
    TBool startOfEditor = EFalse;
    if ( aReplyType == EReplyTypeEditor)
        {
        // If completion is for editor command then set iCmdData.iEditorHandler
        // for the first time only
        if ( !iCmdData.iEditorHandler )
            {
            iCmdData.iEditorHandler = FindInstanceFromPlugindata( aPlugin );
            iCmdData.iReplyExpected = ETrue;  // reply expected when first reply in editor mode
            startOfEditor = ETrue;
            }
        }
    else
        {
        // If completion was something else than EReplyTypeEditor then just end
        // editor mode (no need to check iEditorHandler)
        if ( iCmdData.iEditorHandler )
            {
            iCmdData.iReplyExpected = ETrue;  // reply expected when last reply in editor mode
            }
        iCmdData.iEditorHandler = NULL;
        }
    // Next check if aPlugin is set (the call comes from a plugin and not from
    // ATEXT) and a reply is not needed. In this case do nothing as it is wrong
    // behavior from the plugin (a plugin must not complete messages where no
    // reply is expected; this is done by ATEXT)
    if ( aPlugin && !iCmdData.iReplyExpected && !iCmdData.iEditorHandler )
        {
        TRACE_FUNC_EXIT
        return KErrAlreadyExists;
        }
    // Finally write the data and complete the message
    TPckg<TATExtensionReplyType> replyType( aReplyType );
    TInt writeError = iCmdData.iCmdMessage.Write( EATExtHandleCmdParamReplyType,
                                                  replyType );
    TRACE_INFO(( _L("Write returned %d"), writeError ));
    if ( aError==KErrNone && writeError==KErrNone )
        {
        if ( iCmdData.iHandler )  // Can be NULL when invoked from inside ATEXT
            {
            iCmdData.iOldHandler = iCmdData.iHandler->iInstance;
            }
        if ( !aPlugin )
            {
            CreateEmptyOrErrorBuffer( iCmdData.iCmdReplyBuffer, aErrorReply );
            }
        WriteHandleCmdReplyBuffer( aMultiPart, startOfEditor );
        }
    iCmdData.iCmdStarted = EFalse;
    iCmdData.iReplyExpected = EFalse;
    iCmdData.iCmdMessage.Complete( aError );
    ClearInitializedCmdHandlerData( aMultiPart );
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Writes multipart or single part reply buffer to client for handle.
// Used for creating a reply for HandleCommand().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::WriteHandleCmdReplyBuffer( TBool aMultiPart,
                                                TBool aStartOfEditor )
    {
    TRACE_FUNC_ENTRY
    if ( iCmdData.iEditorHandler && !aStartOfEditor )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    if ( aMultiPart )
        {
        WriteReplyBufferToClient( iCmdData.iCmdReplyBuffer,
                                  EATExtHandleCmdParamReply,
                                  iCmdData.iCmdMessage,
                                  ETrue,
                                  EATExtHandleCmdParamLength );
        }
    else
        {
        WriteReplyBufferToClient( iCmdData.iCmdReplyBuffer,
                                  EATExtHandleCmdParamReply,
                                  iCmdData.iCmdMessage );
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Clears internal initialized command handler data. This is currently used
// only by CompleteCommandMessage() and is called when the data is not needed
// anymore. It also prepares the internal data for a new HandleCommand() call.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::ClearInitializedCmdHandlerData( TBool aMultiPart )
    {
    TRACE_FUNC_ENTRY
    if ( iCmdData.iCmdStarted )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    iCmdData.iCmdBuffer.Close();
    iCmdData.iCmdReplyBuffer.Close();
    if ( !aMultiPart )
        {
        iCmdData.iHandler = NULL;
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Gets the array of supported commands.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::GetSupportedCommands( CATExtPluginBase* aPlugin,
                                           RPointerArray<HBufC8>& aCmds )
    {
    TRACE_FUNC_ENTRY
    if ( !iSupport || !iSupportAux )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TATExtPluginEntry* foundEntry = FindInstanceFromPlugindata( aPlugin );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    // Next create the array of supported commands
    TInt retVal = KErrNone;
    TBool firstSearch = ETrue;
    TInt supportFind = KErrNotFound;
    TBool findInAux = EFalse;
    for ( ;; )
        {
        HBufC8* foundCmd = GetNextSupportedCommand( firstSearch,
                                                    supportFind,
                                                    findInAux );
        if ( !foundCmd )
            {
            break;
            }
        retVal = aCmds.Append( foundCmd );
        if ( retVal != KErrNone )
            {
            break;
            }
        }
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Starts URC message receiving for plugin. Note that
// MarkUrcHandlingOwnership() must be called immediately after this in order
// for the messages to receive their destination.
// ---------------------------------------------------------------------------
//
void CATExtMetadata::StartUrcReceivingL( const RMessage2& aMessage,
                                         TUid& aPluginUid )
    {
    TRACE_FUNC_ENTRY
    TATExtPluginEntry* foundEntry = FindUrcProcessingPlugin( aPluginUid );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrNotFound );
        }
    // Note: Let locked plugin pass here
    InstantiatePluginL( *foundEntry );
    foundEntry->iUrcMessage = aMessage;
    foundEntry->iInstance->ReceiveUnsolicitedResult();
    foundEntry->iUrcStarted = ETrue;
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Cancels an active URC message receiving operation by a given plugin UID.
// Uses CancelOneUrcOperation() to cancel if plugin UID found from plugin
// data.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CancelUrcReceiving( TUid& aPluginUid )
    {
    TRACE_FUNC_ENTRY
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    CancelOneUrcOperation( *foundEntry, KErrCancel, ETrue );
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Marks URC handling ownership for a plugin entry. Call to this function must
// be done immediately after the call to StartUrcReceivingL().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::MarkUrcHandlingOwnership( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt i = 0;
    TInt count = iPluginData->Count();
    for ( i=0; i<count; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        if ( entry.iUrcStarted && !entry.iUrcOwned )
            {
            TPckg<TUid> pluginUid( entry.iPluginUid );
            TInt retTemp = aMessage.Write( EATExtMarkUrcHandlingOwnershipParamUid,
                                           pluginUid );
            TRACE_INFO(( _L("Write returned %d"), retTemp ));
            entry.iUrcOwned = ETrue;
            TRACE_FUNC_EXIT
            return KErrNone;
            }
        }
    TRACE_FUNC_EXIT
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Marks the access to a plugin data as "locked". This blocks all operations
// where plugin function calls are to be done.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::LockPluginAccess( TUid& aPluginUid )
    {
    TRACE_FUNC_ENTRY
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    if ( foundEntry == iCmdData.iHandler )
        {
        CancelCommandOperation( KErrCancel );
        }
    CancelOneUrcOperation( *foundEntry, KErrCancel );
    foundEntry->iLocked = ETrue;
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Marks the access to a plugin data as "unlocked". This enables all
// operations where plugin function call are to be done.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::UnlockPluginAccess( TUid& aPluginUid )
    {
    TRACE_FUNC_ENTRY
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    foundEntry->iLocked = EFalse;
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Searches for an instances from the support data's plugin data link and only
// marks the instance as uninitialized. Note that this doesn't try to cancel
// any of the current plugin operations and should be used only when a plugin
// destroys itself.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::RemoveImplementationInstance( CATExtPluginBase* aInstance )
    {
    TRACE_FUNC_ENTRY
    if ( !aInstance )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TATExtPluginEntry* foundEntry = FindInstanceFromPlugindata( aInstance );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    TInt retVal = RemoveImplementation( foundEntry->iPluginUid, EFalse );
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Checks if support data has been constructed from the plugin data.
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::SupportExists()
    {
    TRACE_FUNC_ENTRY
    if ( (iSupport&&iSupport->Count()>0) ||
         (iSupportAux&&iSupportAux->Count()>0) )
        {
        TRACE_FUNC_EXIT
        return ETrue;
        }
    TRACE_FUNC_EXIT
    return EFalse;
    }

// ---------------------------------------------------------------------------
// Number of plugins with AT command handling support based on the information
// in RSS files' AT command handling entries. This information is needed to
// instantiate one or more listeners by the user of the client.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::NumberOfPlugins()
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt pluginCount = iPluginData->Count();
    TRACE_FUNC_EXIT
    return pluginCount;
    }

// ---------------------------------------------------------------------------
// Sets the quiet mode to the required value. After this the mode is reported
// to the plugins with ReportQuietModeChange().
// ---------------------------------------------------------------------------
//
void CATExtMetadata::SetQuietMode( TBool aMode )
    {
    TRACE_FUNC_ENTRY
    iQuietMode = aMode;
    if ( iPluginData )
        {
        TInt i;
        TInt count = iPluginData->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtPluginEntry& entry = (*iPluginData)[i];
            if ( entry.iLocked )
                {
                continue;
                }
            TInt retTemp = InstantiatePlugin( entry );
            if ( retTemp != KErrNone )
                {
                continue;
                }
            // Note: here two reports will be made if the plugin was not
            // instantiated but as the operatio is fast no new parameter will be
            // done to InstantiatePlugin() for instantiation reporting.
            entry.iInstance->ReportQuietModeChange( aMode );
            }
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Sets the verbose mode to the required value. After this the mode is
// reported to the plugins with ReportVerboseModeChange().
// ---------------------------------------------------------------------------
//
void CATExtMetadata::SetVerboseMode( TBool aMode )
    {
    TRACE_FUNC_ENTRY
    iVerboseMode = aMode;
    if ( iPluginData )
        {
        TInt i;
        TInt count = iPluginData->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtPluginEntry& entry = (*iPluginData)[i];
            if ( entry.iLocked )
                {
                continue;
                }
            TInt retTemp = InstantiatePlugin( entry );
            if ( retTemp != KErrNone )
                {
                continue;
                }
            // Note: here two reports will be made if the plugin was not
            // instantiated but as the operatio is fast no new parameter will be
            // done to InstantiatePlugin() for instantiation reporting.
            entry.iInstance->ReportVerboseModeChange( aMode );
            }
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Sets new character value for a carriage return, line feed or backspace
// character. After this its type and value are report to the plugins with
// ReportCharacterChange().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::SetCharacterValue( TATExtensionCharType aCharType,
                                        TInt8 aNewChar )
    {
    TRACE_FUNC_ENTRY
    // First pick up the needed information for ATEXT
    TInt retVal = KErrNone;
    switch ( aCharType )
        {
        case ECharTypeCarriage:
            iCarriageReturn = aNewChar;
            break;
        case ECharTypeLineFeed:
            iLineFeed = aNewChar;
            break;
        case ECharTypeBackspace:
            iBackspace = aNewChar;
            break;
        default:
            retVal = KErrNotSupported;
            break;
        }
    if ( retVal==KErrNone && iPluginData )
        {
        // Next notify about character change
        TInt i;
        TInt count = iPluginData->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtPluginEntry& entry = (*iPluginData)[i];
            if ( entry.iLocked )
                {
                continue;
                }
            TInt retTemp = InstantiatePlugin( entry );
            if ( retTemp != KErrNone )
                {
                continue;
                }
            // Note: here two reports will be made if the plugin was not
            // instantiated but as the operatio is fast no new parameter will be
            // done to InstantiatePlugin() for instantiation reporting.
            entry.iInstance->ReportCharacterChange( aCharType, aNewChar );
            }
        }
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Extracts the NVRAM settings from a pipe-character delimited NVRAM buffer
// and sends the subsettings to all of the plugins with
// ReportNvramStatusChange().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::BroadcastNvramStatusChange( const TDesC8& aNvram )
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    HBufC8* nvramEntry = NULL;
    TRAP_IGNORE( nvramEntry=HBufC8::NewMaxL(aNvram.Length()) );
    if ( !nvramEntry )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt i;
    TInt retVal = KErrNotFound;
    TInt count = iPluginData->Count();
    TPtr8 nvramEntryPtr = nvramEntry->Des();
    TInt startIndex = 0;   // Start of found subsetting
    TInt endIndex = 0;     // End of found subsetting
    while ( ExtractNextNvramSetting(aNvram,startIndex,endIndex)  )
        {
        // Report the subsetting to every plugin (and instantiate as every delta
        // change has to be signalled to every plugin).
        nvramEntryPtr.Copy( &aNvram[startIndex], endIndex-startIndex );
        for ( i=0; i<count; i++ )
            {
            TATExtPluginEntry& entry = (*iPluginData)[i];
            if ( entry.iLocked )
                {
                continue;
                }
            TInt retTemp = InstantiatePlugin( entry );
            if ( retTemp != KErrNone )
                {
                continue;
                }
            entry.iInstance->ReportNvramStatusChange( nvramEntryPtr );
            retVal = KErrNone;
            }
        }
    delete nvramEntry;
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Reports about external handle command error condition.
// This is for cases when for example DUN decided the reply contained an
// error condition but the plugin is still handling the command internally.
// Example: "AT+TEST;+TEST2" was given in command line; "AT+TEST" returns
// non-EReplyTypeError condition and "AT+TEST2" returns EReplyTypeError.
// As the plugin(s) returning the non-EReplyTypeError may still have some
// ongoing operation then these plugins are notified about the external
// EReplyTypeError in command line processing. It is to be noted that
// HandleCommandCancel() is not sufficient to stop the processing as the
// command handling has already finished.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::ReportExternalHandleCommandError()
    {
    TRACE_FUNC_ENTRY
    if ( !iCmdData.iOldHandler )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    iCmdData.iOldHandler->ReportExternalHandleCommandError();
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Reports about abort condition in command handling.
// This is for cases when for example DUN decided an abort condition was
// received from DTE (ITU-T V.250 5.6.1). This API is for notifying the
// plugin that abort was requested. However the plugin currently handling
// the command may ignore the request if it doesn't support abort for the
// command or it may return the changed condition with
// HandleCommandCompleted()
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::ReportHandleCommandAbort( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    if ( !iCmdData.iHandler || !iCmdData.iHandler->iInstance )
        {
        TRACE_FUNC_EXIT
        return KErrNotReady;
        }
    // Uncomment the following line for abort API
//    CATExtPluginBase& plugin = *iCmdData.iHandler->iInstance;
    // Remove the following line for abort API
    TInt retTemp = KErrNone;
    TBool stop = EFalse;
    // Uncomment the following line for abort API
//    TInt retTemp = plugin.ReportHandleCommandAbort( stop );
    if ( retTemp != KErrNone )
        {
        TRACE_FUNC_EXIT
        return retTemp;
        }
    TPckg<TBool> stopPckg( stop );
    TInt retVal = aMessage.Write( EATExtReportHandleCommandAbortParamStop,
                                  stopPckg );
    TRACE_INFO(( _L("Write returned %d"), retVal ));
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Extracts the NVRAM settings from a pipe-character delimited NVRAM buffer
// and sends the subsettings to all of the plugins with
// ReportNvramStatusChange().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::GetNextSpecialCommand( const RMessage2& aMessage,
                                            TBool aFirstSearch )
    {
    TRACE_FUNC_ENTRY
    if ( !iSupportAux )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    // If first search then start from index 0, othewise start from the
    // next index
    if ( aFirstSearch )
        {
        iSupportAuxFind = -1;
        }
    iSupportAuxFind++;
    TInt i;
    TInt count = iSupportAux->Count();
    TATExtAtCmdSupport* support = NULL;
    for ( i=iSupportAuxFind; i<count; i++ )
        {
        support = &(*iSupportAux)[i];
        if ( support->iEntries )
            {
            break;
            }
        }
    if ( i >= count )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    TInt retTemp = WriteReplyBufferToClient( *support->iAtCmdBase,
                                             EATExtGetNextSpecialCmdParamCmd,
                                             aMessage );
    TRACE_INFO(( _L("First write returned %d"), retTemp ));
    TPckg<TBool> firstSearch( EFalse );
    retTemp = aMessage.Write( EATExtGetNextSpecialCmdParamFirst, firstSearch );
    TRACE_INFO(( _L("Second write returned %d"), retTemp ));
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CATExtMetadata::CATExtMetadata
// ---------------------------------------------------------------------------
//
CATExtMetadata::CATExtMetadata( REComSession& aEComSession,
                                CATExtListen* aListener,
                                MATExtPluginObserver& aObserver ) :
    iEComSession( aEComSession ),
    iListener( aListener ),
    iObserver( aObserver )
    {
    Initialize();
    }

// ---------------------------------------------------------------------------
// CATExtMetadata::ConstructL
// ---------------------------------------------------------------------------
//
void CATExtMetadata::ConstructL()
    {
    if ( !iListener )
        {
        User::Leave( KErrGeneral );
        }
    }

// ---------------------------------------------------------------------------
// Initializes this class
// ---------------------------------------------------------------------------
//
void CATExtMetadata::Initialize()
    {
    // Don't initialize iListener here (it is set through NewL)
    // Don't initialize iCallback here (it is set through NewL)
    iIfUid = TUid::Null();
    iSupport = NULL;
    iSupportAux = NULL;
    iPluginData = NULL;
    iCmdData.iCmdStarted = EFalse;
    iCmdData.iReplyExpected = EFalse;
    iCmdData.iHandler = NULL;
    iCmdData.iOldHandler = NULL;
    iCmdData.iEditorHandler = NULL;
    iCarriageReturn = KDefaultCarriageReturn;
    iLineFeed = KDefaultLineFeed;
    iQuietMode = EFalse;
    iVerboseMode = ETrue;
    iSupportAuxFind = 0;
    iShutdown = EFalse;
    }

// ---------------------------------------------------------------------------
// Removes one plugin data entry by plugin UID
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::RemoveOnePlugindata( TUid& aPluginUid,
                                          TBool aInstanceExists )
    {
    TRACE_FUNC_ENTRY
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    if ( !aInstanceExists )
        {
        foundEntry->iInstance = NULL;
        }
    if ( foundEntry == iCmdData.iHandler )
        {
        CancelCommandOperation( KErrCompletion );
        }
    CancelOneUrcOperation( *foundEntry, KErrCompletion );
    delete foundEntry->iInstance;
    foundEntry->iInstance = NULL;
    iPluginData->Delete( foundIndex );
    iPluginData->Compress();
    UpdateRemovedPluginLinks( iSupport, foundIndex );
    UpdateRemovedPluginLinks( iSupportAux, foundIndex );
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Updates removed plugin links
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::UpdateRemovedPluginLinks(
    CArrayFixFlat<TATExtAtCmdSupport>* aSupport,
    TInt aRemovedIndex )
    {
    TRACE_FUNC_ENTRY
    if ( !aSupport )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt i;
    TInt j;
    TInt supportCount = aSupport->Count();
    for ( i=0; i<supportCount; i++ )
        {
        TATExtAtCmdSupport& support = (*aSupport)[i];
        if ( !support.iEntries )
            {
            continue;
            }
        TInt entryCount = support.iEntries->Count();
        for ( j=entryCount-1; j>=0; j-- )
            {
            TATExtOneCmdSupport& oneCmdSupport = (*support.iEntries)[j];
            TInt entryIndex = oneCmdSupport.iEntryIndex;
            if ( entryIndex > aRemovedIndex )
                {
                oneCmdSupport.iEntryIndex--;
                }
            else if ( entryIndex == aRemovedIndex )
                {
                support.iEntries->Delete( j );
                }
            }
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Completes command data
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CompleteCommandData()
    {
    TRACE_FUNC_ENTRY
    CompleteCommandMessage( NULL,
                            KErrCompletion,
                            EFalse,
                            EReplyTypeUndefined,
                            EFalse );
    // It is possible that CompleteCommandMessage() didn't complete but there
    // is still data set to non-null values. Force setting now.
    iCmdData.iCmdStarted = EFalse;
    iCmdData.iReplyExpected = EFalse;
    iCmdData.iCmdBuffer.Close();
    iCmdData.iCmdReplyBuffer.Close();
    iCmdData.iHandler = NULL;
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Adds AT commands set to metadata. Used by function AddImplementationL().
// ---------------------------------------------------------------------------
//
void CATExtMetadata::DoAddImplementationL(
    CImplementationInformation* aImplInfo,
    const TDesC8& aAtCmdSet )
    {
    TRACE_FUNC_ENTRY
    if ( !aImplInfo || !iPluginData || !iSupport || !iSupportAux )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    TATExtSupportType type = ESupportTypeUndefined;
    TInt start = 0;  // Start of found subcommand
    TInt end = 0;    // End of found subcommand
    HBufC8* parsedCmd = HBufC8::NewMaxLC( aAtCmdSet.Length() );
    TPtr8 parsedCmdPtr = parsedCmd->Des();
    while ( ExtractNextCommand(aAtCmdSet,start,end,type) == KErrNone )
        {
        if ( type == ESupportTypeUndefined )
            {
            continue;
            }
        parsedCmdPtr.Copy( &aAtCmdSet[start], end-start );
        TUid pluginUid = aImplInfo->ImplementationUid();
        TATExtCleanupInfo cleanupInfo;
        TRAPD( retTrap, AddEntryToMetadataL(parsedCmdPtr,pluginUid,type,
                                            cleanupInfo) );
        if ( retTrap != KErrNone )
            {
            CleanPartialMetadata( cleanupInfo );
            TRACE_FUNC_EXIT
            User::Leave( retTrap );
            }
        TInt retTemp = iListener->AddPluginUid( pluginUid );
        if ( retTemp != KErrNone )
            {
            TRACE_FUNC_EXIT
            User::Leave( retTemp );
            }
        }
    CleanupStack::PopAndDestroy( parsedCmd );
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Removes an implementation from the support data by a given plugin UID.
// Used by function RemoveImplementation().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::DoRemoveImplementation(
    TUid& aPluginUid,
    CArrayFixFlat<TATExtAtCmdSupport>* aSupport )
    {
    TRACE_FUNC_ENTRY
    if ( !aSupport )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    // 1. Find the UID from plugindata.
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    // 2. Delete the entries from metadata which point to the found entry.
    TInt i;
    TInt j;
    TInt supportCount = aSupport->Count();
    for ( i=supportCount-1; i>=0; i-- )
        {
        TATExtAtCmdSupport& support = (*aSupport)[i];
        if ( !support.iEntries )
            {
            continue;
            }
        TInt entryCount = support.iEntries->Count();
        for ( j=entryCount-1; j>=0; j-- )
            {
            TInt entryIndex = (*support.iEntries)[j].iEntryIndex;
            if ( &(*iPluginData)[entryIndex] == foundEntry )
                {
                support.iEntries->Delete( j );
                }
            }
        support.iEntries->Compress();
        // 3. Remove the entries from metadata with zero length.
        if ( support.iEntries->Count() == 0 )
            {
            aSupport->Delete( i );
            }
        }
    aSupport->Compress();
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Checks whether AT command handling is active or not
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::IsCommandHandling()
    {
    TRACE_FUNC_ENTRY
    if ( iCmdData.iCmdStarted ||
        (iCmdData.iCmdMessage.Handle() && iCmdData.iCmdBuffer.Length()>0) ||
         iCmdData.iCmdReplyBuffer.Length()>0 )
        {
        TRACE_FUNC_EXIT
        return ETrue;
        }
    TRACE_FUNC_EXIT
    return EFalse;
    }

// ---------------------------------------------------------------------------
// Destroys data related to plugins
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::DestroyPlugindata()
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    CancelCommandOperation( KErrServerTerminated );
    // First cancel the plugindata operations before delete
    TInt i;
    TInt pluginCount = iPluginData->Count();
    for ( i=0; i<pluginCount; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        CancelOneUrcOperation( entry, KErrServerTerminated );
        }
    // Next delete the actual data
    for ( i=0; i<pluginCount; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        delete entry.iInstance;
        entry.iInstance = NULL;
        }
    iPluginData->Reset();
    delete iPluginData;
    iPluginData = NULL;
    iCmdData.iHandler = NULL;
    iCmdData.iOldHandler = NULL;
    iCmdData.iEditorHandler = NULL;
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Cancels an active AT command handling operation by reinitializing internal
// data, completing the client request message and calling the plugin's
// HandleCommandCancel().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CancelCommandOperation( TInt aError, TBool aCheckLock )
    {
    TRACE_FUNC_ENTRY
    if ( aCheckLock && iCmdData.iHandler && iCmdData.iHandler->iLocked )
        {
        TRACE_FUNC_EXIT
        return KErrAccessDenied;
        }
    if ( iCmdData.iHandler && iCmdData.iHandler->iInstance )
        {
        iCmdData.iHandler->iInstance->HandleCommandCancel();
        iCmdData.iEditorHandler = NULL;
        }
    CompleteCommandMessage( NULL,
                            aError,
                            EFalse,
                            EReplyTypeUndefined,
                            EFalse );
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Cancels an active URC message receiving operation by a given plugin entry,
// completing the client request message and calling the plugin's
// ReceiveUnsolicitedResultCancel().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CancelOneUrcOperation( TATExtPluginEntry& aPluginEntry,
                                            TInt aError,
                                            TBool aCheckLock )
    {
    TRACE_FUNC_ENTRY
    if ( aCheckLock && aPluginEntry.iLocked )
        {
        TRACE_FUNC_EXIT
        return KErrAccessDenied;
        }
    aPluginEntry.iUrcStarted = EFalse;
    if ( aPluginEntry.iInstance )
        {
        aPluginEntry.iInstance->ReceiveUnsolicitedResultCancel();
        }
    if ( aPluginEntry.iUrcMessage.Handle() )
        {
        aPluginEntry.iUrcMessage.Complete( aError );
        }
    // Note: Don't touch the iUrcOwned here
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Destroys data related to AT command support
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::DestroySupportdata(
    CArrayFixFlat<TATExtAtCmdSupport>*& aSupport )
    {
    TRACE_FUNC_ENTRY
    if ( !aSupport )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    // Next just remove the entries
    TInt i;
    TInt supportCount = aSupport->Count();
    for ( i=0; i<supportCount; i++ )
        {
        TATExtAtCmdSupport& support = (*aSupport)[i];
        delete support.iAtCmdBase;
        support.iAtCmdBase = NULL;
        if ( support.iEntries )
            {
            support.iEntries->Reset();
            delete support.iEntries;
            support.iEntries = NULL;
            }
        }
    aSupport->Reset();
    delete aSupport;
    aSupport = NULL;
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Extracts one command from a pipe-character delimited command buffer (Note:
// This function is used to get the settings from a plugin RSS file)
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::ExtractNextCommand( const TDesC8& aCommands,
                                         TInt& aStartIndex,
                                         TInt& aEndIndex,
                                         TATExtSupportType& aSupportType )
    {
    TRACE_FUNC_ENTRY
    aSupportType = ESupportTypeUndefined;
    // Skip data before command
    TInt i;
    TChar character;
    TInt count = aCommands.Length();
    for ( i=aEndIndex; i<count; i++ )
        {
        character = aCommands[i];
        if ( character.IsPrint() && character!='|' )
            {
            break;
            }
        }
    if ( i >= count )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    // Now the plugin type marker is found; check the plugin type
    switch ( character )
        {
        case 'M':  // Master plugin
        case 'm':
            aSupportType = ESupportTypeMaster;
            break;
        case 'P':  // Partial plugin
        case 'p':
            aSupportType = ESupportTypePartial;
            break;
        case 'O':  // Observer plugin
        case 'o':
            aSupportType = ESupportTypeObserver;
            break;
        default:
            _LIT( KPluginType, "PluginType" );
            User::Panic( KPluginType, EPanicCategoryType );
        }
    i++;
    if ( i >= count )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    aStartIndex = i;
    // Start of command found, next mark the end
    while ( i < count )
        {
        character = aCommands[i];
        if ( !character.IsPrint() || character=='|' )
            {
            break;
            }
        i++;
        }
    aEndIndex = i;
    if ( aEndIndex-aStartIndex <= 0 )
        {
        TRACE_FUNC_EXIT
        return KErrNotFound;
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Adds a new plugin entry to support data. If command to be added is not
// found from the support data then that command is inserted alphabetically to
// the support data. After this a search for the plugin UID is done; if the
// UID is not found then it is added to the plugin data and a new initialized
// data is created for that entry. Finally the added or new plugin entry is
// linked to the new or existing support entry.
// ---------------------------------------------------------------------------
//
void CATExtMetadata::AddEntryToMetadataL( TDes8& aAtCmdBase,
                                          TUid& aPluginUid,
                                          TATExtSupportType aSupportType,
                                          TATExtCleanupInfo& aCleanupInfo )
    {
    TRACE_FUNC_ENTRY
    if ( !iSupport || !iSupportAux || !iPluginData )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    // First try to find the string with binary search.
    // If found, add aUid.
    // If not found, add aAtCmdBase, aPluginUid.
    CArrayFixFlat<TATExtAtCmdSupport>* support = iSupport;
    aCleanupInfo.iSupportCreated = EFalse;
    aCleanupInfo.iSupportIndex = KErrNotFound;
    aCleanupInfo.iEntryCreated = EFalse;
    aCleanupInfo.iEntryIndex = KErrNotFound;
    aCleanupInfo.iSupport = iSupport;
    TBool findAux = EFalse;
    TInt strLength = aAtCmdBase.Length();
    if ( strLength>=3 && aAtCmdBase[strLength-1]=='*' )  // 3 for "AT*"
        {
        aAtCmdBase.SetLength( strLength-1 );
        aCleanupInfo.iSupport = iSupportAux;
        support = iSupportAux;
        findAux = ETrue;
        }
    TInt foundPos = KErrNotFound;
    TBool found = FindCommandFromMetadata( aAtCmdBase, foundPos, findAux, EFalse );
    if ( !found )
        {
        // Not found. foundPos is the position where to add the new entry
        TATExtAtCmdSupport newEntry;
        foundPos = -foundPos;  // convert to positive
        newEntry.iAtCmdBase = HBufC8::NewMaxLC( aAtCmdBase.Length() );
        newEntry.iEntries = NULL;
        TPtr8 atCmdPtr = newEntry.iAtCmdBase->Des();
        atCmdPtr.Copy( aAtCmdBase );
        support->InsertL( foundPos, newEntry );
        CleanupStack::Pop( newEntry.iAtCmdBase );
        aCleanupInfo.iSupportCreated = ETrue;
        }
    aCleanupInfo.iSupportIndex = foundPos;
    // Next try to find the plugin UID from plugin data.
    TInt foundIndex = KErrNotFound;
    TATExtPluginEntry* foundEntry = FindUidFromPlugindata( aPluginUid,
                                                           foundIndex );
    if ( !foundEntry )
        {
        // Not found. Append new initialized entry.
        TATExtPluginEntry pluginEntry;
        pluginEntry.iPluginUid = aPluginUid;
        pluginEntry.iInstance = NULL;
        pluginEntry.iLocked = EFalse;
        pluginEntry.iUrcStarted = EFalse;
        pluginEntry.iUrcOwned = EFalse;
        iPluginData->AppendL( pluginEntry );
        aCleanupInfo.iEntryCreated = ETrue;
        aCleanupInfo.iEntryIndex = iPluginData->Count() - 1;
        foundIndex = aCleanupInfo.iEntryIndex;
        foundEntry = &(*iPluginData)[foundIndex];
        }
    // Now foundEntry is either the found entry or a newly added entry.
    // Make the iSupport metadata point to this.
    TATExtAtCmdSupport& foundSupport = (*support)[foundPos];
#if defined( PRJ_OPTIMIZE_FOR_SPEED )
    AddNewMetadataEntryLinkL( foundSupport.iEntries,
                              foundIndex,
                              aSupportType,
                              foundSupport.iSearchHelper );
#else  // PRJ_OPTIMIZE_FOR_SPEED
    TATExtSearchHelper searchHelper;
    AddNewMetadataEntryLinkL( foundSupport.iEntries,
                              foundIndex,
                              aSupportType,
                              searchHelper );
#endif  // PRJ_OPTIMIZE_FOR_SPEED
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Adds new plugin entry link from plugin support entry to plugin entry
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::AddNewMetadataEntryLinkL(
    CArrayFixFlat<TATExtOneCmdSupport>*& aEntries,
    TInt aEntryIndex,
    TATExtSupportType aSupportType,
    TATExtSearchHelper& aSearchHelper )
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData || aEntryIndex<0 || aEntryIndex>=iPluginData->Count() )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    if ( !aEntries )
        {
        aEntries = new (ELeave) CArrayFixFlat<TATExtOneCmdSupport>( KGranularity );
        }
    TATExtOneCmdSupport oneCmdSupport;
    oneCmdSupport.iSupportType = aSupportType;
    oneCmdSupport.iEntryIndex = aEntryIndex;
    TInt retVal = KErrNotSupported;
    switch ( aSupportType )
        {
        case ESupportTypeMaster:
            {
            retVal = AddNewMasterMetadataEntryLinkL( aEntries,
                                                     aSearchHelper,
                                                     oneCmdSupport );
            }
            break;
        case ESupportTypePartial:
            {
            retVal = AddNewPartialMetadataEntryLinkL( aEntries,
                                                      aSearchHelper,
                                                      oneCmdSupport );
            }
            break;
        case ESupportTypeObserver:
            {
            retVal = AddNewObserverMetadataEntryLinkL( aEntries,
                                                       aSearchHelper,
                                                       oneCmdSupport );
            }
            break;
        default:
            {
            User::Leave( KErrNotSupported );
            }
        }
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Adds new master plugin entry link from plugin support entry to plugin entry
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::AddNewMasterMetadataEntryLinkL(
    CArrayFixFlat<TATExtOneCmdSupport>* aEntries,
    TATExtSearchHelper& aSearchHelper,
    TATExtOneCmdSupport& aOneCmdSupport )
    {
    TRACE_FUNC_ENTRY
    if ( aEntries->Count() > 0 )
        {
        TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[0];
        if ( oneCmdSupport.iSupportType == ESupportTypePartial )
            {
            _LIT( KAddMIsP, "AddMIsP" );
            User::Panic( KAddMIsP, EPanicCategoryAddMIsP );
            }
        if ( oneCmdSupport.iSupportType == ESupportTypeMaster )
            {
            _LIT( KAddMIsM, "AddMIsM" );
            User::Panic( KAddMIsM, EPanicCategoryAddMIsM );
            }
        }
    aEntries->InsertL( 0, aOneCmdSupport );
    if ( aSearchHelper.iPartialIndex >= 0 )
       {
        aSearchHelper.iPartialIndex++;
        }
    if ( aSearchHelper.iObserverIndex >= 0 )
        {
        aSearchHelper.iObserverIndex++;
        }
    TRACE_FUNC_EXIT
    return 0;
    }

// ---------------------------------------------------------------------------
// Adds new partial plugin entry link from plugin support entry to plugin
// entry
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::AddNewPartialMetadataEntryLinkL(
    CArrayFixFlat<TATExtOneCmdSupport>* aEntries,
    TATExtSearchHelper& aSearchHelper,
    TATExtOneCmdSupport& aOneCmdSupport )
    {
    TRACE_FUNC_ENTRY
    if ( aEntries->Count() > 0 )
        {
        TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[0];
        if ( oneCmdSupport.iSupportType == ESupportTypeMaster )
            {
            _LIT( KAddPIsM, "AddPIsM" );
            User::Panic( KAddPIsM, EPanicCategoryAddPIsM );
            }
        }
    TInt i = aSearchHelper.iPartialIndex;
    if ( i < 0 )
        {
        TInt count = aEntries->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[i];
            if ( oneCmdSupport.iSupportType==ESupportTypePartial ||
                 oneCmdSupport.iSupportType==ESupportTypeObserver )
                {
                break;
                }
            }
        aSearchHelper.iPartialIndex = i;
        }
    aEntries->InsertL( i, aOneCmdSupport );
    if ( aSearchHelper.iObserverIndex >= 0 )
        {
        aSearchHelper.iObserverIndex++;
        }
    TRACE_FUNC_EXIT
    return i;
    }

// ---------------------------------------------------------------------------
// Adds new observer plugin entry link from plugin support entry to plugin
// entry. Search starts from the front as there could be multiple O plugins
// but only one or two M/P plugins.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::AddNewObserverMetadataEntryLinkL(
    CArrayFixFlat<TATExtOneCmdSupport>* aEntries,
    TATExtSearchHelper& aSearchHelper,
    TATExtOneCmdSupport& aOneCmdSupport )
    {
    TRACE_FUNC_ENTRY
    TInt i = aSearchHelper.iObserverIndex;
    if ( i < 0 )
        {
        TInt count = aEntries->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[i];
            if ( oneCmdSupport.iSupportType == ESupportTypeObserver )
                {
                break;
                }
            }
        aSearchHelper.iObserverIndex = i;
        }
    aEntries->InsertL( i, aOneCmdSupport );
    TRACE_FUNC_EXIT
    return i;
    }

// ---------------------------------------------------------------------------
// Cleans partial created metadata based on TATExtCleanupInfo
// ---------------------------------------------------------------------------
//
void CATExtMetadata::CleanPartialMetadata( TATExtCleanupInfo& aCleanupInfo )
    {
    TRACE_FUNC_ENTRY
    TBool withinEntryLimits = EFalse;
    TBool withinSupportLimits = EFalse;
    CArrayFixFlat<TATExtAtCmdSupport>* support = aCleanupInfo.iSupport;
    if ( aCleanupInfo.iEntryIndex>=0 &&
         aCleanupInfo.iEntryIndex<support->Count() )
        {
        withinEntryLimits = ETrue;
        }
    if ( aCleanupInfo.iSupportIndex>=0 &&
         aCleanupInfo.iSupportIndex<support->Count() )
        {
        withinSupportLimits = ETrue;
        }
    if ( aCleanupInfo.iEntryCreated && withinEntryLimits )
        {
        iPluginData->Delete( aCleanupInfo.iEntryIndex );
        }
    if ( aCleanupInfo.iSupportCreated && withinSupportLimits )
        {
        TInt deleteIndex = aCleanupInfo.iSupportIndex;
        TATExtAtCmdSupport& supportData = (*support)[deleteIndex];
        delete supportData.iAtCmdBase;
        supportData.iAtCmdBase = NULL;
        delete supportData.iEntries;
        supportData.iEntries = NULL;
        support->Delete( deleteIndex );
        support->Compress();
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Prints the AT command plugin entries and information about them.
// For debugging/bug hunting only
// ---------------------------------------------------------------------------
//
#if defined(_DEBUG) && defined( PRJ_PRINT_SUPPORT_DATA )
TInt CATExtMetadata::PrintSupportData(
    CArrayFixFlat<TATExtAtCmdSupport>* aSupport )
    {
    TRACE_FUNC_ENTRY
    if ( !aSupport )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt i;
    TInt j;
    TInt supportCount = aSupport->Count();
    for ( i=0; i<supportCount; i++ )
        {
        TATExtAtCmdSupport& support = (*aSupport)[i];
        if ( !support.iEntries )
            {
            continue;
            }
        TInt entryCount = support.iEntries->Count();
        TRACE_INFO(( _L8("Support entry at index %d = '%S' (%d entries):"), i, &(*support.iAtCmdBase), entryCount ));
        for ( j=0; j<entryCount; j++ )
            {
            TATExtOneCmdSupport& oneCmdSupport = (*support.iEntries)[j];
            TInt entryIndex = oneCmdSupport.iEntryIndex;
            TRACE_INFO(( _L8("\tPlugin entry at index %d = Type:%d, UID:0x%08X"), j, oneCmdSupport.iSupportType, (*iPluginData)[entryIndex].iPluginUid ));
            }
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }
#endif

// ---------------------------------------------------------------------------
// Prints the found AT command plugin entries and information about them.
// For debugging/bug hunting only
// ---------------------------------------------------------------------------
//
#if defined(_DEBUG) && defined( PRJ_PRINT_SUPPORT_DATA )
TInt CATExtMetadata::PrintFoundEntries(
    CArrayFixFlat<TATExtOneCmdSupport>* aEntries )
    {
    TRACE_FUNC_ENTRY
    if ( !aEntries )
        {
        TRACE_FUNC_EXIT
        return KErrGeneral;
        }
    TInt i;
    TInt count = aEntries->Count();
    for ( i=0; i<count; i++ )
        {
        TATExtOneCmdSupport& entry = (*aEntries)[i];
        TInt entryIndex = entry.iEntryIndex;
        TRACE_INFO(( _L8("\tFound entry at index %d = Type:%d, UID:0x%08X"), i, entry.iSupportType, (*iPluginData)[entryIndex].iPluginUid ));
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }
#endif

// ---------------------------------------------------------------------------
// Does a binary search for an AT command to find the AT command from the
// support data
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::FindCommandFromMetadata( TDesC8& aAtCmdBase,
                                               TInt& aFoundIndex,
                                               TBool aFindAuxCmd,
                                               TBool aCheckAsterisk )
    {
    TRACE_FUNC_ENTRY
    if ( !iSupport || !iSupportAux || (!aFindAuxCmd&&aCheckAsterisk) )
        {
        TRACE_FUNC_EXIT
        return EFalse;
        }
    TInt result = 0;
    TInt middle = 0;
    TInt left = 0;
    CArrayFixFlat<TATExtAtCmdSupport>* support = iSupport;
    if ( aFindAuxCmd )
        {
        support = iSupportAux;
        }
    TInt right = support->Count() - 1;
    while ( left <= right )
        {
        middle = ( left + right ) / 2;
        HBufC8& compareStr = *(*support)[middle].iAtCmdBase;
        TInt strLength = compareStr.Length();
        if ( aCheckAsterisk )
            {
            TPtrC8 leftPart = aAtCmdBase.Left( strLength );
            result = compareStr.Compare( leftPart );
            }
        else
            {
            result = compareStr.Compare( aAtCmdBase );
            }
        if ( result == 0 )  // iSupport == aAtCmd
            {
            aFoundIndex = middle;
            TRACE_FUNC_EXIT
            return ETrue;
            }
        else if ( result < 0 )  // iSupport < aAtCmd
            {
            left = middle + 1;
            }
        else  // iSupport > aAtCmd
            {
            right = middle - 1;
            }
        }
    aFoundIndex = -left;
    TRACE_FUNC_EXIT
    return EFalse;
    }

// ---------------------------------------------------------------------------
// Finds the first plugin entry from a given plugin UID
// ---------------------------------------------------------------------------
//
TATExtPluginEntry* CATExtMetadata::FindUidFromPlugindata( TUid& aUid,
                                                          TInt& aFoundIndex )
    {
    TRACE_FUNC_ENTRY
    aFoundIndex = KErrNotFound;
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return NULL;
        }
    TInt i;
    TInt count = iPluginData->Count();
    for ( i=0; i<count; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        if ( entry.iPluginUid == aUid )
            {
            TRACE_FUNC_EXIT
            aFoundIndex = i;
            return &entry;
            }
        }
    TRACE_FUNC_EXIT
    return NULL;
    }

// ---------------------------------------------------------------------------
// Find the first plugin entry from a given plugin instance
// ---------------------------------------------------------------------------
//
TATExtPluginEntry* CATExtMetadata::FindInstanceFromPlugindata(
    CATExtPluginBase* aInstance )
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return NULL;
        }
    TInt i;
    TInt count = iPluginData->Count();
    for ( i=0; i<count; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        if ( entry.iInstance == aInstance )
            {
            TRACE_FUNC_EXIT
            return &entry;
            }
        }
    TRACE_FUNC_EXIT
    return NULL;
    }

// ---------------------------------------------------------------------------
// Finds an Urc message processing plugin. Must be used in synchronization
// with MarkUrcHandlingOwnership(). More information in aPluginUid parameter's
// explanation below.
// ---------------------------------------------------------------------------
//
TATExtPluginEntry* CATExtMetadata::FindUrcProcessingPlugin(
    TUid& aPluginUid )
    {
    TRACE_FUNC_ENTRY
    if ( !iPluginData )
        {
        TRACE_FUNC_EXIT
        return NULL;
        }
    TInt i;
    TInt count = iPluginData->Count();
    for ( i=0; i<count; i++ )
        {
        TATExtPluginEntry& entry = (*iPluginData)[i];
        if ( entry.iUrcMessage.Handle() )
            {
            continue;
            }
        // second run (after MarkUrcHandlingOwnership())
        if ( aPluginUid != TUid::Null() )
            {
            if ( entry.iUrcOwned && entry.iPluginUid==aPluginUid )
                {
                TRACE_FUNC_EXIT
                return &entry;
                }
            }
        // first run (before MarkUrcHandlingOwnership())
        else if ( !entry.iUrcStarted && !entry.iUrcOwned )
            {
            TRACE_FUNC_EXIT
            return &entry;
            }
        }
    TRACE_FUNC_EXIT
    return NULL;
    }

// ---------------------------------------------------------------------------
// Extracts the base command from a given AT command to find support with
// DoCreateAndFindSupportL()
// ---------------------------------------------------------------------------
//
void CATExtMetadata::CreateAndFindSupportL(
    TDesC8& aAtCmdFull,
    const RMessage2& aMessage,
    TATExtCompletionInfo& aComplInfo )
    {
    TRACE_FUNC_ENTRY
    // First extract the base of AT command from aAtCmdFull
    // ('=' and everything after that)
    TInt newLength = aAtCmdFull.Length();
    TInt foundPos = aAtCmdFull.Locate( '=' );
    if ( foundPos >= 2 )  // After "AT"
        {
        newLength = foundPos;
        }
    else if ( newLength >= 1 )
        {
        // There was no '=' so check if the last character is '?'.
        // If it is then remove it.
        if ( aAtCmdFull[newLength-1] == '?' )
            {
            newLength--;
            }
        }
    HBufC8* baseCmd = HBufC8::NewMaxLC( newLength );
    TPtr8 baseCmdPtr = baseCmd->Des();
    baseCmdPtr.Copy( &aAtCmdFull[0], newLength );
    // Now the baseCmd has the base command. Use it to find the support.
    DoCreateAndFindSupportL( *baseCmd,
                             aAtCmdFull,
                             aMessage,
                             aComplInfo );
    CleanupStack::PopAndDestroy( baseCmd );
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Creates AT command support and finds the given AT command from the created
// support data
// ---------------------------------------------------------------------------
//
void CATExtMetadata::DoCreateAndFindSupportL(
    TDesC8& aAtCmdBase,
    TDesC8& aAtCmdFull,
    const RMessage2& aMessage,
    TATExtCompletionInfo& aComplInfo )
    {
    TRACE_FUNC_ENTRY
    // Note: don't set iCmdData.iOldHandler to NULL here as the sequence can
    // invoke this function again before ReportExternalHandleCommandError().
    // Get array of supported plugins for the base command
    CArrayFixFlat<TATExtOneCmdSupport>* support;
    support = FindEntriesForCommandLC( aAtCmdBase );
    if ( !support )
        {
        aComplInfo.iProcessed = EFalse;
        aComplInfo.iReplyExpected = ETrue;
        CreateSelfReplyData( aMessage );
        CleanupStack::PopAndDestroy();
        TRACE_FUNC_EXIT
        return;
        }
    TATExtEntrySupport entrySupport( aAtCmdFull,
                                     const_cast<RMessage2&>(aMessage),
                                     support );
    TInt i;
    aComplInfo.iProcessed = EFalse;
    TInt count = support->Count();
    for ( i=0; i<count; i++ )
        {
        TBool supported = EFalse;
        TATExtOneCmdSupport& oneCmdSupport = (*support)[i];
        entrySupport.iEntry = &(*iPluginData)[oneCmdSupport.iEntryIndex];
        if ( oneCmdSupport.iSupportType == ESupportTypeMaster )
            {
            supported = HandleMasterAndPartialPluginSupportL(
                entrySupport,
                i+1,
                aComplInfo.iReplyExpected );
            }
        else if ( oneCmdSupport.iSupportType == ESupportTypePartial )
            {
            supported = FindExclusivePartialSupportL( entrySupport );
            if ( !supported )
                {
                break;
                }
            supported = HandleMasterAndPartialPluginSupportL(
                entrySupport,
                i+1,
                aComplInfo.iReplyExpected );
            }
        else if ( oneCmdSupport.iSupportType == ESupportTypeObserver )
            {
            supported = HandleObserverPluginSupportL(
                entrySupport,
                i+1,
                aComplInfo.iReplyExpected );
            }
        if ( supported )
            {
            aComplInfo.iProcessed = ETrue;
            break;
            }
        }
    CleanupStack::PopAndDestroy( support );
    if ( !aComplInfo.iProcessed || !aComplInfo.iReplyExpected )
        {
        CreateSelfReplyData( aMessage );
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Prepares internal data for completion with "" or "ERROR" messages.
// More explation in CreateEmptyOrErrorBuffer().
// ---------------------------------------------------------------------------
//
void CATExtMetadata::CreateSelfReplyData( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    // "ERROR" or "" needed; initialize
    iCmdData.iCmdStarted = ETrue;
    iCmdData.iCmdMessage = aMessage;
    iCmdData.iHandler = NULL;
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Handles support when a master or partial plugin is detected in the plugin
// data via support data's link. If a partial or master plugin is detected
// then reply is expected from that plugin. Also if one or more observer
// plugins are detected then no reply is expected from them.
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::HandleMasterAndPartialPluginSupportL(
    TATExtEntrySupport& aEntrySupport,
    TInt aStartIndex,
    TBool& aReplyExpected )
    {
    TRACE_FUNC_ENTRY
    aReplyExpected = EFalse;
    if ( !aEntrySupport.iSupportFound )
        {
        TBool supported = HandleCommandSupportL( aEntrySupport );
        if ( !supported )
            {
            TRACE_FUNC_EXIT
            return EFalse;
            }
        }
    // If HandleCommand() is implemented synchronously, the command must be
    // saved before executing as CompleteCommandMessage() closes the string
    HBufC8* atCmdFull = HBufC8::NewMaxLC( aEntrySupport.iAtCmdFull.Length() );
    TPtr8 atCmdFullPtr = atCmdFull->Des();
    atCmdFullPtr.Copy( aEntrySupport.iAtCmdFull );
    // First send the entry to the observers
    TATExtEntrySupport handleCommandEntry = aEntrySupport;
    aEntrySupport.iStartIndex = aStartIndex;
    SendToMultipleObserverL( aEntrySupport, atCmdFull );
    aEntrySupport = handleCommandEntry;
    // Now execute the HandleCommand()
    iCmdData.iReplyExpected = ETrue;  // Set before HandleCommandL()
    HandleCommandL( aEntrySupport, ETrue );
    CleanupStack::PopAndDestroy( atCmdFull );
    aReplyExpected = ETrue;
    TRACE_FUNC_EXIT
    return ETrue;
    }

// ---------------------------------------------------------------------------
// Handles support when a observer plugin is detected in the plugin data via
// support data's link. If only one observer plugin is detected then reply is
// expected from that plugin. Instead, if more than one observer plugins are
// detected then no reply is expected from them.
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::HandleObserverPluginSupportL(
    TATExtEntrySupport& aEntrySupport,
    TInt aStartIndex,
    TBool& aReplyExpected )
    {
    TRACE_FUNC_ENTRY
    aReplyExpected = EFalse;
    if ( !aEntrySupport.iSupportFound )
        {
        TBool supported = HandleCommandSupportL( aEntrySupport );
        if ( !supported )
            {
            TRACE_FUNC_EXIT
            return EFalse;
            }
        }
    TATExtEntrySupport nextSupport = aEntrySupport;
    nextSupport.iStartIndex = aStartIndex;
    TBool entryFound = FindFirstObserverSupportL( nextSupport );
    if ( entryFound )
        {
        // Entry found; send all without reply request
        // If HandleCommand() is implemented synchronously, the command must be
        // saved before executing as CompleteCommandMessage() closes the string
        HBufC8* atCmdFull = HBufC8::NewMaxLC( aEntrySupport.iAtCmdFull.Length() );
        TPtr8 atCmdFullPtr = atCmdFull->Des();
        atCmdFullPtr.Copy( aEntrySupport.iAtCmdFull );
        // Now execute the HandleCommand()
        SendToMultipleObserverL( nextSupport, atCmdFull );
        HandleCommandL( aEntrySupport, EFalse );
        CleanupStack::PopAndDestroy( atCmdFull );
        }
    else
        {
        // No entry found; send one with reply request
        iCmdData.iReplyExpected = ETrue;  // Set before HandleCommandL()
        HandleCommandL( aEntrySupport, ETrue );
        aReplyExpected = ETrue;
        }
    TRACE_FUNC_EXIT
    return ETrue;
    }

// ---------------------------------------------------------------------------
// Finds exclusive partial plugin support
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::FindExclusivePartialSupportL(
    TATExtEntrySupport& aEntrySupport )
    {
    TRACE_FUNC_ENTRY
    TInt i;
    TInt supports;
    TInt count = aEntrySupport.iSupport->Count();
    for ( i=0,supports=0; i<count; i++ )
        {
        TATExtOneCmdSupport& oneCmdSupport = (*aEntrySupport.iSupport)[i];
        if ( oneCmdSupport.iSupportType == ESupportTypePartial )
            {
            aEntrySupport.iSupportFound = EFalse;
            aEntrySupport.iEntry = &(*iPluginData)[oneCmdSupport.iEntryIndex];
            TBool supported = HandleCommandSupportL( aEntrySupport );
            if ( supported )
                {
                aEntrySupport.iSupportFound = ETrue;
                supports++;
                }
            if ( supports != 1 )
                {
                TRACE_FUNC_EXIT
                return EFalse;
                }
            }
        }
    TRACE_FUNC_EXIT
    return ETrue;
    }

// ---------------------------------------------------------------------------
// Finds support entries from support data for a given base AT command
// ---------------------------------------------------------------------------
//
CArrayFixFlat<TATExtOneCmdSupport>* CATExtMetadata::FindEntriesForCommandLC(
    TDesC8& aAtCmdBase )
    {
    TRACE_FUNC_ENTRY
    TBool found;
    TInt foundPos;
    CArrayFixFlat<TATExtOneCmdSupport>* support =
        new (ELeave) CArrayFixFlat<TATExtOneCmdSupport>( KGranularity );
    CleanupStack::PushL( support );
    // First find normal data to which to add the aux entries
    found = FindCommandFromMetadata( aAtCmdBase, foundPos, EFalse, EFalse );
    if ( found )
        {
        CArrayFixFlat<TATExtOneCmdSupport>* entries =
            (*iSupport)[foundPos].iEntries;
        support->AppendL( &(*entries)[0], entries->Count() );
        }
    // Next add the aux links
    found = FindCommandFromMetadata( aAtCmdBase, foundPos, ETrue, ETrue );
    if ( found )
        {
        CArrayFixFlat<TATExtOneCmdSupport>* entries =
            (*iSupportAux)[foundPos].iEntries;
        TATExtSearchHelper searchHelper;
        TInt i;
        TInt count = entries->Count();
        for ( i=0; i<count; i++ )
            {
            TATExtOneCmdSupport& entry = (*entries)[i];
            AddNewMetadataEntryLinkL( support,
                                      entry.iEntryIndex,
                                      entry.iSupportType,
                                      searchHelper );
            }
        }
    // Now, as the normal data was inserted *before* the auxiliary data, the
    // auxiliary data is in front of the created "support" array for each M, P
    // and O entry. This insertion is faster than the other way around as there
    // can be multiple O plugins for the same command but usually much less
    // auxiliary entries for the same command.
#if defined(_DEBUG) && defined( PRJ_PRINT_SUPPORT_DATA )
    PrintFoundEntries( support );
#endif
    if ( support->Count() > 0 )
        {
        TRACE_FUNC_EXIT
        return support;
        }
    TRACE_FUNC_EXIT
    return NULL;
    }

// ---------------------------------------------------------------------------
// Instantiates plugin support
// ---------------------------------------------------------------------------
//
void CATExtMetadata::InstantiatePluginSupportL(
    TATExtEntrySupport& aEntrySupport )
    {
    TRACE_FUNC_ENTRY
    if ( aEntrySupport.iEntry )
        {
        InstantiatePluginL( *aEntrySupport.iEntry );
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Instantiates a plugin
// ---------------------------------------------------------------------------
//
void CATExtMetadata::InstantiatePluginL( TATExtPluginEntry& aPluginEntry )
    {
    TRACE_FUNC_ENTRY
    if ( !aPluginEntry.iInstance )
         {
         TUid pluginUid = aPluginEntry.iPluginUid;
         CATExtPluginBase* plugin = CATExtPluginBase::NewL( pluginUid,
                                                            iObserver,
                                                            iConnectionName );
         plugin->ReportQuietModeChange( iQuietMode );
         plugin->ReportVerboseModeChange( iVerboseMode );
         plugin->ReportCharacterChange( ECharTypeCarriage, iCarriageReturn );
         plugin->ReportCharacterChange( ECharTypeLineFeed, iLineFeed );
         plugin->ReportCharacterChange( ECharTypeBackspace, iBackspace );
         aPluginEntry.iInstance = plugin;
         }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Instantiates a plugin
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::InstantiatePlugin( TATExtPluginEntry& aPluginEntry )
    {
    TRACE_FUNC_ENTRY
    TRAPD( retTrap, InstantiatePluginL(aPluginEntry) );
    TRACE_FUNC_EXIT
    return retTrap;
    }

// ---------------------------------------------------------------------------
// Handles an AT command support request
// ---------------------------------------------------------------------------
TBool CATExtMetadata::HandleCommandSupportL( TATExtEntrySupport& aEntrySupport,
                                             const TDesC8* aAtCmdFull )
    {
    TRACE_FUNC_ENTRY
    InstantiatePluginSupportL( aEntrySupport );
    TBool supported = EFalse;
    if ( aEntrySupport.iEntry->iLocked )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrAccessDenied );
        }
    if ( !aAtCmdFull )
        {
        supported = aEntrySupport.iEntry->iInstance->IsCommandSupported(
            aEntrySupport.iAtCmdFull );
        }
    else
        {
        supported = aEntrySupport.iEntry->iInstance->IsCommandSupported(
            *aAtCmdFull );
        }
    TRACE_FUNC_EXIT
    return supported;
    }

// ---------------------------------------------------------------------------
// Handles an AT command
// ---------------------------------------------------------------------------
void CATExtMetadata::HandleCommandL( TATExtEntrySupport& aEntrySupport,
                                     TBool aReplyNeeded,
                                     const TDesC8* aAtCmdFull )
    {
    TRACE_FUNC_ENTRY
    if ( !aEntrySupport.iEntry || !aEntrySupport.iEntry->iInstance )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    if ( aEntrySupport.iEntry->iLocked )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrAccessDenied );
        }
    // As HandleCommand() could complete the message synchronously, set the
    // iCmdData before the call
    if ( aReplyNeeded )
        {
        iCmdData.iCmdStarted = ETrue;
        iCmdData.iCmdMessage = aEntrySupport.iMessage;
        iCmdData.iHandler = aEntrySupport.iEntry;
        iCmdData.iEditorHandler = NULL;
        }
    // No "else" here as HandleCommandL() is used also with observer plugins
    if ( !aAtCmdFull )
        {
        TRACE_INFO(( _L8("Handling command '%S' for UID:0x%08X, aReplyNeeded=%d..."), &iCmdData.iCmdBuffer, aEntrySupport.iEntry->iPluginUid, aReplyNeeded ));
        aEntrySupport.iEntry->iInstance->HandleCommand(
            iCmdData.iCmdBuffer,
            iCmdData.iCmdReplyBuffer,
            aReplyNeeded );
        }
    else
        {
        TRACE_INFO(( _L8("Handling command '%S' for UID:0x%08X, aReplyNeeded=%d..."), &(*aAtCmdFull), aEntrySupport.iEntry->iPluginUid, aReplyNeeded ));
        aEntrySupport.iEntry->iInstance->HandleCommand(
            *aAtCmdFull,
            iCmdData.iCmdReplyBuffer,
            aReplyNeeded );
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Sends an AT commands to multiple observer plugins, starting from a given
// position.
// ---------------------------------------------------------------------------
//
void CATExtMetadata::SendToMultipleObserverL(
    TATExtEntrySupport& aEntrySupport,
    const TDesC8* aAtCmdFull )
    {
    TRACE_FUNC_ENTRY
    if ( !aEntrySupport.iSupport )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    if ( !aEntrySupport.iSupportFound )
        {
        // No initial support found -> do nothing
        TRACE_FUNC_EXIT
        return;
        }
    TInt i;
    TInt count = aEntrySupport.iSupport->Count();
    for ( i=aEntrySupport.iStartIndex; i<count; i++ )
        {
        TATExtOneCmdSupport& oneCmdSupport = (*aEntrySupport.iSupport)[i];
        if ( oneCmdSupport.iSupportType != ESupportTypeObserver )
            {
            continue;
            }
        aEntrySupport.iEntry = &(*iPluginData)[oneCmdSupport.iEntryIndex];
        TBool supported = HandleCommandSupportL( aEntrySupport, aAtCmdFull );
        if ( supported )
            {
            HandleCommandL( aEntrySupport, EFalse, aAtCmdFull );
            aEntrySupport.iSupportFound = ETrue;
            }
        if ( i < count-1 )
            {
            aEntrySupport.iSupportFound = EFalse;
            }
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// Finds the first observer plugin support from a given starting position
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::FindFirstObserverSupportL(
    TATExtEntrySupport& aEntrySupport )
    {
    TRACE_FUNC_ENTRY
    if ( !aEntrySupport.iSupport )
        {
        TRACE_FUNC_EXIT
        User::Leave( KErrGeneral );
        }
    TInt i;
    TInt count = aEntrySupport.iSupport->Count();
    for ( i=aEntrySupport.iStartIndex; i<count; i++ )
        {
        TATExtOneCmdSupport& oneCmdSupport = (*aEntrySupport.iSupport)[i];
        if ( oneCmdSupport.iSupportType != ESupportTypeObserver )
            {
            continue;
            }
        aEntrySupport.iSupportFound = EFalse;
        aEntrySupport.iEntry = &(*iPluginData)[oneCmdSupport.iEntryIndex];
        TBool supported = HandleCommandSupportL( aEntrySupport );
        if ( supported )
            {
            aEntrySupport.iSupportFound = ETrue;
            TRACE_FUNC_EXIT
            return ETrue;
            }
        }
    TRACE_FUNC_EXIT
    return EFalse;
    }

// ---------------------------------------------------------------------------
// Extracts one NVRAM entry from a pipe-character delimited NVRAM buffer
// ---------------------------------------------------------------------------
//
TBool CATExtMetadata::ExtractNextNvramSetting( const TDesC8& aNvram,
                                               TInt& aStartIndex,
                                               TInt& aEndIndex )
    {
    TRACE_FUNC_ENTRY
    // Skip data before command
    TInt i;
    TChar character;
    TInt count = aNvram.Length();
    for ( i=aEndIndex; i<count; i++ )
        {
        character = aNvram[i];
        if ( character.IsPrint() && character!='|' )
            {
            break;
            }
        }
    if ( i >= count )
        {
        TRACE_FUNC_EXIT
        return EFalse;
        }
    aStartIndex = i;
    // Start of command found, next mark the end
    while ( i < count )
        {
        character = aNvram[i];
        if ( !character.IsPrint() || character=='|' )
            {
            break;
            }
        i++;
        }
    aEndIndex = i;
    if ( aEndIndex-aStartIndex <= 0 )
        {
        TRACE_FUNC_EXIT
        return EFalse;
        }
    TRACE_FUNC_EXIT
    return ETrue;
    }

// ---------------------------------------------------------------------------
// Gets the next command from support or auxiliary support data.
// ---------------------------------------------------------------------------
//
HBufC8* CATExtMetadata::GetNextSupportedCommand( TBool& aFirstSearch,
                                                 TInt& aSupportFind,
                                                 TBool& aFindInAux )
    {
    TRACE_FUNC_ENTRY
    // If first search then start from index 0, othewise start from the
    // next index
    if ( aFirstSearch )
        {
        aSupportFind = -1;
        aFindInAux = EFalse;
        }
    aSupportFind++;
    aFirstSearch = EFalse;
    CArrayFixFlat<TATExtAtCmdSupport>* support = iSupport;
    if ( !aFindInAux )
        {
        if ( aSupportFind >= support->Count() )
            {
            aSupportFind = 0;
            aFindInAux = ETrue;
            support = iSupportAux;
            if ( aSupportFind >= support->Count() )
                {
                TRACE_FUNC_EXIT
                return NULL;
                }
            }
        }
    else
        {
        support = iSupportAux;
        if ( aSupportFind >= support->Count() )
            {
            TRACE_FUNC_EXIT
            return NULL;
            }
        }
    TATExtAtCmdSupport& supportEntry = (*support)[aSupportFind];
    HBufC8& atCmdBase = *supportEntry.iAtCmdBase;
    HBufC8* newBuffer = HBufC8::NewMax( atCmdBase.Length() );
    if ( newBuffer )
        {
        newBuffer->Des().Copy( atCmdBase );
        }
    TRACE_FUNC_EXIT
    return newBuffer;
    }

// ---------------------------------------------------------------------------
// Reads an AT command from a client request message and creates buffer
// locally. For HandleCommand().
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::ReadCommandFromMessage( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    // Read AT command from message, stop if error encountered
    TInt desLength = aMessage.GetDesLength( EATExtHandleCmdParamCmd );
    if ( desLength <= 0 )
        {
        TRACE_FUNC_EXIT
        return KErrArgument;
        }
    TInt retTemp = iCmdData.iCmdBuffer.Create( desLength );
    if ( retTemp != KErrNone )
        {
        TRACE_FUNC_EXIT
        return retTemp;
        }
    retTemp = aMessage.Read( EATExtHandleCmdParamCmd, iCmdData.iCmdBuffer );
    TRACE_INFO(( _L("Read returned %d"), retTemp ));
    if ( retTemp != KErrNone )
        {
        iCmdData.iCmdBuffer.Close();
        TRACE_FUNC_EXIT
        return retTemp;
        }
    TRACE_FUNC_EXIT
    return retTemp;
    }

// ---------------------------------------------------------------------------
// Creates a buffer with "ERROR" or "" string in it; needed for creating a
// reply to an unknown command (i.e. when no plugin supports the "base" part
// of a command) or to the case when plugin support exists but reply is not
// expected from them.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::CreateEmptyOrErrorBuffer( RBuf8& aBuffer,
                                               TBool aErrorReply )
    {
    TRACE_FUNC_ENTRY
    // Note: The behavior of RBuf8 is awkward. It is not possible to query
    // the "allocation status". Comparing the length to zero always returns
    // true even if RBuf8::Create() was not called. Here we should do the
    // following: If allocated and zero length, return with KErrNone. Otherwise
    // if not allocated create the buffer and return with RBuf::Create()'s
    // return code. We have to hack here so let's expect the buffer is not
    // allocated when calling this function.
    if ( !aErrorReply )
        {
        TInt retTemp = aBuffer.Create( KNullDesC8 );
        TRACE_FUNC_EXIT
        return retTemp;
        }
    TInt retVal = KErrNone;
    TBuf8<KErrorMsgLen> errorBuffer;
    if ( !iQuietMode )
        {
        if ( iVerboseMode )
            {
            _LIT8( KVerboseError, "ERROR" );
            errorBuffer.Append( iCarriageReturn );
            errorBuffer.Append( iLineFeed );
            errorBuffer.Append( KVerboseError );
            errorBuffer.Append( iCarriageReturn );
            errorBuffer.Append( iLineFeed );
            }
        else
            {
            _LIT8( KNumericError, "4" );
            errorBuffer.Append( KNumericError );
            errorBuffer.Append( iCarriageReturn );
            }
        }
    retVal = aBuffer.Create( errorBuffer );
    TRACE_FUNC_EXIT
    return retVal;
    }

// ---------------------------------------------------------------------------
// Writes specified input reply buffer to a client request message at given
// message slot number.
// ---------------------------------------------------------------------------
//
TInt CATExtMetadata::WriteReplyBufferToClient( const TDesC8& aBuffer,
                                               TInt aDataSlot,
                                               const RMessage2& aMessage,
                                               TBool aReportNextLength,
                                               TInt aLengthSlot )
    {
    TRACE_FUNC_ENTRY
    TInt maxLength = aMessage.GetDesMaxLength( aDataSlot );
    if ( aBuffer.Length() > maxLength )
        {
        TRACE_FUNC_EXIT
        return KErrTooBig;
        }
    TInt retTemp = aMessage.Write( aDataSlot, aBuffer );
    TRACE_INFO(( _L("First write returned %d"), retTemp ));
    if ( retTemp != KErrNone )
        {
        TRACE_FUNC_EXIT
        return retTemp;
        }
    if ( aReportNextLength && iCmdData.iHandler &&
         iCmdData.iHandler->iInstance )  // optional
        {
        CATExtPluginBase& plugin = *iCmdData.iHandler->iInstance;
        TInt nextPartLength = plugin.NextReplyPartLength();
        if ( nextPartLength <= 0 )
            {
            iCmdData.iHandler = NULL;
            }
        TPckg<TInt> nextPartLengthPckg( nextPartLength );
        retTemp = aMessage.Write( aLengthSlot, nextPartLengthPckg );
        TRACE_INFO(( _L("Second write returned %d"), retTemp ));
        if ( retTemp != KErrNone )
            {
            TRACE_FUNC_EXIT
            return retTemp;
            }
        }
    TRACE_FUNC_EXIT
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Assignment operator for ease of usage
// ---------------------------------------------------------------------------
//
TATExtEntrySupport& TATExtEntrySupport::operator=( TATExtEntrySupport& aEntrySupport )
    {
    if ( &aEntrySupport == this )
        {
        return aEntrySupport;
        }
    iAtCmdFull = aEntrySupport.iAtCmdFull;
    iMessage = aEntrySupport.iMessage;
    iSupport = aEntrySupport.iSupport;
    iEntry = aEntrySupport.iEntry;
    iStartIndex = aEntrySupport.iStartIndex;
    iSupportFound = aEntrySupport.iSupportFound;
    return *this;
    }