appinstaller/AppMngr2/src/appmngr2model.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:22:02 +0100
branchRCL_3
changeset 26 8b7f4e561641
parent 25 7333d7932ef7
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Model implementation
*
*/


#include "appmngr2model.h"              // CAppMngr2Model
#include "appmngr2modelobserver.h"      // CAppMngr2ModelObserver
#include "appmngr2infomaker.h"          // CAppMngr2InfoMaker
#include "appmngr2appinfomaker.h"       // CAppMngr2AppInfoMaker
#include "appmngr2packageinfomaker.h"   // CAppMngr2PackageInfoMaker
#include "appmngr2appinfoarray.h"       // CAppMngr2AppInfoArray
#include "appmngr2packageinfoarray.h"   // CAppMngr2PackageInfoArray
#include "appmngr2scanner.h"            // CAppMngr2Scanner
#include "appmngr2pluginholder.h"       // CAppMngr2PluginHolder
#include <appmngr2runtime.h>            // CAppMngr2Runtime
#include <appmngr2infobase.h>           // CAppMngrInfoBase
#include <appmngr2appinfo.h>            // CAppMngr2AppInfo
#include <appmngr2packageinfo.h>        // CAppMngr2PackageInfo
#include <appmngr2recognizedfile.h>     // CAppMngr2RecognizedFile
#include <appmngr2cleanuputils.h>       // CleanupResetAndDestroyPushL
#include <appmngr2driveutils.h>         // TAppMngr2DriveUtils
#include <appmngr2common.hrh>           // generic command ids
#include <ecom/implementationinformation.h>  // CImplementationInformation
#include <ecom/ecom.h>                  // REComSession
#include <AknIconArray.h>               // CAknIconArray
#include <gulicon.h>                    // CGulIcon
#include <driveinfo.h>                  // DriveInfo
#include <pathinfo.h>                   // Pathinfo
#include <centralrepository.h>          // CRepository
#include <SWInstallerInternalCRKeys.h>  // KCRUidSWInstallerLV
#include <AknsUtils.h>                  // AknsUtils
#include <appmngr2.mbg>                 // icon IDs

_LIT( KAppMngr2BitmapFile, "appmngr2.mif" );
_LIT( KDriveSpec, "%c:" );


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

// ---------------------------------------------------------------------------
// FindDataType()
// ---------------------------------------------------------------------------
//
TBool FindDataType( const TDataType& aDataType, CDataTypeArray& aArray )
    {
    TInt count = aArray.Count();
    for( TInt index = 0; index < count; index++ )
        {
        if( aArray[ index ] == aDataType )
            {
            return ETrue;
            }
        }
    return EFalse;
    }


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

