mtpfws/mtpfw/dataproviders/dputility/src/cmtpfsenumerator.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 15 Mar 2010 12:43:12 +0200
branchRCL_3
changeset 3 8b094906a049
parent 2 4843bb5893b6
child 4 60a94a45d437
permissions -rw-r--r--
Revision: 201004 Kit: 201010

// Copyright (c) 2007-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 <f32file.h>
#include <bautils.h>
#include <mtp/cmtpdataproviderplugin.h>
#include <mtp/cmtpobjectmetadata.h>
#include <mtp/mmtpdataproviderframework.h>
#include <mtp/mmtpobjectmgr.h>
#include <mtp/mmtpstoragemgr.h>
#include <mtp/mtpprotocolconstants.h>
#include <mtp/tmtptypeevent.h>
#include "cmtpfsexclusionmgr.h"
#include "cmtpfsenumerator.h"
#include "mmtpenumerationcallback.h"
#include "cmtpdataprovidercontroller.h"
#include "cmtpdataprovider.h"
#include "mtpframeworkconst.h"


// Class constants.
__FLOG_STMT(_LIT8(KComponent,"FSEnumerator");)

const TUint KMTPMaxFullFileName = 259;
/*
 * 
 */
#define KMAX_FILECOUNT_ENUMERATINGPHASE1 5000

/**
 * the files should not be owned by any dp. 
 */
#define FILES_OWNED_BY_NONE             0

/**
 * the missed files of other dps should be owned by file dp
 */
#define MISSED_FILES_OWNED_BY_FILE_DP   1

/**
 * the missed files of other dps should be owned by counterparter dps
 */
#define MISSED_FILES_OWNED_BY_OTHER_DP  2

/**
 * the files of other dps should be owned by counterparter dps
 */
#define FILES_OWNED_BY_OTHER_DP         3

