mtpfws/mtpfw/dataproviders/devdp/src/cmtpstoragewatcher.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:11:40 +0200
changeset 0 d0791faffa3f
child 1 f8e15b44d440
permissions -rw-r--r--
Revision: 201003 Kit: 201005

// Copyright (c) 2006-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:
//

#include <mtp/cmtpdataproviderplugin.h>
#include <mtp/mmtpdataproviderframework.h>
#include <mtp/mtpprotocolconstants.h>
#include <mtp/mtpdataproviderapitypes.h>

#include "cmtpdataprovider.h"
#include "cmtpdataprovidercontroller.h"
#include "cmtpframeworkconfig.h"
#include "cmtpstoragemgr.h"
#include "cmtpobjectmgr.h"
#include "cmtpstoragewatcher.h"
#include "rmtpdevicedpsingletons.h"
#include "cmtpdevicedpconfigmgr.h"


// Class constants.
__FLOG_STMT(_LIT8(KComponent,"StorageWatcher");)
static const TBool KAllDrives(ETrue);
static const TBool KAvailableDrives(EFalse);

const TInt KFolderExclusionGranularity = 8;

/**
MTP system storage watcher factory method.
@return A pointer to an MTP system storage watcher object. Ownership IS 
transfered.
@leave One of the system wide error codes, if a processing failure occurs.
*/
CMTPStorageWatcher* CMTPStorageWatcher::NewL(MMTPDataProviderFramework& aFramework)
    {
    CMTPStorageWatcher* self = new (ELeave) CMTPStorageWatcher(aFramework);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/**
Destructor.
*/    
CMTPStorageWatcher::~CMTPStorageWatcher()
    {
    __FLOG(_L8("~CMTPStorageWatcher - Entry"));
    Cancel();
    delete iFolderExclusionList;
    iDpSingletons.Close();
    iDrivesExcluded.Close();
    iFrameworkSingletons.Close();
    __FLOG(_L8("~CMTPStorageWatcher - Exit"));
    __FLOG_CLOSE;
    }
    
void CMTPStorageWatcher::EnumerateStoragesL()
    {
    __FLOG(_L8("EnumerateStoragesL - Entry"));

    //Use Hash to replace it
    AppendFolderExclusionListL();
    
    // Retrieve the drive exclusion list.
    iFrameworkSingletons.FrameworkConfig().GetValueL(CMTPFrameworkConfig::EExcludedStorageDrives, iDrivesExcluded);
    iDrivesExcluded.Sort();
    
    // Claim system storages ownership.
    CMTPStorageMgr& mgr(iFrameworkSingletons.StorageMgr());
    mgr.SetFrameworkId(iFramework.DataProviderId());
    
    /* 
    Enumerate the initial drive set. 
    
        1.   Enumerate each known drive as a physical storage.
    */
    iDrivesConfig = DriveConfigurationL(KAllDrives);
    CMTPStorageMetaData* storage = CMTPStorageMetaData::NewLC();
    storage->SetUint(CMTPStorageMetaData::EStorageSystemType, CMTPStorageMetaData::ESystemTypeDefaultFileSystem);
    _LIT(KSuidTemplate, "?:");
    RBuf suid;
    suid.CleanupClosePushL();
    suid.Assign((KSuidTemplate().AllocL()));
    
    for (TInt drive(0); (drive < KMaxDrives); drive++)
        {
        const TUint32 mask(1 << drive);
        if (iDrivesConfig & mask)
            {
            TChar driveChar;
            User::LeaveIfError(iFramework.Fs().DriveToChar(drive, driveChar));
            suid[0] = driveChar;
            storage->SetDesCL(CMTPStorageMetaData::EStorageSuid, suid);
                    
            TUint32 id(mgr.AllocatePhysicalStorageIdL(iFramework.DataProviderId(), *storage));
            mgr.SetDriveMappingL(static_cast<TDriveNumber>(drive), id);
            }
        }
        
    CleanupStack::PopAndDestroy(&suid);
    CleanupStack::PopAndDestroy(storage);
    
    /* 
        2.  If so configured, enumerate a single logical storage for each of 
            the available drives.
    */
    if (iAllocateLogicalStorages)
        {
        iDrivesConfig = DriveConfigurationL(KAvailableDrives);

        for (TInt drive(0); (drive < KMaxDrives); drive++)
            {
            const TUint32 mask(1 << drive);
            if (iDrivesConfig & mask)
                {
                StorageAvailableL(static_cast<TDriveNumber>(drive));
                }
            }
        }
    
    // Set the default storage.
    TUint defaultDrive;
    iFrameworkSingletons.FrameworkConfig().GetValueL(CMTPFrameworkConfig::EDefaultStorageDrive, defaultDrive);
    
    if ( defaultDrive <= EDriveZ )
        {
        // Default drive is specified by drive number.. retrieve from manager..
        if (iAllocateLogicalStorages)
            {
            mgr.SetDefaultStorageId(mgr.FrameworkStorageId(static_cast<TDriveNumber>(defaultDrive)));
            }
        else
            {
            mgr.SetDefaultStorageId(mgr.PhysicalStorageId(static_cast<TDriveNumber>(defaultDrive)));
            }
        }
    else
       {
       // Default drive is specified by storage number
       mgr.SetDefaultStorageId(defaultDrive);
       }
       
    __FLOG(_L8("EnumerateStoragesL - Exit"));
    }

/**
Initiates storage change notice subscription.
*/
void CMTPStorageWatcher::Start()
    {
    __FLOG(_L8("Start - Entry"));
    if (!(iState & EStarted))
        {
        __FLOG(_L8("Starting RFs notifier"));
        TRequestStatus* status(&iStatus);
        User::RequestComplete(status, KErrNone);
        SetActive();
        iState |= EStarted;
        }
    __FLOG(_L8("Start - Exit"));    
    }
    
void CMTPStorageWatcher::DoCancel()
    {
    __FLOG(_L8("DoCancel - Entry"));
    __FLOG(_L8("Stopping RFs notifier"));
    iFrameworkSingletons.Fs().NotifyChangeCancel();
    iState &= (!EStarted);
    __FLOG(_L8("DoCancel - Exit"));
    }

/**
Append all DPs folder exclusion list strings in Device DP
 */
void CMTPStorageWatcher::AppendFolderExclusionListL()
    {
    CDesCArraySeg* folderExclusionSets = new (ELeave) CDesCArraySeg(KFolderExclusionGranularity);
    CleanupStack::PushL(folderExclusionSets);
    CMTPDataProviderController& dps(iFrameworkSingletons.DpController());
    TUint currentDpIndex = 0, count = dps.Count();
    while (currentDpIndex < count)
        {
        CMTPDataProvider& dp(dps.DataProviderByIndexL(currentDpIndex));
        if(KMTPImplementationUidDeviceDp != dp.ImplementationUid().iUid)
            {
            folderExclusionSets->Reset();
            dp.Plugin().SupportedL(EFolderExclusionSets,*folderExclusionSets);
            for(TInt i = 0; i < folderExclusionSets->Count(); ++i)
                {
                TPtrC16 excludedFolder = (*folderExclusionSets)[i];
                iFolderExclusionList->AppendL(excludedFolder);
                }
            }
        currentDpIndex++;
        }
    CleanupStack::PopAndDestroy(folderExclusionSets);
    }

/**
Handles leaves occurring in RunL.
@param aError leave error code
@return KErrNone
*/
#ifdef __FLOG_ACTIVE
TInt CMTPStorageWatcher::RunError(TInt aError)
#else
TInt CMTPStorageWatcher::RunError(TInt /*aError*/)
#endif
    {
    __FLOG(_L8("RunError - Entry"));
    __FLOG_VA((_L8("Error = %d"), aError));

    // Ignore the error, meaning that the storages may not be accurately accounted for
    RequestNotification();

    __FLOG(_L8("RunError - Exit"));
    return KErrNone;
    }
    
void CMTPStorageWatcher::RunL()
    {
    __FLOG(_L8("RunL - Entry"));
    const TUint32 previous(iDrivesConfig);
    const TUint32 current(DriveConfigurationL(KAvailableDrives));
    if (current != previous)
        {        
        const TUint32 changed(current ^ previous);
        const TUint32 added(changed & current);
        const TUint32 removed(changed & previous);
        TInt i(KMaxDrives);
        while (i--)
            {
            const TUint32 mask(1 << i);
            if (added & mask)
                {
                StorageAvailableL(static_cast<TDriveNumber>(i));
                }
            else if (removed & mask)
                {
                StorageUnavailableL(static_cast<TDriveNumber>(i));
                }
            }
        }
    iDrivesConfig = current;
    RequestNotification();
    __FLOG(_L8("RunL - Exit"));
    }
    
/**
Constructor.
@param aConnectionMgr The MTP connection manager interface.
*/
CMTPStorageWatcher::CMTPStorageWatcher(MMTPDataProviderFramework& aFramework) :
    CActive(EPriorityStandard),
    iFramework(aFramework)
    {
    CActiveScheduler::Add(this);
    }
    
/**
Second phase constructor.
*/
void CMTPStorageWatcher::ConstructL()
    {
    __FLOG_OPEN(KMTPSubsystem, KComponent);
    __FLOG(_L8("ConstructL - Entry"));
    iFrameworkSingletons.OpenL();
    iFrameworkSingletons.FrameworkConfig().GetValueL(CMTPFrameworkConfig::ELogicalStorageIdsAllocationEnable, iAllocateLogicalStorages);
    
    RMTPDeviceDpSingletons devSingletons;
    devSingletons.OpenL(iFramework);
    CleanupClosePushL(devSingletons);
    
    iDpSingletons.OpenL(iFramework);
    iFolderExclusionList = devSingletons.ConfigMgr().GetArrayValueL(CMTPDeviceDpConfigMgr::EFolderExclusionList); 
    CleanupStack::PopAndDestroy(&devSingletons);
    __FLOG(_L8("ConstructL - Exit"));
    }
    
TUint32 CMTPStorageWatcher::DriveConfigurationL(TBool aAllDrives) const
    {
    __FLOG(_L8("DriveConfigurationL - Entry"));
    TUint32     config(0);
    TDriveList  drives;
    RFs&        fs(iFrameworkSingletons.Fs());
    User::LeaveIfError(fs.DriveList(drives));
    TInt i(KMaxDrives);
    while (i--)
        {        
        __FLOG_VA((_L8("Drive number %d, available = 0x%02d"), i, drives [i]));
        if ((drives[i]) &&
            (!Excluded(static_cast<TDriveNumber>(i))))
            {
            TDriveInfo info;
            User::LeaveIfError(fs.Drive(info, i));
            if ((info.iType != EMediaNotPresent) || (aAllDrives))
                {
                TVolumeInfo volumeInfo;
                if(KErrNone == fs.Volume(volumeInfo,i))
                	{
                	config |=  (1 << i);
                	}
                }
            }
        }
    __FLOG_VA((_L8("Drives list = 0x%08X, AllDrives = %d"), config, aAllDrives));
    __FLOG(_L8("DriveConfigurationL - Exit"));
    return config;
    }

TBool CMTPStorageWatcher::Excluded(TDriveNumber aDriveNumber) const
    {
    __FLOG(_L8("Excluded - Entry"));
    TBool ret(iDrivesExcluded.FindInOrder(aDriveNumber) != KErrNotFound);
    __FLOG_VA((_L8("Drive = %d, excluded = %d"), aDriveNumber, ret));
    __FLOG(_L8("Excluded - Exit"));
    return ret;
    }
    
void CMTPStorageWatcher::RequestNotification()
    {
    __FLOG(_L8("RequestNotification - Entry"));
    _LIT(KPath, "?:\\..");
    iFrameworkSingletons.Fs().NotifyChange(ENotifyEntry, iStatus, KPath);
    SetActive();
    __FLOG(_L8("RequestNotification - Exit"));
    }
 
void CMTPStorageWatcher::SendEventL(TUint16 aEvent, TUint32 aStorageId)
    {
    __FLOG(_L8("SendEventL - Entry"));
    if (iState & EStarted)
        {
        __FLOG_VA((_L8("Sending event 0x%04X for StorageID 0x%08X"), aEvent, aStorageId));
        iEvent.Reset();
        iEvent.SetUint16(TMTPTypeEvent::EEventCode, aEvent);
        iEvent.SetUint32(TMTPTypeEvent::EEventSessionID, KMTPSessionAll);
        iEvent.SetUint32(TMTPTypeEvent::EEventTransactionID, KMTPTransactionIdNone);
        iEvent.SetUint32(TMTPTypeEvent::EEventParameter1, aStorageId);
        iFramework.SendEventL(iEvent);
        }
    __FLOG(_L8("SendEventL - Exit"));
    }

/**
Configures the specified drive as an available MTP storage.
@param aDriveNumber The Symbian OS file system drive number.
@leave One of the system wide error codes, if a processing failure occurs.
*/    
void CMTPStorageWatcher::StorageAvailableL(TDriveNumber aDriveNumber)
    {
    __FLOG(_L8("StorageAvailableL - Entry"));
    __FLOG_VA((_L8("Drive = %d is available."), aDriveNumber));
    CMTPStorageMgr& mgr(iFrameworkSingletons.StorageMgr());
    TInt32 physical(mgr.PhysicalStorageId(aDriveNumber));
    _LIT(KSuidTemplate, "?:");
    // Generate the storage SUID as the drive root folder.
    RBuf suid;
    suid.CleanupClosePushL();
    suid.Assign((KSuidTemplate().AllocL()));
    TChar driveChar;
    User::LeaveIfError(iFramework.Fs().DriveToChar(aDriveNumber, driveChar));
    driveChar.LowerCase();
    suid[0] = driveChar;
    // Create the storage meta-data.
    CMTPStorageMetaData* storage = CMTPStorageMetaData::NewLC();
    storage->SetUint(CMTPStorageMetaData::EStorageSystemType, CMTPStorageMetaData::ESystemTypeDefaultFileSystem);
    storage->SetDesCL(CMTPStorageMetaData::EStorageSuid, suid);
    if(physical == KErrNotFound)
    	{
        TUint32 id(mgr.AllocatePhysicalStorageIdL(iFramework.DataProviderId(), *storage));
        mgr.SetDriveMappingL(aDriveNumber, id);
    	}
    physical = mgr.PhysicalStorageId(aDriveNumber);

    User::LeaveIfError(physical);
    TUint32 logical(physical);

    // If configured to do so, assign a logical storage ID mapping.
    if (iAllocateLogicalStorages)
        {
        __FLOG(_L8("Assigning local storage ID mapping"));
        
        // Try to read from resource file to use a specified root dir path, if available.
        RBuf rootDirPath;
        rootDirPath.CreateL(KMaxFileName);
        rootDirPath.CleanupClosePushL();
        RMTPDeviceDpSingletons devSingletons;
        devSingletons.OpenL(iFramework);
        CleanupClosePushL(devSingletons);
        TRAPD(resError, devSingletons.ConfigMgr().GetRootDirPathL(aDriveNumber, rootDirPath));
        __FLOG_VA((_L8("ResError = %d"), resError));
        if ((KErrNone == resError) && (0 < rootDirPath.Length()))
            {
            __FLOG(_L8("Reading resource file succeeded"));
            // If there is a root directory information in rss file then check the directory exist or not. 
            // If not exists, then create it. 
            // Before doing anything, delete the leading and trailing white space.
            rootDirPath.Trim();       
            TBuf<KMaxFileName> buffer;
            buffer.Append(driveChar);
            _LIT(KSeperator,":");
            buffer.Append(KSeperator);
            buffer.Append(rootDirPath);
            TInt error = iFramework.Fs().MkDir(buffer);
            suid.Close();
            _LIT(KSuidTemplate, "?:\\");
            suid.Assign((KSuidTemplate().AllocL()));
            driveChar.LowerCase();
            suid[0] = driveChar;

            if ((KErrNone == error) || (KErrAlreadyExists == error))
                {
                __FLOG(_L8("Overwriting SUID to specified root dir path from resource file"));  
                //if dir already existed or created, make that as root directory
                suid.ReAllocL(buffer.Length());
                suid = buffer;
                }
            }
        CleanupStack::PopAndDestroy(&devSingletons);
        CleanupStack::PopAndDestroy(&rootDirPath);
        
        // Set up folder exclusion list
        CDesCArraySeg* storageExclusions = new (ELeave) CDesCArraySeg(KFolderExclusionGranularity);
        CleanupStack::PushL(storageExclusions);
        TInt excludedFolderCount = iFolderExclusionList->Count();

        for (TInt i = 0; i < excludedFolderCount; ++i)
            {
            TPtrC16 excludedFolder = (*iFolderExclusionList)[i];

            if (excludedFolder[0] == '?' ||
                excludedFolder[0] == '*' ||
                excludedFolder[0] == suid[0])
                {
                storageExclusions->AppendL(excludedFolder);
                }
            }
        for ( TInt i=0; i<storageExclusions->Count();++i)
            {
            HBufC16* temp = static_cast<TPtrC16>((*storageExclusions)[i]).AllocL();
            TPtr16 tempptr(temp->Des());
            tempptr[0] = suid[0];
            storage->SetHashPath(tempptr,i);
            delete temp;
            }
        
        storage->SetDesCL(CMTPStorageMetaData::EStorageSuid, suid);
        storage->SetDesCArrayL(CMTPStorageMetaData::EExcludedAreas, *storageExclusions);
        CleanupStack::PopAndDestroy(storageExclusions);

        // Create the logical StorageID and drive mapping.
        logical = mgr.AllocateLogicalStorageIdL(iFramework.DataProviderId(), physical, *storage);
        mgr.SetDriveMappingL(aDriveNumber, logical);

        __FLOG_VA((_L8("Drive = %d mapped as storage 0x%08X"), aDriveNumber, logical));
        }

    CleanupStack::PopAndDestroy(storage);
    CleanupStack::PopAndDestroy(&suid);
    
    // Notify the active data providers.
    if (iState & EStarted)
        {
        TMTPNotificationParamsStorageChange params = {physical};
        iFrameworkSingletons.DpController().NotifyDataProvidersL(EMTPStorageAdded, static_cast<TAny*>(&params));
        }

    // Notify any connected Initiator(s).
    if (iAllocateLogicalStorages)
        {
        SendEventL(EMTPEventCodeStoreAdded, logical);
        }
        
    __FLOG(_L8("StorageAvailableL - Exit"));
    }

/**
Configures the specified drive as an unavailable MTP storage.
@param aDriveNumber The Symbian OS file system drive number.
@leave One of the system wide error codes, if a processing failure occurs.
*/    
void CMTPStorageWatcher::StorageUnavailableL(TDriveNumber aDriveNumber)
{
    __FLOG(_L8("StorageUnavailableL - Entry"));
    __FLOG_VA((_L8("Drive = %d is unavailable."), aDriveNumber));
    CMTPStorageMgr& mgr(iFrameworkSingletons.StorageMgr());
    TInt32 physical(mgr.PhysicalStorageId(aDriveNumber));
    User::LeaveIfError(physical);
    TUint32 logical(0);
    
    // If configured to do so, assign a logical storage ID mapping.
    if (iAllocateLogicalStorages)
        {
        logical = mgr.FrameworkStorageId(aDriveNumber);

        // Deassign the logical storage ID mapping.
        mgr.DeallocateLogicalStorageIds(iFramework.DataProviderId(), physical);
        mgr.SetDriveMappingL(aDriveNumber, physical);
        __FLOG_VA((_L8("Drive = %d unmapped as storage 0x%08X"), aDriveNumber, logical));
        }

    // Notify the active data providers.
    TMTPNotificationParamsStorageChange params = {physical};
    iFrameworkSingletons.DpController().NotifyDataProvidersL(EMTPStorageRemoved, static_cast<TAny*>(&params));

    // Notify any connected Initiator(s).
    if (iAllocateLogicalStorages)
        {
        SendEventL(EMTPEventCodeStoreRemoved, logical);
        }   
    __FLOG(_L8("StorageUnavailableL - Exit"));
    }