convergedcallengine/cce/src/cccepluginmanager.cpp
changeset 0 ff3b6d0fd310
child 19 7d48bed6ce0c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/convergedcallengine/cce/src/cccepluginmanager.cpp	Tue Feb 02 01:11:09 2010 +0200
@@ -0,0 +1,703 @@
+/*
+* Copyright (c) 2006-2007 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:  Handles plugins / threads
+*
+*/
+
+
+//  INCLUDE FILES
+#include "cccepluginmanager.h"
+#include "mccedtmfobserver.h"
+#include "ccceplugin.h"
+#include "cccelogger.h"
+#include "cccecallcontainer.h"
+#include "ccceemergencycall.h"
+#include "cccecall.h"
+#include "mccpdtmfprovider.h"
+#include "cconvergedcallprovider.h"
+#include "icmapi.h"
+#include "cccespsettingshandler.h"
+
+
+
+// ======== MEMBER FUNCTIONS ========
+// -----------------------------------------------------------------------------
+// NewL()
+// -----------------------------------------------------------------------------
+//
+CCCEPluginManager* CCCEPluginManager::NewL( 
+    CCCECallContainer& aCallContainer,
+    CCCETransferController& aTransferController )
+    {
+    CCCEPluginManager* self = new (ELeave) CCCEPluginManager( 
+        aCallContainer, 
+        aTransferController );
+        
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CCCEPluginManager() 
+// -----------------------------------------------------------------------------
+//
+CCCEPluginManager::CCCEPluginManager( 
+    CCCECallContainer& aCallContainer,
+    CCCETransferController& aTransferController ) : 
+        CActive( EPriorityStandard ),
+        iCallContainer( aCallContainer ),
+        iTransferController( aTransferController )
+    {
+    iPluginArray.Reset();
+    }
+
+// -----------------------------------------------------------------------------
+// ConstructL()
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::ConstructL()
+    {
+#if 0
+// capabilities still todo here
+
+	static _LIT_SECURITY_POLICY_PASS( KAllowAllPolicy );
+	static _LIT_SECURITY_POLICY_C2(	KICMPolicy, 
+									ECapabilityNetworkControl,
+									ECapabilityWriteDeviceData);	
+
+        
+    // define first property to be integer type
+    TInt err = RProperty::Define( KPSUidICMIncomingCall, 
+        KPropertyKeyICMPluginUID, RProperty::EInt, KAllowAllPolicy, KICMPolicy );
+#else
+    // define first property to be integer type
+    TInt err = RProperty::Define( KPSUidICMIncomingCall, 
+        KPropertyKeyICMPluginUID, RProperty::EInt );
+#endif
+        
+    if ( err != KErrAlreadyExists )
+        {
+        User::LeaveIfError( err );
+        }
+        
+    User::LeaveIfError( iProperty.Attach( KPSUidICMIncomingCall,
+        KPropertyKeyICMPluginUID ) );
+    CActiveScheduler::Add(this);
+    RunL();
+
+    iIdle = CIdle::NewL( EPriorityIdle );
+    iIdle->Start( TCallBack(DoAfterBoot,this) );
+    
+    iSPSettings = CCCESPSettingsHandler::NewL( *this );
+    }
+
+// -----------------------------------------------------------------------------
+// ~CCCEPluginManager()
+// -----------------------------------------------------------------------------
+//
+CCCEPluginManager::~CCCEPluginManager()
+    {
+    delete iSPSettings;
+    
+    if( iIdle )
+        {
+        iIdle->Cancel();
+        delete iIdle;
+        iIdle = NULL;
+        }
+
+    iPluginArray.ResetAndDestroy();
+    iPluginArray.Close();
+    
+    iPluginsToClose.ResetAndDestroy();
+    iPluginsToClose.Close();
+    
+    iAlternativeEmergencyPlugins.Reset();
+    iAlternativeEmergencyPlugins.Close();
+    
+    Cancel();
+    iProperty.Close();
+    
+    REComSession::FinalClose();
+    
+    }
+
+// -----------------------------------------------------------------------------
+// RunL()
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::RunL()
+    {
+    // TODO If there is two calls arriving same time. How ICM can handle it?
+    
+    CCELOGSTRING("CCCEPluginManager::RunL()" );
+    // resubscribe before processing new value to prevent missing updates
+    iProperty.Subscribe( iStatus );
+    SetActive();
+    // property updated, get new value
+
+    TInt keyValue;
+    if ( iProperty.Get( keyValue ) == KErrNotFound )
+        {
+        // property deleted, do necessary actions here...
+        }
+    else
+        {
+        if( keyValue )
+            {
+            TUid pluginId = TUid::Uid(keyValue);
+
+            CCELOGSTRING2("CCCEPluginManager::RunL: IncomingCallAPI = %d",
+                pluginId.iUid );
+            // use new value ...
+            GetPluginL( pluginId );
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// SetObserver()
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::SetObserver( const MCCEObserver& aObserver )
+    {
+    iObserver = &aObserver;
+    }
+    
+// -----------------------------------------------------------------------------
+// SetObserver()
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::SetEmergencyCallObserver(MCCPCallObserver& aEmergencyCallObserver)
+    {
+    iEmergencyCallObserver = &aEmergencyCallObserver;
+    }
+
+// -----------------------------------------------------------------------------
+// DoAfterBoot()
+// -----------------------------------------------------------------------------
+//
+TInt CCCEPluginManager::DoAfterBoot( TAny* aPluginManager )
+    {
+    CCELOGSTRING("CCCEPluginManager::DoAfterBoot()" );
+    
+    CCCEPluginManager* manager = 
+        static_cast< CCCEPluginManager* >( aPluginManager );
+        TRAP_IGNORE(manager->LoadBootPluginsL());
+   
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// Remove unneeded plug-ins
+// -----------------------------------------------------------------------------
+//
+TInt CCCEPluginManager::RemovePlugins( TAny* aPluginManager )
+    {
+    CCELOGSTRING("CCCEPluginManager::RemovePlugins()" );
+    
+    CCCEPluginManager* manager = 
+        static_cast< CCCEPluginManager* >( aPluginManager );
+        TRAP_IGNORE(manager->iPluginsToClose.ResetAndDestroy());
+   
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// ServiceEnabledL
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::ServiceEnabledL( TUid aImplementationUid )
+    {
+    CCELOGSTRING("CCCEPluginManager::ServiceEnabledL" );
+    CConvergedCallProvider* provider = GetPluginL( aImplementationUid );
+    
+    if( provider )
+        {
+        CCCEPlugin* plugin( NULL );
+        for( TInt i=0; i<iPluginArray.Count(); i++ )
+            {
+            TUid oldPlugin = iPluginArray[i]->Type();
+           
+            if( oldPlugin.iUid == aImplementationUid.iUid )
+                {
+                plugin =  iPluginArray[i];
+                // set plugin to stay in memory
+                plugin->SetReleaseWhenIdle( EFalse );
+                i = iPluginArray.Count();
+                }
+            }
+        }
+    else
+        {
+        User::Leave( KErrGeneral );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// ServiceDisabledL
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::ServiceDisabledL( TUid aImplementationUid )
+    {
+    CCELOGSTRING("CCCEPluginManager::ServiceDisabledL" );
+    CCCEPlugin* plugin( NULL );
+    for( TInt i=0; i<iPluginArray.Count(); i++ )
+        {
+        TUid oldPlugin = iPluginArray[i]->Type();
+       
+        if( oldPlugin.iUid == aImplementationUid.iUid )
+            {
+            plugin =  iPluginArray[i];
+            i = iPluginArray.Count();
+            }
+        }
+
+    if( !plugin )
+        {
+        CCELOGSTRING("CCCEPluginManager:: No plugin found" );
+        User::Leave( KErrNotFound );
+        }
+    else if( iCallContainer.CountCalls( aImplementationUid ) )
+        {
+        CCELOGSTRING("CCCEPluginManager:: Plugin found with ongoing calls" );
+        plugin->SetReleaseWhenIdle( ETrue );
+        }
+    else
+        {
+        CCELOGSTRING("CCCEPluginManager:: Plugin found" );
+        plugin->SetReleaseWhenIdle( ETrue );
+        RemovePlugin( aImplementationUid );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// Load EBootstrapCallProvider marked or enabled plugins
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::LoadBootPluginsL()
+    {
+    //List implementations
+#ifdef _DEBUG
+    RImplInfoPtrArray implementations;
+    CConvergedCallProvider::ListImplementationsL(implementations);
+    CCELOGSTRING2("CCCEPluginManager::GetPluginL: %d Implementation(s) found", 
+        implementations.Count() );
+        
+    for( TInt i=0; i<implementations.Count(); i++ )
+        {
+        CImplementationInformation *info = implementations[i];
+        
+        CCELOGSTRING3("CCCEPluginManager::GetPluginL: Uid = %d, Name = %S", 
+            info->ImplementationUid().iUid, &info->DisplayName() );
+        CCELOGSTRING3("CCCEPluginManager::GetPluginL: ->RomBased = %d, RomOnly = %d", 
+            info->RomBased(), info->RomOnly() );
+        }
+  
+    implementations.ResetAndDestroy();
+    implementations.Close();
+#endif // _DEBUG
+    
+    RIdArray serviceIDArray;
+    CleanupClosePushL( serviceIDArray );
+    iSPSettings->GetServicesL( serviceIDArray );
+    const TInt serviceCount = serviceIDArray.Count();
+    iPrimaryEmergencyCallPlugin = NULL;
+    TInt err = KErrNone;
+    
+    CCELOGSTRING2("CCCEPluginManager::LoadBootPluginsL: Service count: %d", serviceCount );
+    for( TInt service = 0; service < serviceCount; service++ )
+        {
+        CCELOGSTRING2("CCCEPluginManager::LoadBootPluginsL: Processing service: %d", service );
+        CCELOGSTRING2("CCCEPluginManager::LoadBootPluginsL: ServiceID: %d", serviceIDArray[service] );
+        
+        TRAP( err, LoadBootPluginL( serviceIDArray[service] ) )
+        
+        CCELOGSTRING2("CCCEPluginManager::LoadBootPluginsL: Result %i", err );
+        }
+
+    CleanupStack::PopAndDestroy();
+    }
+
+// -----------------------------------------------------------------------------
+// Load EBootstrapCallProvider marked or enabled plugin
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::LoadBootPluginL( TInt aServiceId )
+    {
+    const TUid id = iSPSettings->ImplementationUidL( aServiceId );
+    CCELOGSTRING2("CCCEPluginManager::LoadBootPluginL: Plugin id: %i", id );
+    
+    const TInt mask = iSPSettings->CapabilitiesL( aServiceId );
+    CCELOGSTRING2("CCCEPluginManager::LoadBootPluginL: Service mask: %d", mask );
+    
+    const TBool enabled = iSPSettings->IsEnabledL( aServiceId );
+    CCELOGSTRING2("CCCEPluginManager::LoadBootPluginL: VoIP enabled: %i", enabled );
+    
+    const TBool emergencyCall = mask & ESupportsEmergencyCall;
+    CCELOGSTRING2(
+        "CCCEPluginManager::LoadBootPluginL: Support EmergencyCall: %i", emergencyCall );
+    
+    if( mask & EBootstrapCallProvider || enabled )
+        {
+        CCELOGSTRING("CCCEPluginManager::LoadBootPluginL: Bootable plugin found" );
+        ServiceEnabledL( id );
+        }
+
+    if( emergencyCall )
+        {
+        if( !iPrimaryEmergencyCallPlugin )
+            {
+            CCCEPlugin* plugin( NULL );
+            for( TInt i=0; i<iPluginArray.Count(); i++ )
+                {
+                TUid oldPlugin = iPluginArray[i]->Type();
+                
+                if( oldPlugin.iUid == id.iUid )
+                    {
+                    plugin =  iPluginArray[i];
+                    i = iPluginArray.Count();
+                    }
+                }
+            
+            if( plugin )
+                {
+                CCELOGSTRING("CCCEPluginManager::LoadBootPluginL: Initialise primary emergency call" );
+                plugin->InitialiseEmergencyCallL( *iEmergencyCallObserver );
+                iPrimaryEmergencyCallPlugin = plugin;
+                }
+            else
+                {
+                CCELOGSTRING("CCCEPluginManager::LoadBootPluginL: append plugin to alternative emergency array" );
+                AddToAlternativeEmergencyArray( id );   
+                }
+            }
+        else
+            {
+            CCELOGSTRING("CCCEPluginManager::LoadBootPluginL: append plugin to alternative emergency array" );
+            AddToAlternativeEmergencyArray( id );   
+            } 
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// DoCancel()
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::DoCancel()
+    {
+    CCELOGSTRING("CCCEPluginManager::DoCancel()" );
+    iProperty.Cancel();
+    }
+    
+// -----------------------------------------------------------------------------
+// Return Primary emergency call 
+// -----------------------------------------------------------------------------
+//    
+MCCPEmergencyCall* CCCEPluginManager::PrimaryEmergencyCall()
+    {
+     MCCPEmergencyCall* call (NULL);
+     if( iPrimaryEmergencyCallPlugin )
+        {
+        call =  iPrimaryEmergencyCallPlugin->GetEmergencyCall();
+        }
+    return call;      
+    }
+
+// -----------------------------------------------------------------------------
+// Return Primary emergency call plugin uid
+// -----------------------------------------------------------------------------
+//    
+TUid CCCEPluginManager::PrimaryEmergencyCallUid() const
+    {
+    TUid id( KNullUid );
+    
+    if( iPrimaryEmergencyCallPlugin )
+        {
+        id = iPrimaryEmergencyCallPlugin->Type();
+        }
+    
+    return id;
+    }
+    
+// -----------------------------------------------------------------------------
+// Get array of know emergency plugins
+// -----------------------------------------------------------------------------
+//  
+RArray<TUid>& CCCEPluginManager::AlternativeEmergencyPlugins()
+    {
+    CCELOGSTRING("CCCEPluginManager::AlternativeEmergencyPlugins ");
+    RImplInfoPtrArray implementations;
+    TRAP_IGNORE( CConvergedCallProvider::ListImplementationsL(implementations) );
+    iAlternativeEmergencyPlugins.Reset();
+        
+    for( TInt i=0; i<implementations.Count(); i++ )
+        {
+        CImplementationInformation *info = implementations[i];
+        CCELOGSTRING2("CCCEPluginManager::AlternativeEmergencyPlugins: Found %d ", info->ImplementationUid());
+        AddToAlternativeEmergencyArray(info->ImplementationUid());
+        }
+         
+    implementations.ResetAndDestroy();
+    implementations.Close();  
+ 
+    return iAlternativeEmergencyPlugins;
+    }
+
+// -----------------------------------------------------------------------------
+// Add new plugin to array. Do not allow duplicates
+// -----------------------------------------------------------------------------
+//
+void CCCEPluginManager::AddToAlternativeEmergencyArray( TUid aUid )
+    {
+    if( iPrimaryEmergencyCallPlugin &&
+        ( iPrimaryEmergencyCallPlugin->Type().iUid == aUid.iUid ) )
+        {
+        CCELOGSTRING("CCCEPluginManager::AddToAlternativeEmergencyArray: Is Primary emergency plugin ");
+        }
+    else if( iAlternativeEmergencyPlugins.Find( aUid ) == KErrNotFound )
+        {
+        CCELOGSTRING("CCCEPluginManager::AddToAlternativeEmergencyArray: New plugin. Append to array ");    
+        iAlternativeEmergencyPlugins.Append( aUid );  
+        }    
+    else
+        {
+        CCELOGSTRING("CCCEPluginManager::AddToAlternativeEmergencyArray: Is already in Alternative Plugins ");    
+        }
+    
+    }
+// -----------------------------------------------------------------------------
+// Return protocol interface object of wanted service ID
+// -----------------------------------------------------------------------------
+// 
+CConvergedCallProvider* CCCEPluginManager::GetPluginL( TUint32 aServiceId )
+    {
+    CCELOGSTRING2("CCCEPluginManager::GetPluginL: Get plugin for servce id %d", aServiceId);
+    TUid id( KNullUid );
+    
+    id = iSPSettings->ImplementationUidL( aServiceId );
+
+    if( !id.iUid )
+        {
+        CCELOGSTRING("CCCEPluginManager::GetPluginL: PluginId not found");
+        User::Leave( KErrNotFound );
+        }
+    
+    return GetPluginL( id );
+    }
+    
+// -----------------------------------------------------------------------------
+// Return protocol interface object of wanted type.
+// -----------------------------------------------------------------------------
+//
+CConvergedCallProvider* CCCEPluginManager::GetPluginL( const TUid& aType )
+    {
+    CCCEPlugin* plugin = NULL;
+   
+   //Is plugin started? 
+    CCELOGSTRING2("CCCEPluginManager::GetPluginL: %d Implementation(s) running", 
+        iPluginArray.Count() );
+
+    // Checked if ie. VoIP Plugins is supported
+    iSPSettings->IsPluginSupportedL( aType );
+    
+    for( TInt i=0; i<iPluginArray.Count(); i++ )
+        {
+        TUid oldPlugin = iPluginArray[i]->Type();
+        CCELOGSTRING2("CCCEPluginManager::GetPluginL: existing plugin = %d",
+            oldPlugin.iUid );
+            
+        if( oldPlugin.iUid == aType.iUid )
+            {
+            CCELOGSTRING("CCCEPluginManager::GetPluginL: Plugin found" );
+            plugin =  iPluginArray[i];
+            }
+        }
+ 
+    if (!plugin)
+        {
+        //Start plugin
+        CCELOGSTRING("CCCEPluginManager::GetPluginL: New Plugin" );
+        CCELOGSTRING2("CCCEPluginManager::GetPluginL: Plugin Uid = %d",
+            aType.iUid );
+        plugin = CCCEPlugin::NewL(
+            aType, const_cast<MCCEObserver&>(*iObserver),
+                 iCallContainer, *this, iTransferController );
+                 
+        CleanupStack::PushL(plugin);
+        CCELOGSTRING("CCCEPluginManager::GetPluginL: Append to array" );
+        iPluginArray.AppendL( plugin );
+        CleanupStack::Pop();
+        }
+        
+   return plugin->GetPluginL();
+   }
+
+// -----------------------------------------------------------------------------
+// remove protocol interface object of wanted type.
+// -----------------------------------------------------------------------------
+//   
+void CCCEPluginManager::RemovePlugin( const TUid& aType )
+    {
+    CCELOGSTRING2("CCCEPluginManager::RemovePlugin: %d Implementation(s) running", 
+        iPluginArray.Count() );
+        
+    TInt pluginCount = iPluginArray.Count();
+    for (TInt a = pluginCount-1; a >= 0 ; a--)
+        {
+        if ( iPluginArray[a]->Type() == aType && 
+             iPluginArray[a]->ReleaseWhenIdle() )
+            {
+            // keep track that only one instance of pointer is in array
+            iPluginsToClose.InsertInAddressOrder(iPluginArray[a]);
+            CCELOGSTRING("CCCEPluginManager::RemovePlugin scheduled") 
+            
+            if (!iIdle->IsActive())
+                {
+                iIdle->Start( TCallBack(RemovePlugins,this) );
+                }
+            iPluginArray.Remove(a);
+            iPluginArray.Compress();
+            break;
+            }
+        }
+    }
+   
+    
+// ---------------------------------------------------------------------------
+// Returns ETrue if plugin can be relesed if idle
+// ---------------------------------------------------------------------------
+//
+TBool CCCEPluginManager::ReleaseWhenIdle( const TUid aType )
+    {
+    
+    TBool release = EFalse;     
+    TInt pluginCount = iPluginArray.Count();
+    for (TInt a = 0; a < pluginCount; a++)
+        {
+        if ( iPluginArray[a]->Type() == aType )
+            {
+            release = iPluginArray[a]->ReleaseWhenIdle();
+            break;
+            }
+        }
+        
+    CCELOGSTRING2("CCCEPluginManager::ReleaseWhenIdle  returning %d ", (TInt) release);
+    return release;
+    }
+
+// -----------------------------------------------------------------------------
+// Return array of dtmf providers.
+// -----------------------------------------------------------------------------
+//
+MCCPDTMFProvider& CCCEPluginManager::DtmfProviderL( 
+    const MCCPDTMFObserver& aObserver,
+    TUid aImplementationUid ) const
+    {
+    CCCEPlugin* plugin( NULL );
+    
+    for( TInt i=0; i<iPluginArray.Count(); i++ )
+        {
+        if( iPluginArray[i]->Type().iUid == aImplementationUid.iUid )
+            {
+            plugin =  iPluginArray[i];
+            i = iPluginArray.Count();
+            }
+        }
+  
+    if ( !plugin )
+        {
+        User::Leave( KErrNotFound );
+        }
+   
+    return plugin->GetDtmfProviderL( aObserver );
+    }
+
+// -----------------------------------------------------------------------------
+// From class MPluginObserver.
+// 
+// -----------------------------------------------------------------------------
+//
+
+void CCCEPluginManager::CCPPluginDiedEvent(TUid aPluginId, TInt /*aDeathType*/, TInt /*aReason*/)
+    {
+
+    CCELOGSTRING2("CCCEPluginManager::CCPPluginDiedEvent: %d", aPluginId.iUid );
+       
+    RPointerArray<CCCECall> calls;
+    
+    TInt count = iCallContainer.GetCall(aPluginId, calls);
+    CCELOGSTRING2("CCCEPluginManager::CCPPluginDiedEvent: %d calls found!", count);
+    for (TInt i = 0; i < count; i++)
+        {
+        calls[i]->ErrorOccurred( ECCPServerFailure, &calls[i]->GetCCPCall() );
+        calls[i]->CallStateChanged( MCCPCallObserver::ECCPStateIdle, &calls[i]->GetCCPCall() );
+        }
+        
+    calls.Close();
+    
+    CCELOGSTRING("CCCEPluginManager::CCPPluginDiedEvent: Plugin calls terminated");
+
+    TInt pluginCount = iPluginArray.Count();
+    for (TInt a = 0; a < pluginCount; a++)
+        {
+        if ( iPluginArray[a]->Type() == aPluginId )
+            {
+            iPluginsToClose.Append(iPluginArray[a]);
+            if (!iIdle->IsActive())
+                {
+                iIdle->Start( TCallBack(RemovePlugins,this) );
+                }
+            iPluginArray.Remove(a);
+            iPluginArray.Compress();
+            return;
+            }
+        }
+    }
+// -----------------------------------------------------------------------------
+// From class MPluginObserver.
+// 
+// -----------------------------------------------------------------------------
+//    
+
+void CCCEPluginManager::CCPPluginInitialisationFailed(TUid aPluginUid, TInt /*aError*/)
+    {
+ 
+    CCELOGSTRING2("CCCEPluginManager::CCPPluginInitialisationFailed: %d", aPluginUid.iUid );
+    if (iCallContainer.CountCalls(aPluginUid))
+        {
+        // there is active call, cannot close.
+        return;
+        }
+        
+    TInt pluginCount = iPluginArray.Count();
+    for (TInt a = 0; a < pluginCount; a++)
+        {
+        if ( iPluginArray[a]->Type() == aPluginUid )
+            {
+            iPluginsToClose.Append(iPluginArray[a]);
+            if (!iIdle->IsActive())
+                {
+                iIdle->Start( TCallBack(RemovePlugins,this) );
+                }
+            iPluginArray.Remove(a);
+            iPluginArray.Compress();
+            return;
+            }
+        }
+    }
+    
+// end of file