/**
Two-phase construction
@param aFramework    data provider framework of data provider
@param aObjectMgr    the reference to the object manager
@param aExclusionMgr    the reference to the exclusion manager 
@param aCallback callback to be called when enumeration is complete
*/
EXPORT_C CMTPFSEnumerator* CMTPFSEnumerator::NewL(MMTPDataProviderFramework& aFramework, CMTPFSExclusionMgr& aExclusionMgr, MMTPEnumerationCallback& aCallback, TInt aProcessLimit)
    {
    CMTPFSEnumerator* self = new (ELeave) CMTPFSEnumerator(aFramework, aExclusionMgr, aCallback, aProcessLimit);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

/**
destructor
*/    
EXPORT_C CMTPFSEnumerator::~CMTPFSEnumerator()
	{
	Cancel();	
	iDir.Close();
	iDirStack.Close();
	iStorages.Close();
	iDpSingletons.Close();
	iSingletons.Close();
	delete iObject;
	__FLOG_CLOSE; 
	}

/**
Kick off the enumeration on the specified storage
@param aStorageId storage to be enumerated
*/
EXPORT_C void CMTPFSEnumerator::StartL(TUint32 aStorageId, TBool aOnlyRoot)
	{
	__ASSERT_DEBUG(!IsActive(), User::Invariant());
	iNumOfFoldersAndFiles = 0;
	iOnlyScanRoot = aOnlyRoot;
	__FLOG_VA((_L8("iOnlyScanRoot == %d "), iOnlyScanRoot));
	
	MMTPStorageMgr& storageMgr(iFramework.StorageMgr());
	if (aStorageId == KMTPStorageAll)
	    {
        // Retrieve the available logical StorageIDs
        RPointerArray<const CMTPStorageMetaData> storages;
        CleanupClosePushL(storages);
        TMTPStorageMgrQueryParams params(KNullDesC, CMTPStorageMetaData::ESystemTypeDefaultFileSystem);
        storageMgr.GetLogicalStoragesL(params, storages);
        
        // Construct the StorageIDs list.
        const TUint KCount(storages.Count());
        for (TUint i(0); (i < KCount); i++)
            {
            iStorages.Insert(storages[i]->Uint(CMTPStorageMetaData::EStorageId),0);
            __FLOG_VA((_L8("FileEnumerator is doing storage id = %x\r\n"), storages[i]->Uint(CMTPStorageMetaData::EStorageId) ));
            }
        CleanupStack::PopAndDestroy(&storages);
	    }
    else if (aStorageId != KMTPNotSpecified32)
        {
		__ASSERT_DEBUG(storageMgr.ValidStorageId(aStorageId), User::Invariant());
		const CMTPStorageMetaData& storage(storageMgr.StorageL(aStorageId));
		if (storage.Uint(CMTPStorageMetaData::EStorageSystemType) == CMTPStorageMetaData::ESystemTypeDefaultFileSystem)
		    {
    	    if (storageMgr.LogicalStorageId(aStorageId))
    		    {
    		    // Logical StorageID.
    			iStorages.AppendL(aStorageId);
    		    }
    		else
    		    {
    		    // Physical StorageID. Enumerate all eligible logical storages.
    		    const RArray<TUint>& logicalIds(storage.UintArray(CMTPStorageMetaData::EStorageLogicalIds));
    		    const TUint KCountLogicalIds(logicalIds.Count());
                for (TUint i(0); (i < KCountLogicalIds); i++)
                    {
                    iStorages.AppendL(logicalIds[i]);
                    }
    		    }
		    }
		}

	iStorageId = aStorageId;
	iSkipCurrentStorage = EFalse;
	
	if (iStorages.Count() > 0)
		{
		 TRAPD(err, ScanStorageL(iStorages[0]));        
		 if(err != KErrNone)
			 {
			 if( !storageMgr.ValidStorageId(iStorages[0]) )
				 {
				 //Scan storage leave because storage(memory card) removed.
				 //Scan next specified storage in RunL, if there is.
				 __FLOG_VA(_L8("StartL - iSkipCurrentStorage - ETrue."));
				 iSkipCurrentStorage = ETrue;
				TRequestStatus* status = &iStatus;
				User::RequestComplete(status, iStatus.Int());
				 SetActive();
				 }
			 else
				 {
				 User::Leave(err);
				 }
			 }
		}
	else
		{
		if((!iIsFileEnumerator) &&(iNumOfFoldersAndFiles > KMAX_FILECOUNT_ENUMERATINGPHASE1))
			{
			iSingletons.DpController().SetNeedEnumeratingPhase2(ETrue);
			}
		
		iCallback.NotifyEnumerationCompleteL(iStorageId, KErrNone);
		
		TMTPTypeEvent event;
		
		event.SetUint16(TMTPTypeEvent::EEventCode, EMTPEventCodeUnreportedStatus);
		event.SetUint32(TMTPTypeEvent::EEventSessionID, KMTPSessionAll);
		
		iFramework.SendEventL(event);
		}
	}
	
/**
Cancel the enumeration process
*/    
void CMTPFSEnumerator::DoCancel()
	{
	iDir.Close();
	}

void CMTPFSEnumerator::ScanStorageL(TUint32 aStorageId)
	{
	__FLOG_VA(_L8("ScanStorageL - entry"));
    const CMTPStorageMetaData& storage(iFramework.StorageMgr().StorageL(aStorageId));
    __ASSERT_DEBUG((storage.Uint(CMTPStorageMetaData::EStorageSystemType) == CMTPStorageMetaData::ESystemTypeDefaultFileSystem), User::Invariant());
    TFileName root(storage.DesC(CMTPStorageMetaData::EStorageSuid));
    
    #ifdef __FLOG_ACTIVE    
	TBuf8<KMaxFileName> tmp;
	tmp.Copy(root);
 	__FLOG_VA((_L8("StorageSuid - %S"), &tmp));	
	#endif // __FLOG_ACTIVE
 	
 	if ( iExclusionMgr.IsFolderAcceptedL(root, aStorageId) )
 	    {
 	    iParentHandle = KMTPHandleNoParent;
 	    iPath.Set(root, NULL, NULL);
 	    User::LeaveIfError(iDir.Open(iFramework.Fs(), iPath.DriveAndPath(), KEntryAttNormal | KEntryAttHidden | KEntryAttDir));
 	    ScanDirL();
 	    }
 	else
 	    {
 	    TRequestStatus* status = &iStatus;
 	    User::RequestComplete(status, iStatus.Int());
 	    SetActive();
 	    }
 	__FLOG_VA(_L8("ScanStorageL - exit"));
	}

/**
Scans directory at aPath recursing into subdirectories on a depth first basis.

Directory entries are kept in iDirStack - which is a LIFO stack.
The current path, needed since TEntries don't keep track of it, 
is kept in iPath.

The algorithm works as follows:

1. Read directory entries.
2. ProcessEntriesL is called if no error occurs and >= 1 entries are read.
3. ProcessEntriesL adds entries to database, if entry is directory add to iDirStack.
4. When all entries are processed pop entry off the dirstack, 
	if entry is empty TEntry remove one directory from iPath.
5. Append entry name onto iPath - to update path with new depth (parent/subdir).
6. Push an empty TEntry onto iDirStack - this tells us we have recursed one,
	think of it as pushing the '\' separator onto iDirStack.
7. Repeat 1-7 until iDirStack is empty.
*/

void CMTPFSEnumerator::ScanDirL()
	{
	__FLOG_VA(_L8("ScanDirL - entry"));
	iFirstUnprocessed = 0;
	iDir.Read(iEntries, iStatus);
	SetActive();
	__FLOG_VA(_L8("ScanDirL - exit"));
	}

void CMTPFSEnumerator::ScanNextStorageL()
	{
	__FLOG_VA(_L8("ScanNextStorageL - entry"));
	// If there are one or more unscanned storages left
	// (the currently scanned one is still on the list)
	if (iStorages.Count() > 1)
		{
		iStorages.Remove(0);
		ScanStorageL(iStorages[0]);
		}
	else
		{
		// We are done
		iStorages.Reset();
		if((!iIsFileEnumerator) &&(iNumOfFoldersAndFiles > KMAX_FILECOUNT_ENUMERATINGPHASE1))
			{
			iSingletons.DpController().SetNeedEnumeratingPhase2(ETrue);
			}
		iCallback.NotifyEnumerationCompleteL(iStorageId, KErrNone);
		
		}
	__FLOG_VA(_L8("ScanNextStorageL - exit"));
	}

void CMTPFSEnumerator::ScanNextSubdirL()
	{
	__FLOG_VA(_L8("ScanNextSubdirL - entry"));
	// A empty (non-constructed) TEntry is our marker telling us to pop a directory 
	// from iPath when we see this
	iDirStack.AppendL(TEntry());
			
	// Leave with KErrNotFound if we don't find the object handle since it shouldn't be on the 
	// dirstack if the entry wasn't added
	TPtrC suid = iPath.DriveAndPath().Left(iPath.DriveAndPath().Length());
	// Update the current parentId with object of the directory
	iParentHandle = iFramework.ObjectMgr().HandleL(suid);
				
	// Kick-off a scan of the next directory
	iDir.Close();
	User::LeaveIfError(iDir.Open(iFramework.Fs(), iPath.DriveAndPath(), KEntryAttNormal | KEntryAttHidden | KEntryAttDir));
	ScanDirL();
	__FLOG_VA(_L8("ScanNextSubdirL - exit"));
	}

/**
Recurse into the next directory on the stack
and scan it for entries.
*/

void CMTPFSEnumerator::ScanNextL()
	{
	__FLOG_VA(_L8("ScanNextL - entry"));
	TInt count = iDirStack.Count();
	
	if ((count == 0) || iOnlyScanRoot )
		{
		// No more directories on the stack, try the next storage
		ScanNextStorageL();
		}
	else
		{
		TEntry& entry = iDirStack[count - 1];
		
		// Empty TEntry, no more subdirectories in
		// the current path
		if (entry.iName == KNullDesC)
			{
			// Remove current dir from path
			iPath.PopDir();
			iDirStack.Remove(count - 1);
			iDir.Close();
			
			// Scan the next directory of the parent
			ScanNextL();
			}
			
		// Going into a subdirectory of current
		else 
			{
			// Add directory to path		
			iPath.AddDir(entry.iName);
			// Remove directory so we don't think it's a subdirectory
			iDirStack.Remove(count - 1);
	
			ScanNextSubdirL();
			}
		}
	__FLOG_VA(_L8("ScanNextL - exit"));
	}

void CMTPFSEnumerator::RunL()
	{
	__FLOG_VA(_L8("RunL - entry"));
	if(iSkipCurrentStorage)
		{
		__FLOG_VA(_L8("RunL - iSkipCurrentStorage - ETrue."));
		iSkipCurrentStorage = EFalse;
		ScanNextStorageL();
		}
	else if (iEntries.Count() == 0)
		{
		// No entries to process, scan next dir or storage
		ScanNextL();
		}
	else if (iFirstUnprocessed < iEntries.Count())
		{
		ProcessEntriesL();
		
		// Complete ourselves with current TRequestStatus
		// since we need to run again to either scan a new dir or drive
		// or process more entries
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, iStatus.Int());
		SetActive();
		}
	else
		{
		switch (iStatus.Int())
			{
			case KErrNone:
				// There are still entries left to be read
				ScanDirL();
				break;
			
			case KErrEof:
				// There are no more entries
			default:
				// Error, ignore and continue with next dir
				ScanNextL();
				break;
			}
		}
	__FLOG_VA(_L8("RunL - exit"));
	}