// ---------------------------------------------------------------------------
// CAppMngr2Model::NewL()
// ---------------------------------------------------------------------------
//
CAppMngr2Model* CAppMngr2Model::NewL( RFs& aFsSession,
        MAppMngr2ModelObserver& aObserver )
    {
    CAppMngr2Model* self = new (ELeave) CAppMngr2Model( aFsSession, aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::~CAppMngr2Model()
// ---------------------------------------------------------------------------
//
CAppMngr2Model::~CAppMngr2Model()
    {
    FLOG( "CAppMngr2Model::~CAppMngr2Model" );
    iClosing = ETrue;   // to disable notifications

    Cancel();
    delete iApaAppListNotifier;
    iInfoMakers.ResetAndDestroy();
    delete iScanner;
    delete iInstalledApps;
    delete iInstallationFiles;
    iPlugins.ResetAndDestroy();     // unloads plugin DLLs
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::AppInfoCount()
// ---------------------------------------------------------------------------
//
TInt CAppMngr2Model::AppInfoCount() const
    {
    return iInstalledApps->Count();
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::AppInfo()
// ---------------------------------------------------------------------------
//
CAppMngr2AppInfo& CAppMngr2Model::AppInfo( TInt aIndex ) const
    {
    return *( reinterpret_cast< CAppMngr2AppInfo* >( iInstalledApps->At( aIndex ) ) );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::PackageInfoCount()
// ---------------------------------------------------------------------------
//
TInt CAppMngr2Model::PackageInfoCount() const
    {
    return iInstallationFiles->Count();
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::PackageInfo()
// ---------------------------------------------------------------------------
//
CAppMngr2PackageInfo& CAppMngr2Model::PackageInfo( TInt aIndex ) const
    {
    return *( reinterpret_cast< CAppMngr2PackageInfo* >( iInstallationFiles->At( aIndex ) ) );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::LoadIconsL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::LoadIconsL( CAknIconArray& aIconArray )
    {
    LoadDefaultIconsL( aIconArray );

    TInt pluginCount = iPlugins.Count();
    for( TInt index = 0; index < pluginCount; index++ )
        {
        iPlugins[ index ]->LoadIconsL( aIconArray );
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::GetIconIndexesL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::GetIconIndexesL( TUid aUid, TInt& aIconIndexBase, TInt& aIconIndexMax ) const
    {
    TInt pluginCount = iPlugins.Count();
    for( TInt index = 0; index < pluginCount; index++ )
        {
        if( iPlugins[ index ]->Runtime().RuntimeUid() == aUid )
            {
            aIconIndexBase = iPlugins[ index ]->IconIndexBase();
            aIconIndexMax = iPlugins[ index ]->IconIndexMax();
            return;
            }
        }
    User::Leave( KErrNotFound );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::HandleCommandL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::HandleCommandL( CAppMngr2InfoBase& aInfo, TInt aCommand )
    {
    FLOG( "CAppMngr2Model::HandleCommandL( %d ), IsActive() = %d", aCommand, IsActive() );

    if( !IsActive() )
        {
        // About to start plugin specific command. Note that when the command completes
        // we need to call CAppMngr2InfoBase::CommandComplete() function. For this (and
        // to indicate that there is a plugin specific command on-going) we take the
        // address of CAppMngr2InfoBase item into iActiveItem. Because plugin specific
        // command (like delete or uninstall) may trigger new scanning, it is necessary
        // to disable iInstalledApps and iInstallationFiles array refreshing temporarily.
        // When refreshing is disabled, the item which address is stored in iActiveItem
        // is maintained in arrays and we can call iActiveItem->CommandComplete() when
        // the command is complete. If arrays could be refreshed during plugin specific
        // command, then calling iActiveItem->CommandComplete() would panic.
        if( iObs.InstalledAppsDisplayed() )
            {
            iInstalledApps->DisableRefreshNotificationsL();
            }
        if( iObs.InstallationFilesDisplayed() )
            {
            iInstallationFiles->DisableRefreshNotificationsL();
            }

        iActiveItem = &aInfo;
        iActiveCommand = aCommand;
        FLOG( "CAppMngr2Model::HandleCommandL, iActiveItem = 0x%08x '%S'",
                iActiveItem, &( iActiveItem->Name() ) );
        TRAPD( err, iActiveItem->HandleCommandL( aCommand, iStatus ) );
        FLOG( "CAppMngr2Model::HandleCommandL, command started, err = %d", err );
        SetActive();
        if( err )
            {
            TRequestStatus* statusPtr = &iStatus;
            User::RequestComplete( statusPtr, err );
            }
        }
    // Ignore the command silently if already active. This may happen when user
    // cancels previous installation command. Because cancelling installation can
    // take long time, user may be able to issue new commands while model is still
    // active (i.e. previous installation command is being cancalled).
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::StartFetchingInstallationFilesL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::StartFetchingInstallationFilesL()
    {
    FLOG( "CAppMngr2Model::StartFetchingInstallationFilesL, fetching %d",
            iFetchingInstallationFiles );
    if( !iFetchingInstallationFiles )
        {
        FLOG_PERF_START( FetchInstallationFiles )
        iFetchingInstallationFiles = ETrue;

        // Installation files cache must be enabled until scanner has completed.
        // This ensures that scanner has time to call GetInstallationFilesL() for
        // each plugin and for each directory before the first call completes.
        // If the first call completes before scanner has made all these requets,
        // cache will be turned off and partial results are displayed.
        iInstallationFiles->IncrementCacheUseStartingNewRoundL();

        iScanner->StartScanningL();
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::StartFetchingInstalledAppsL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::StartFetchingInstalledAppsL()
    {
    FLOG( "CAppMngr2Model::StartFetchingInstalledAppsL, fetching %d",
            iFetchingInstalledApps );
    if( !iFetchingInstalledApps )
        {
        FLOG_PERF_START( FetchInstalledApps )
        iFetchingInstalledApps = ETrue;

        // Additional cache increment to ensure that iInstalledApps cache is
        // used until GetInstalledAppsL() function is called for each plugin.
        // Without this, the fastest plugin might get it's list complete before
        // other IncrementCacheUseL() calls and iInstalledApps would display
        // partial list.
        iInstalledApps->IncrementCacheUseStartingNewRoundL();

        TInt pluginCount = iPlugins.Count();
        for( TInt pluginIndex = 0; pluginIndex < pluginCount; pluginIndex++ )
            {
            CAppMngr2AppInfoMaker* appInfoMaker = CAppMngr2AppInfoMaker::NewLC(
                    iPlugins[ pluginIndex ]->Runtime(), *this, iFs );

            TRAPD( err, appInfoMaker->StartGettingInstalledAppsL() );
            FLOG( "CAppMngr2Model::StartFetchingInstalledAppsL, plugin 0x%08x, err = %d",
                    iPlugins[ pluginIndex ]->Runtime().RuntimeUid().iUid, err );
            if( err == KErrNone )
                {
                iInfoMakers.AppendL( appInfoMaker );
                CleanupStack::Pop( appInfoMaker );
                iInstalledApps->IncrementCacheUseL();
                }
            else
                {
                CleanupStack::PopAndDestroy( appInfoMaker );
                }
            }

        // All GetInstalledAppsL() requests have been issued
        iInstalledApps->DecrementCacheUse();
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::IsUninstall()
// ---------------------------------------------------------------------------
//
TBool CAppMngr2Model::IsUninstall()
    {       
    if ( iActiveCommand == EAppMngr2CmdUninstall )
        {
        FLOG( "CAppMngr2Model::IsUninstall: ETrue" );
        return ETrue;
        }   
    FLOG( "CAppMngr2Model::IsUninstall: EFalse" );
    return EFalse;    
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::DoCancel()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::DoCancel()
    {
    FLOG( "CAppMngr2Model::DoCancel, iActiveItem = 0x%08x", iActiveItem );

    if( iActiveItem )
        {
        iActiveItem->CancelCommand();
        iActiveItem = NULL;
        if( !iClosing )
            {
            TRAP_IGNORE( iInstalledApps->EnableRefreshNotificationsL() );
            TRAP_IGNORE( iInstallationFiles->EnableRefreshNotificationsL() );
            }
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::RunL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::RunL()
    {
    FLOG( "CAppMngr2Model::RunL, iActiveItem = 0x%08x, iStatus = %d",
            iActiveItem, iStatus.Int() );

    if( iActiveItem )
        {
        TInt completionCode = iStatus.Int();
        TRAPD( err, iActiveItem->HandleCommandResultL( completionCode ) );
        FLOG( "CAppMngr2Model::RunL, HandleCommandResultL err = %d", err );
        CAppMngr2InfoBase* itemToDelete = iActiveItem;
        iActiveItem = NULL;

        // Enable refresh notifications. No need to check which view is active
        // because notifications can be enabled even if they were not disabled.
        iInstalledApps->EnableRefreshNotificationsL();
        iInstallationFiles->EnableRefreshNotificationsL();

        // Leave on error. This displays error note (if error notes are enabled).
        User::LeaveIfError( err );

        // If the command is EAppMngr2CmdUninstall or EAppMngr2CmdRemove, and it
        // completed without errors, then we remove the current item immediatelty
        // from the displayed list. Otherwise it may take quite long time until
        // the item is removed from the UI, and in worst case it may not be removed
        // at all.
        // For example when an installation file is deleted, scanner notices change
        // in directories and re-scans everything. UI is updated when the scanning
        // completes. Delay between delete command and UI update (deleted item is
        // removed from UI) depends on how quickly scanning completes.
        if( completionCode == KErrNone )
            {
            if( iActiveCommand == EAppMngr2CmdUninstall && iObs.InstalledAppsDisplayed() )
                {
                iInstalledApps->ImmediateDelete( itemToDelete );
                }
            if( iActiveCommand == EAppMngr2CmdRemove && iObs.InstallationFilesDisplayed() )
                {
                iInstallationFiles->ImmediateDelete( itemToDelete );
                }
            }
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::RefreshInstalledApps()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::RefreshInstalledApps()
    {
    FLOG( "CAppMngr2Model::RefreshInstalledApps" );

    TRAP_IGNORE( StartFetchingInstalledAppsL() );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::RefreshInstallationFiles()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::RefreshInstallationFiles()
    {
    FLOG( "CAppMngr2Model::RefreshInstallationFiles" );

    TRAP_IGNORE( StartFetchingInstallationFilesL() );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ScanningResultL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ScanningResultL( RPointerArray<CAppMngr2RecognizedFile>& aResult )
    {
    FLOG( "CAppMngr2Model::ScanningResultL, begin: aResult.Count() = %d", aResult.Count() );

    // Split recognition result array into smaller (plugin specific) arrays. Plugin
    // specific arrays are maintained by CAppMngr2PackageInfoMaker objects, so one
    // CAppMngr2PackageInfoMaker object is needed for each plugin that has recognized
    // files.
    TInt pluginCount = iPlugins.Count();
    for( TInt pluginIndex = 0; pluginIndex < pluginCount; pluginIndex++ )
        {
        CAppMngr2PackageInfoMaker* packageInfoMaker = CAppMngr2PackageInfoMaker::NewLC(
                iPlugins[ pluginIndex ]->Runtime(), *this, iFs );

        TInt fileCount = 0;
        TInt resultCount = aResult.Count();
        for( TInt resultIndex = resultCount - 1; resultIndex >= 0; resultIndex-- )
            {
            CAppMngr2RecognizedFile* recFile = aResult[ resultIndex ];
            if( FindDataType( recFile->DataType(), iPlugins[ pluginIndex ]->DataTypes() ) )
                {
                packageInfoMaker->AddFileL( recFile );  // takes ownership
                aResult.Remove( resultIndex );
                fileCount++;
                }
            }

        FLOG( "CAppMngr2Model::ScanningResultL, plugin 0x%08x: fileCount = %d",
                iPlugins[ pluginIndex ]->Runtime().RuntimeUid().iUid, fileCount );
        if( fileCount > 0 )
            {
            packageInfoMaker->StartGettingInstallationFilesL();
            iInfoMakers.AppendL( packageInfoMaker );
            CleanupStack::Pop( packageInfoMaker );
            iInstallationFiles->IncrementCacheUseL();
            }
        else
            {
            CleanupStack::PopAndDestroy( packageInfoMaker );
            }
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ScanningComplete()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ScanningComplete()
    {
    FLOG( "CAppMngr2Model::ScanningComplete" );

    iInstallationFiles->DecrementCacheUse();
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::DirectoryChangedL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::DirectoryChangedL( const TDesC& /*aChangedDir*/ )
    {
    FLOG( "CAppMngr2Model::DirectoryChangedL" );

    // This might be improved by scanning the changed directory only. Model
    // could record which items are got from which directory, so that it could
    // remove those items that were created from the changed directory and
    // re-scan only the changed directory. Scanner should also support
    // scanning one directory only. Now, we just scan all again.
    StartFetchingInstallationFilesL();
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::HandleAppListEvent()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::HandleAppListEvent( TInt /*aEvent*/ )
    {
    FLOG( "CAppMngr2Model::HandleAppListEvent" );

    TRAP_IGNORE( StartFetchingInstalledAppsL() );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::NewAppsCreatedL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::NewAppsCreatedL( const CAppMngr2InfoMaker& aMaker,
        RPointerArray<CAppMngr2AppInfo>& aAppInfos )
    {
    FLOG( "CAppMngr2Model::NewAppsCreatedL, plugin 0x%08x: packageCount = %d",
            aMaker.RuntimeUid().iUid, aAppInfos.Count() );

    iInstalledApps->AddItemsInOrderL( aAppInfos );
    iInstalledApps->DecrementCacheUse();
    CloseInfoMaker( aMaker );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ErrorInCreatingAppsL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ErrorInCreatingAppsL( const CAppMngr2InfoMaker& aMaker,
#ifdef FLOG_DEBUG_TRACES
        TInt aError )
#else
        TInt /*aError*/ )
#endif
    {
    FLOG( "CAppMngr2Model::ErrorInCreatingAppsL, plugin 0x%08x: error = %d",
            aMaker.RuntimeUid().iUid, aError );

    iInstalledApps->DecrementCacheUse();
    CloseInfoMaker( aMaker );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::NewPackagesCreatedL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::NewPackagesCreatedL( const CAppMngr2InfoMaker& aMaker,
            RPointerArray<CAppMngr2PackageInfo>& aPackageInfos )
    {
    FLOG( "CAppMngr2Model::NewPackagesCreatedL, plugin 0x%08x: packageCount = %d",
            aMaker.RuntimeUid().iUid, aPackageInfos.Count() );

    iInstallationFiles->AddItemsInOrderL( aPackageInfos );
    iInstallationFiles->DecrementCacheUse();
    CloseInfoMaker( aMaker );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ErrorInCreatingPackagesL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ErrorInCreatingPackagesL( const CAppMngr2InfoMaker& aMaker,
#ifdef FLOG_DEBUG_TRACES
        TInt aError )
#else
        TInt /*aError*/ )
#endif
    {
    FLOG( "CAppMngr2Model::ErrorInCreatingPackagesL, plugin 0x%08x: error = %d",
            aMaker.RuntimeUid().iUid, aError );

    iInstallationFiles->DecrementCacheUse();
    CloseInfoMaker( aMaker );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ArrayContentChanged()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ArrayContentChanged( CAppMngr2InfoArray* aArray,
        TInt aMoreRefreshesExpected )
    {
    FLOG( "CAppMngr2Model::ArrayContentChanged, more = %d", aMoreRefreshesExpected );
    if( aArray == iInstalledApps )
        {
        FLOG_PERF_STOP( FetchInstalledApps )
        FLOG_PERF_PRINT( FetchInstalledApps )
        iObs.InstalledAppsChanged( aMoreRefreshesExpected );
        if( !aMoreRefreshesExpected )
            {
            FLOG( "CAppMngr2Model::ArrayContentChanged: StartFetchingInstalledAppsL done" );
            iFetchingInstalledApps = EFalse;
            }
        }
    if( aArray == iInstallationFiles )
        {
        FLOG_PERF_STOP( FetchInstallationFiles )
        FLOG_PERF_PRINT( FetchInstallationFiles )
        iObs.InstallationFilesChanged( aMoreRefreshesExpected );
        if( !aMoreRefreshesExpected )
            {
            FLOG( "CAppMngr2Model::ArrayContentChanged: StartFetchingInstallationFilesL done" );
            iFetchingInstallationFiles = EFalse;
            }
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::CAppMngr2Model()
// ---------------------------------------------------------------------------
//
CAppMngr2Model::CAppMngr2Model( RFs& aFsSession, MAppMngr2ModelObserver& aObserver )
        : CActive( CActive::EPriorityStandard ), iFs( aFsSession ), iObs( aObserver )
    {
    CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::ConstructL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::ConstructL()
    {
    FLOG( "CAppMngr2Model::ConstructL" );
    iClosing = EFalse;

    iInstalledApps = CAppMngr2AppInfoArray::NewL( *this );
    iInstallationFiles = CAppMngr2PackageInfoArray::NewL( *this );

    FLOG_PERF_STATIC_BEGIN( LoadPluginsL )
    LoadPluginsL();
    FLOG_PERF_STATIC_END( LoadPluginsL )
    FLOG_PERF_STATIC_BEGIN( CreateScannerL )
    CreateScannerL();
    FLOG_PERF_STATIC_END( CreateScannerL )
    FLOG_PERF_STATIC_BEGIN( FetchDataTypesL )
    FetchDataTypesL();
    FLOG_PERF_STATIC_END( FetchDataTypesL )

    // start monitoring changes in applications list
    iApaAppListNotifier = CApaAppListNotifier::NewL( this, CActive::EPriorityStandard );
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::LoadDefaultIconsL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::LoadDefaultIconsL( CAknIconArray& aIconArray )
    {
    FLOG( "CAppMngr2Model::LoadDefaultIconsL" );

    MAknsSkinInstance* skinInstance = AknsUtils::SkinInstance();
    HBufC* bitmapFile = TAppMngr2DriveUtils::FullBitmapFileNameLC( KAppMngr2BitmapFile, iFs );
    CFbsBitmap* bitmap = NULL;
    CFbsBitmap* mask = NULL;
    CGulIcon* icon = NULL;

    // Note that icons can be graphically-skinned (icon graphic defined in theme)
    // or color-skinned (icon colors change depending on background color defined
    // in theme). Normal icons are graphically-skinned and indicator icons are
    // color-skinned. AknsUtils::CreateGulIconL() creates graphically-skinned icons,
    // and AknsUtils::CreateColorIconL() creates color-skinned icons. Hence both
    // of these functions are used below.

    // Icon 0: EAppMngr2IconIndex_QgnIndiAmInstMmcAdd
    // Indicator icon for items stored/installed in memory card
    AknsUtils::CreateColorIconLC( skinInstance,
            KAknsIIDQgnIndiMmcAdd, KAknsIIDQsnIconColors, EAknsCIQsnIconColorsCG13,
            bitmap, mask, *bitmapFile,
            EMbmAppmngr2Qgn_indi_mmc_add,
            EMbmAppmngr2Qgn_indi_mmc_add_mask,
            KRgbBlack );
    icon = CGulIcon::NewL( bitmap, mask );
    icon->SetBitmapsOwnedExternally( EFalse );
    CleanupStack::Pop( 2 );   // bitmap and mask, order is varying
    CleanupStack::PushL( icon );
    aIconArray.AppendL( icon );
    CleanupStack::Pop( icon );

    // Icon 1: EAppMngr2IconIndex_QgnIndiFmgrMsAdd
    // Indicator icon for items stored/installed in mass memory
    AknsUtils::CreateColorIconLC( skinInstance,
            KAknsIIDQgnIndiFmgrMsAdd, KAknsIIDQsnIconColors, EAknsCIQsnIconColorsCG13,
            bitmap, mask, *bitmapFile,
            EMbmAppmngr2Qgn_indi_fmgr_ms_add,
            EMbmAppmngr2Qgn_indi_fmgr_ms_add_mask,
            KRgbBlack );
    icon = CGulIcon::NewL( bitmap, mask );
    icon->SetBitmapsOwnedExternally( EFalse );
    CleanupStack::Pop( 2 );   // bitmap and mask, order may vary
    CleanupStack::PushL( icon );
    aIconArray.AppendL( icon );
    CleanupStack::Pop( icon );

    // Icon 2: EAppMngr2IconIndex_QgnPropUnknown
    // List icon for items that are not known
    icon = AknsUtils::CreateGulIconL( skinInstance,
            KAknsIIDQgnPropUnknown, *bitmapFile,
            EMbmAppmngr2Qgn_prop_unknown,
            EMbmAppmngr2Qgn_prop_unknown_mask );
    CleanupStack::PushL( icon );
    aIconArray.AppendL( icon );
    CleanupStack::Pop( icon );

    CleanupStack::PopAndDestroy( bitmapFile );

    // Additionally some unknown indicator icon could be defined.
    // Now, if some plugin gives incorrect index fox indicator icon,
    // then no indicator icon is displayed.
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::LoadPluginsL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::LoadPluginsL()
    {
    FLOG( "CAppMngr2Model::LoadPluginsL" );

    RImplInfoPtrArray implInfoArray;
    CleanupResetAndDestroyPushL( implInfoArray  );
    REComSession::ListImplementationsL( KAppMngr2PluginInterface, implInfoArray );

    CAppMngr2PluginHolder* pluginHolder = NULL;
    CImplementationInformation* implInfo;
    TInt count = implInfoArray.Count();
    for( TInt index = 0; index < count; index++ )
        {
        implInfo = implInfoArray[ index ];
        FLOG( "CAppMngr2Model::LoadPluginsL, loading %S", &( implInfo->DisplayName() ) );
        FLOG_PERF_STATIC_BEGIN( DoLoadPluginL );
        TRAPD( err, pluginHolder = DoLoadPluginL( implInfo->ImplementationUid() ) );
        FLOG_PERF_STATIC_END( DoLoadPluginL );
        FLOG( "CAppMngr2Model::DoLoadPluginL( 0x%08x ), err = %d",
                implInfo->ImplementationUid().iUid, err );
        if( err == KErrNone )
            {
            CleanupStack::PushL( pluginHolder );
            iPlugins.AppendL( pluginHolder );
            CleanupStack::Pop( pluginHolder );
            }
        }

    CleanupStack::PopAndDestroy( &implInfoArray );
    REComSession::FinalClose();
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::DoLoadPluginL()
// ---------------------------------------------------------------------------
//
CAppMngr2PluginHolder* CAppMngr2Model::DoLoadPluginL( TUid aUid )
    {
    CAppMngr2Runtime* plugin = CAppMngr2Runtime::NewL( aUid, *this );
    CleanupStack::PushL( plugin );
    CAppMngr2PluginHolder* holder = new (ELeave) CAppMngr2PluginHolder( plugin );
    CleanupStack::Pop( plugin );
    return holder;
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::CreateScannerL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::CreateScannerL()
    {
    iScanner = CAppMngr2Scanner::NewL( *this );

    TDriveList driveList;
    TInt driveCount = 0;
    TInt err = DriveInfo::GetUserVisibleDrives( iFs, driveList, driveCount );
    User::LeaveIfError( err );

    // Default directories
    TFileName path;
    TInt driveListLength = driveList.Length();
    for( TInt driveNumber = 0; driveNumber < driveListLength; driveNumber++ )
        {
        if( driveList[ driveNumber ] )
            {
            TUint driveStatus = 0;
            err = DriveInfo::GetDriveStatus( iFs, driveNumber, driveStatus );
            User::LeaveIfError( err );

            if( !( driveStatus & DriveInfo::EDriveRemote ) )
                {
                err = PathInfo::GetFullPath( path, driveNumber, PathInfo::EInstallsPath );
                User::LeaveIfError( err );
                iScanner->AddDirectoryL( path );
                }
            }
        }

    // Additional plug-in specific directories
    TInt pluginCount = iPlugins.Count();
    for( TInt pluginIndex = 0; pluginIndex < pluginCount; pluginIndex++ )
        {
        RPointerArray<HBufC> dirsToScan;
        CleanupResetAndDestroyPushL( dirsToScan );
        TRAPD( err, iPlugins[ pluginIndex ]->Runtime().GetAdditionalDirsToScanL( iFs,
                dirsToScan ) );
        if( err == KErrNone )
            {
            TInt dirCount = dirsToScan.Count();
            for( TInt dirIndex = 0; dirIndex < dirCount; dirIndex++ )
                {
                TPtrC dirName = *( dirsToScan[ dirIndex ] );
                iScanner->AddDirectoryL( dirName );
                }
            }
        CleanupStack::PopAndDestroy( &dirsToScan );
        }

    // KSWInstallerPackageFolder directory if defined in CenRep
    CRepository* cenrep = CRepository::NewLC( KCRUidSWInstallerLV );
    err = cenrep->Get( KSWInstallerPackageFolder, path );
    CleanupStack::PopAndDestroy( cenrep );
    if( err == KErrNone )
        {
        TParsePtr parse( path );
        if( parse.DrivePresent() )
            {
            iScanner->AddDirectoryL( path );
            }
        else if( parse.PathPresent() )
            {
            TDriveList driveList;
            TInt driveCount = 0;
            err = DriveInfo::GetUserVisibleDrives( iFs, driveList, driveCount );
            if( err == KErrNone )
                {
                TInt driveListLength = driveList.Length();
                for( TInt driveNumber = 0; driveNumber < driveListLength; driveNumber++ )
                    {
                    if( driveList[ driveNumber ] )
                        {
                        TUint driveStatus = 0;
                        err = DriveInfo::GetDriveStatus( iFs, driveNumber, driveStatus );
                        if( err == KErrNone && !( driveStatus & DriveInfo::EDriveRemote ) )
                            {
                            TChar driveLetter;
                            if( RFs::DriveToChar( driveNumber, driveLetter ) == KErrNone )
                                {
                                TFileName fullPath;
                                fullPath.Format( KDriveSpec, static_cast<TUint>( driveLetter ) );
                                fullPath.Append( path );
                                iScanner->AddDirectoryL( fullPath );
                                }
                            }
                        }
                    }
                }
            }
        else
            {
            // ignore empty values, and values that does not contain valid path
            }
        }
    else
        {
        if( err != KErrNotFound )
            {
            User::Leave( err );
            }
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::FetchDataTypesL()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::FetchDataTypesL()
    {
    FLOG( "CAppMngr2Model::FetchDataTypesL" );

    TInt pluginCount = iPlugins.Count();
    for( TInt pluginIndex = 0; pluginIndex < pluginCount; pluginIndex++ )
        {
        FLOG( "CAppMngr2Model::FetchDataTypesL: pluginIndex = %d", pluginIndex );
        TRAP_IGNORE( iPlugins[ pluginIndex ]->FetchDataTypesL() );
        }
    }

// ---------------------------------------------------------------------------
// CAppMngr2Model::CloseInfoMaker()
// ---------------------------------------------------------------------------
//
void CAppMngr2Model::CloseInfoMaker( const CAppMngr2InfoMaker& aMaker )
    {
    const CAppMngr2InfoMaker* makerToClose = &aMaker;
    for( TInt index = iInfoMakers.Count() - 1; index >= 0; index-- )
        {
        CAppMngr2InfoMaker* maker = iInfoMakers[ index ];
        if( maker == makerToClose )
            {
            iInfoMakers.Remove( index );
            delete maker;
            break;
            }
        }
    }