atext/server/src/atextmetadata.cpp
changeset 0 29b1cd4cb562
child 1 b4a7eebaaebf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/atext/server/src/atextmetadata.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,2608 @@
+/*
+* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  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): Does not send to S if support found.
+ * 2) Primary (P): Sends to all S if support found.
+ * 3) Secondary (S): Process the command and give or not give reply, based on
+ *    the following logic:
+ *
+ * => [If] M found, handle command and send reply, stop, [else]
+ * [If] P found, handle command and send reply + send to N S {no reply}, stop, [else]
+ * [If] > 1 S found, send to N S {no reply}, stop, [else]
+ * [If] only 1 S 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 S and M, P nor other S 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;
+        }
+    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;
+        }
+    // 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 )
+        {
+        TRACE_FUNC_EXIT
+        return KErrAlreadyExists;
+        }
+    if ( !iCmdData.iCmdMessage.Handle() )
+        {
+        TRACE_FUNC_EXIT
+        return KErrBadHandle;
+        }
+    // 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 );
+            }
+        if ( aMultiPart )
+            {
+            WriteReplyBufferToClient( iCmdData.iCmdReplyBuffer,
+                                      EATExtHandleCmdParamReply,
+                                      iCmdData.iCmdMessage,
+                                      ETrue,
+                                      EATExtHandleCmdParamLength );
+            }
+        else
+            {
+            WriteReplyBufferToClient( iCmdData.iCmdReplyBuffer,
+                                      EATExtHandleCmdParamReply,
+                                      iCmdData.iCmdMessage );
+            }
+        }
+    iCmdData.iCmdStarted = EFalse;
+    iCmdData.iReplyExpected = EFalse;
+    iCmdData.iCmdMessage.Complete( aError );
+    ClearInitializedCmdHandlerData( aMultiPart );
+    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;
+    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;
+    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();
+        }
+    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':  // Primary plugin
+        case 'p':
+            aSupportType = ESupportTypePrimary;
+            break;
+        case 'S':  // Secondary plugin
+        case 's':
+            aSupportType = ESupportTypeSecondary;
+            break;
+        }
+    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 ESupportTypePrimary:
+            {
+            retVal = AddNewPrimaryMetadataEntryLinkL( aEntries,
+                                                      aSearchHelper,
+                                                      oneCmdSupport );
+            }
+            break;
+        case ESupportTypeSecondary:
+            {
+            retVal = AddNewSecondaryMetadataEntryLinkL( 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
+    aEntries->InsertL( 0, aOneCmdSupport );
+    if ( aSearchHelper.iPrimaryIndex >= 0 )
+       {
+        aSearchHelper.iPrimaryIndex++;
+        }
+    if ( aSearchHelper.iSecondaryIndex >= 0 )
+        {
+        aSearchHelper.iSecondaryIndex++;
+        }
+    TRACE_FUNC_EXIT
+    return 0;
+    }
+
+// ---------------------------------------------------------------------------
+// Adds new primary plugin entry link from plugin support entry to plugin
+// entry
+// ---------------------------------------------------------------------------
+//
+TInt CATExtMetadata::AddNewPrimaryMetadataEntryLinkL(
+    CArrayFixFlat<TATExtOneCmdSupport>* aEntries,
+    TATExtSearchHelper& aSearchHelper,
+    TATExtOneCmdSupport& aOneCmdSupport )
+    {
+    TRACE_FUNC_ENTRY
+    TInt i = aSearchHelper.iPrimaryIndex;
+    if ( i < 0 )
+        {
+        TInt count = aEntries->Count();
+        for ( i=0; i<count; i++ )
+            {
+            TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[i];
+            if ( oneCmdSupport.iSupportType==ESupportTypePrimary ||
+                 oneCmdSupport.iSupportType==ESupportTypeSecondary )
+                {
+                break;
+                }
+            }
+        aSearchHelper.iPrimaryIndex = i;
+        }
+    aEntries->InsertL( i, aOneCmdSupport );
+    if ( aSearchHelper.iSecondaryIndex >= 0 )
+        {
+        aSearchHelper.iSecondaryIndex++;
+        }
+    TRACE_FUNC_EXIT
+    return i;
+    }
+
+// ---------------------------------------------------------------------------
+// Adds new secondary plugin entry link from plugin support entry to plugin
+// entry. Search starts from the front as there could be multiple S plugins
+// but only one or two M/P plugins.
+// ---------------------------------------------------------------------------
+//
+TInt CATExtMetadata::AddNewSecondaryMetadataEntryLinkL(
+    CArrayFixFlat<TATExtOneCmdSupport>* aEntries,
+    TATExtSearchHelper& aSearchHelper,
+    TATExtOneCmdSupport& aOneCmdSupport )
+    {
+    TRACE_FUNC_ENTRY
+    TInt i = aSearchHelper.iSecondaryIndex;
+    if ( i < 0 )
+        {
+        TInt count = aEntries->Count();
+        for ( i=0; i<count; i++ )
+            {
+            TATExtOneCmdSupport& oneCmdSupport = (*aEntries)[i];
+            if ( oneCmdSupport.iSupportType == ESupportTypeSecondary )
+                {
+                break;
+                }
+            }
+        aSearchHelper.iSecondaryIndex = 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, 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 = HandleMasterPluginSupportL(
+                entrySupport,
+                aComplInfo.iReplyExpected );
+            }
+        else if ( oneCmdSupport.iSupportType == ESupportTypePrimary )
+            {
+            supported = HandlePrimaryPluginSupportL(
+                entrySupport,
+                i+1,
+                aComplInfo.iReplyExpected );
+            }
+        else if ( oneCmdSupport.iSupportType == ESupportTypeSecondary )
+            {
+            supported = HandleSecondaryPluginSupportL(
+                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 plugin is detected in the plugin data via
+// support data's link (support for a full AT command). If a master plugin is
+// detected then reply is detected from that plugin. No further sending to
+// primary or secondary plugins is repformed.
+// ---------------------------------------------------------------------------
+//
+TBool CATExtMetadata::HandleMasterPluginSupportL(
+    TATExtEntrySupport& aEntrySupport,
+    TBool& aReplyExpected )
+    {
+    TRACE_FUNC_ENTRY
+    aReplyExpected = EFalse;
+    TBool supported = HandleCommandSupportL( aEntrySupport );
+    if ( !supported )
+        {
+        TRACE_FUNC_EXIT
+        return EFalse;
+        }
+    iCmdData.iReplyExpected = ETrue;  // Set before HandleCommandL()
+    HandleCommandL( aEntrySupport, ETrue );
+    aReplyExpected = ETrue;
+    TRACE_FUNC_EXIT
+    return ETrue;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles support when a primary plugin is detect in the plugin data via
+// support data's link. If a primary plugin is detected then reply is expected
+// from that plugin. Also if one or more secondary plugins are detected then
+// no reply is expected from them.
+// ---------------------------------------------------------------------------
+//
+TBool CATExtMetadata::HandlePrimaryPluginSupportL(
+    TATExtEntrySupport& aEntrySupport,
+    TInt aStartIndex,
+    TBool& aReplyExpected )
+    {
+    TRACE_FUNC_ENTRY
+    aReplyExpected = EFalse;
+    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 );
+    // Now execute the HandleCommand()
+    iCmdData.iReplyExpected = ETrue;  // Set before HandleCommandL()
+    HandleCommandL( aEntrySupport, ETrue );
+    aEntrySupport.iStartIndex = aStartIndex;
+    SendToMultipleSecondaryL( aEntrySupport, atCmdFull );
+    CleanupStack::PopAndDestroy( atCmdFull );
+    aReplyExpected = ETrue;
+    TRACE_FUNC_EXIT
+    return ETrue;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles support when a secondary plugin is detected in the plugin data via
+// support data's link. If only one secondary plugin is detected then reply is
+// expected from that plugin. Instead, if more than one secondary plugins are
+// detected then no reply is expected from them.
+// ---------------------------------------------------------------------------
+//
+TBool CATExtMetadata::HandleSecondaryPluginSupportL(
+    TATExtEntrySupport& aEntrySupport,
+    TInt aStartIndex,
+    TBool& aReplyExpected )
+    {
+    TRACE_FUNC_ENTRY
+    aReplyExpected = EFalse;
+    TBool supported = HandleCommandSupportL( aEntrySupport );
+    if ( !supported )
+        {
+        TRACE_FUNC_EXIT
+        return EFalse;
+        }
+    TATExtEntrySupport nextSupport = aEntrySupport;
+    nextSupport.iStartIndex = aStartIndex;
+    TBool entryFound = FindFirstSecondarySupportL( 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()
+        HandleCommandL( aEntrySupport, EFalse );
+        SendToMultipleSecondaryL( nextSupport, atCmdFull );
+        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 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 S entry. This insertion is faster than the other way around as there
+    // can be multiple S 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;
+        }
+    // No "else" here as HandleCommandL() is used also with secondary 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 secondary plugins, starting from a given
+// position.
+// ---------------------------------------------------------------------------
+//
+void CATExtMetadata::SendToMultipleSecondaryL(
+    TATExtEntrySupport& aEntrySupport,
+    const TDesC8* aAtCmdFull )
+    {
+    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 != ESupportTypeSecondary )
+            {
+            continue;
+            }
+        aEntrySupport.iEntry = &(*iPluginData)[i];
+        TBool supported = HandleCommandSupportL( aEntrySupport, aAtCmdFull );
+        if ( supported )
+            {
+            HandleCommandL( aEntrySupport, EFalse, aAtCmdFull );
+            }
+        }
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Finds the first secondary plugin support from a given starting position
+// ---------------------------------------------------------------------------
+//
+TBool CATExtMetadata::FindFirstSecondarySupportL(
+    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 != ESupportTypeSecondary )
+            {
+            continue;
+            }
+        aEntrySupport.iEntry = &(*iPluginData)[i];
+        TBool supported = HandleCommandSupportL( aEntrySupport );
+        if ( supported )
+            {
+            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;
+    }