/**
Ignore the error, continue with the next one
*/    
TInt CMTPFSEnumerator::RunError(TInt aError)
	{
	__FLOG_VA((_L8("RunError - entry with error %d"), aError));
	 if(!iFramework.StorageMgr().ValidStorageId(iStorages[0]))
		 {
		 __FLOG_VA((_L8("Invalid StorageID = %d"),iStorages[0] ));
		 if (iStorages.Count()>1)
			 {
			 //Not necessary to process any entry on the storage, since the storage removed.
			 //Then need to start from root dir of next storage if there is.
			 //So, the dir stack is popped to bottom.
			 iDirStack.Reset();
			 }
		 iSkipCurrentStorage = ETrue;
		 }
	
	// Reschedule ourselves
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, aError);
	SetActive();
	
	__FLOG(_L8("RunError - Exit"));
	return KErrNone;
	}
	
/**
Standard c++ constructor
*/	
CMTPFSEnumerator::CMTPFSEnumerator(MMTPDataProviderFramework& aFramework, CMTPFSExclusionMgr& aExclusionMgr, MMTPEnumerationCallback& aCallback, TInt aProcessLimit)
	: CActive(EPriorityLow),
  	iFramework(aFramework),
  	iExclusionMgr(aExclusionMgr),
    iCallback(aCallback),
    iProcessLimit(aProcessLimit)
	{
	__FLOG_OPEN(KMTPSubsystem, KComponent);
	CActiveScheduler::Add(this);
	}

void CMTPFSEnumerator::ConstructL()
	{
	iSingletons.OpenL();
	iDpSingletons.OpenL(iFramework);
	iObject = CMTPObjectMetaData::NewL();	
	iDpID = iFramework.DataProviderId();
	iIsFileEnumerator = (KMTPFileDPID == iDpID);
	}

/**
Iterates iEntries adding entries as needed to object manager and iDirStack.
*/

void CMTPFSEnumerator::ProcessEntriesL()
	{
	TBuf<KMTPMaxFullFileName> path = iPath.DriveAndPath();
	
	// Start looping through entries at where we left off
	TInt count = iEntries.Count() - iFirstUnprocessed;
	// Process no more than KProcessLimit entries
	count = Min(count, iProcessLimit);
	iFirstUnprocessed += count;		
	
	if(!iIsFileEnumerator)
		{
		iNumOfFoldersAndFiles +=count;
		}	
	
	for (TInt i = (iFirstUnprocessed - count); i < iFirstUnprocessed; ++i)
		{
		const TEntry& entry = iEntries[i];
		path.Append(entry.iName);
		
#ifdef __FLOG_ACTIVE    
		TBuf8<KMTPMaxFullFileName> tmp;
        tmp.Copy(path);
        TInt pathLen=path.Length();
        if(pathLen > KLogBufferSize)
            {
            TBuf8<KLogBufferSize> tmp1;
            tmp1.Copy(tmp.Ptr(),KLogBufferSize);
			__FLOG_VA(_L8("Entry - "));
	        __FLOG_VA((_L8("%S"), &tmp1));

	        tmp1.Copy(tmp.Ptr()+KLogBufferSize, pathLen-KLogBufferSize);
	        __FLOG_VA((_L8("%S"), &tmp1));
            }
        else
            {
            __FLOG_VA(_L8("Entry - "));
			__FLOG_VA((_L8("%S"), &tmp));
            }
#endif // __FLOG_ACTIVE
		
		TInt len = entry.iName.Length();
		TInt totalLen = path.Length();
		if(totalLen > KMaxFileName)
		    {
			// Remove filename part
		    path.SetLength(totalLen - len);
		    __FLOG_VA(_L8("Full name exceeds KMaxFileName, ignored."));
		    continue;
		    }
		TUint32 handle = 0;
		TMTPFormatCode format;
		  TParsePtrC parse(path);
		if (entry.IsDir())
			{
			if (iExclusionMgr.IsFolderAcceptedL(path, iStorages[0]))
				{
				path.Append('\\');
				++len;
				format = EMTPFormatCodeAssociation;
				AddEntryL(path, handle, format, iDpID, entry, iStorages[0], iParentHandle);
				iDirStack.AppendL(entry);
				}
			}
		else if ( iExclusionMgr.IsFileAcceptedL(path,iStorages[0]) )
			{
			format = EMTPFormatCodeUndefined;
			AddEntryL(path, handle, format, iDpID, entry, iStorages[0], iParentHandle);
			}
		else if ( parse.ExtPresent() )
		    {
		    switch(iDpSingletons.MTPUtility().GetEnumerationFlag(parse.Ext().Mid(1)))
		        {
            case MISSED_FILES_OWNED_BY_FILE_DP:
                if (KMTPHandleNone == iFramework.ObjectMgr().HandleL(path))
                    {
                    format = EMTPFormatCodeUndefined;
                    AddEntryL(path, handle, format, iDpID, entry, iStorages[0], iParentHandle);		   
                    }
                break;
                
            case MISSED_FILES_OWNED_BY_OTHER_DP:
                if (KMTPHandleNone == iFramework.ObjectMgr().HandleL(path))
                    {
                    format = iDpSingletons.MTPUtility().GetFormatByExtension(parse.Ext().Mid(1));  
                    TUint32 DpId = iDpSingletons.MTPUtility().GetDpId(parse.Ext().Mid(1), KNullDesC);
                    AddFileEntryForOtherDpL(path, handle, format, DpId, entry, iStorages[0], iParentHandle);
                    }
                break;
                
            case FILES_OWNED_BY_OTHER_DP:
                {
                format = iDpSingletons.MTPUtility().GetFormatByExtension(parse.Ext().Mid(1));  
                TUint32 DpId = iDpSingletons.MTPUtility().GetDpId(parse.Ext().Mid(1), KNullDesC);
                AddFileEntryForOtherDpL(path, handle, format, DpId, entry, iStorages[0], iParentHandle);
                }
                break;
                
//          case FILES_OWNED_BY_NONE:
            default:
                //nothing to do
                break;
		        }    
		    }
		// Remove filename part					
		path.SetLength(path.Length() - len);
		}
		
	}

/**
Add a file entry to the object store
@param aEntry    The file Entry to be added
@param aPath    The full path name of the entry
@return MTP object handle, or KMTPHandleNone if entry was not accepted
*/    
void CMTPFSEnumerator::AddEntryL(const TDesC& aPath, TUint32 &aHandle, TMTPFormatCode format, TUint32 aDPId, const TEntry& aEntry, TUint32 aStorageId, TUint32 aParentHandle)
	{
#ifdef __FLOG_ACTIVE    
	TBuf8<KMaxFileName> tmp;
	tmp.Copy(aPath);
	
	__FLOG_VA((_L8("AddEntryL - entry: %S"), &tmp));
#endif // __FLOG_ACTIVE

    TUint16 assoc;
    TPtrC name;
    if (format == EMTPFormatCodeAssociation)
        {
        assoc = EMTPAssociationTypeGenericFolder;
        TParsePtrC pathParser(aPath.Left(aPath.Length() - 1)); // Ignore the trailing "\".
		name.Set(aEntry.iName);
        }
    else
        {
        assoc = EMTPAssociationTypeUndefined;
        TParsePtrC pathParser(aPath);
		name.Set(pathParser.Name());	
        }
    
    if(iExclusionMgr.IsFormatValid(format))
        {
        aHandle = KMTPHandleNone;
        
        iObject->SetUint(CMTPObjectMetaData::EDataProviderId, aDPId);
        iObject->SetUint(CMTPObjectMetaData::EFormatCode, format);
        iObject->SetUint(CMTPObjectMetaData::EStorageId, aStorageId);
        iObject->SetDesCL(CMTPObjectMetaData::ESuid, aPath);
        iObject->SetUint(CMTPObjectMetaData::EFormatSubCode, assoc);
        iObject->SetUint(CMTPObjectMetaData::EParentHandle, aParentHandle);
        iObject->SetUint(CMTPObjectMetaData::ENonConsumable, EMTPConsumable);
        iObject->SetDesCL(CMTPObjectMetaData::EName, name);
        iFramework.ObjectMgr().InsertObjectL(*iObject);
        }
	__FLOG_VA(_L8("AddEntryL - exit"));	
	}

void CMTPFSEnumerator::AddFileEntryForOtherDpL(const TDesC& aPath, TUint32 &aHandle, TMTPFormatCode format, TUint32 aDPId, const TEntry& /*aEntry*/, TUint32 aStorageId, TUint32 aParentHandle)
    {
#ifdef __FLOG_ACTIVE    
    TBuf8<KMaxFileName> tmp;
    tmp.Copy(aPath);
    
    __FLOG_VA((_L8("AddFileEntryForOtherDpL - entry: %S"), &tmp));
#endif // __FLOG_ACTIVE

    TUint16 assoc = EMTPAssociationTypeUndefined;
    TParsePtrC pathParser(aPath);
    TPtrC name(pathParser.Name());    
    
    aHandle = KMTPHandleNone;
    
    iObject->SetUint(CMTPObjectMetaData::EDataProviderId, aDPId);
    iObject->SetUint(CMTPObjectMetaData::EFormatCode, format);
    iObject->SetUint(CMTPObjectMetaData::EStorageId, aStorageId);
    iObject->SetDesCL(CMTPObjectMetaData::ESuid, aPath);
    iObject->SetUint(CMTPObjectMetaData::EFormatSubCode, assoc);
    iObject->SetUint(CMTPObjectMetaData::EParentHandle, aParentHandle);
    iObject->SetUint(CMTPObjectMetaData::ENonConsumable, EMTPConsumable);
    iObject->SetDesCL(CMTPObjectMetaData::EName, name);
    iFramework.ObjectMgr().InsertObjectL(*iObject);
    __FLOG_VA(_L8("AddEntryL - exit")); 
    }

void CMTPFSEnumerator::NotifyObjectAddToDP(const TUint32 aHandle,const TUint DpId)
    {
    iSingletons.DpController().NotifyDataProvidersL(DpId,EMTPObjectAdded,(TAny*)&aHandle);
    }