lowlevellibsandfws/pluginfw/Framework/frame/Discoverer.cpp
changeset 0 e4d67989cc36
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lowlevellibsandfws/pluginfw/Framework/frame/Discoverer.cpp	Tue Feb 02 02:01:42 2010 +0200
@@ -0,0 +1,1007 @@
+// Copyright (c) 1997-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:
+// Contains the implementation of the CDiscoverer class.
+// 
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+#include <e32uid.h>
+#include <startup.hrh> // for EStartupStateNonCritical and EStartupStateCritical
+#include <bacntf.h>
+
+#include <sacls.h>
+
+#include "EComDebug.h"
+#include "TestUtilities.h"	// For __FILE__LINE__
+#include "Discoverer.h"
+#include "DiscovererObserver.h"
+#include "EComUidCodes.h"
+#include "baspi.h"
+#include "bautils.h"
+#include "DriveInfo.h"
+#include <ecom/ecomerrorcodes.h>
+#include <saclscommon.h>
+
+
+#define UNUSED_VAR(a) a = a
+
+
+/** Interface Implementation Collection resource file search path */
+_LIT(KEComResourceFileSearch,	"\\resource\\plugins\\*");
+
+_LIT(KEComResourceFilePathAny,	"\\resource\\plugins\\");
+_LIT(KEComResourceFolderPath,	"?:\\resource\\plugins\\"); 
+
+// Relative to the Drive with a fixed path
+_LIT(KEComSPIFilePath, "\\private\\10009D8F\\");
+
+/** 
+	Begin directory scanning after a delay of 1 Second
+	Allowing multiple directory changes to be applied before
+	beginning a scan.
+ */
+static const TInt32 KEComDefaultBeginScanPeriod	=	1000000;
+
+// __________________________________________________________________________
+//
+CDiscoverer::CSwiChangeNotifier* CDiscoverer::CSwiChangeNotifier::NewL(CDiscoverer& aDiscoverer)
+	{
+	CSwiChangeNotifier* self = new(ELeave) CSwiChangeNotifier(aDiscoverer);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CDiscoverer::CSwiChangeNotifier::CSwiChangeNotifier(CDiscoverer& aDiscoverer)
+: CActive(CActive::EPriorityHigh), iDiscoverer(aDiscoverer)
+	{
+	// Safe because it cannot fail
+	CActiveScheduler::Add(this);
+	}
+
+void CDiscoverer::CSwiChangeNotifier::ConstructL()
+	{
+	// Attach to SWI property
+	User::LeaveIfError(
+        iProperty.Attach(KUidSystemCategory, KSAUidSoftwareInstallKeyValue));
+	}
+
+CDiscoverer::CSwiChangeNotifier::~CSwiChangeNotifier()
+	{
+	Cancel();
+	iProperty.Close();
+	}
+
+void CDiscoverer::CSwiChangeNotifier::DoCancel()
+	{
+	iProperty.Cancel();	// Cancel SWI change notifications
+	}
+
+void CDiscoverer::CSwiChangeNotifier::Subscribe()
+	{
+	if(!IsActive())
+		{
+		iProperty.Subscribe(iStatus);
+		SetActive();
+		}
+	}
+
+void CDiscoverer::CSwiChangeNotifier::RunL()
+	{
+	Subscribe();
+	
+	TInt swiProperty;
+	User::LeaveIfError(
+        iProperty.Get(KUidSystemCategory, KSAUidSoftwareInstallKeyValue, swiProperty));
+
+	// Do a discovery each time an install, uninstall or restore is completed.
+	iDiscoverer.SwiChangeNotificationL(swiProperty);	
+	}
+
+TInt CDiscoverer::CSwiChangeNotifier::RunError(TInt /*aError*/)
+	{
+	//If unable to read the SWI P&S variable set the 
+	//discoverers SWI state to ESASwiNone as this will return
+	//EComs back to its default behaviour
+	TRAP_IGNORE(iDiscoverer.SwiChangeNotificationL(ESASwisNone));	
+	return KErrNone; //avoid CActiveScheduler panic
+	}
+
+// __________________________________________________________________________
+//
+/*
+	The notification object which watches the Interface Implementation 
+	Collection directories for any changes on specific drive.
+	When its RunL method is called, it notifies its owning CDiscoverer class
+	object to re-scan of the Interface Implementation Collection directories.
+*/
+CDiscoverer::CDirChangeNotifier::CDirChangeNotifier(CDiscoverer& aDiscoverer, RFs& aFs, const TDriveUnit& aDriveUnit)
+: CActive(CActive::EPriorityHigh), iDiscoverer(aDiscoverer), iFs(aFs),iDriveUnit(aDriveUnit)
+	{
+	
+	iNotificationFilePath.Append(iDriveUnit.Name());
+	iNotificationFilePath.Append(KEComResourceFilePathAny);
+	// Safe because it cannot fail
+	CActiveScheduler::Add(this);
+	}
+
+CDiscoverer::CDirChangeNotifier::~CDirChangeNotifier()
+	{
+	Cancel();
+	}
+
+void CDiscoverer::CDirChangeNotifier::DoCancel()
+	{
+	iFs.NotifyChangeCancel(iStatus);	// Cancel change notifications
+	}
+
+void CDiscoverer::CDirChangeNotifier::Activate()
+	{
+	if(!IsActive())
+		{
+		iStatus = KRequestPending;
+		SetActive();
+		iFs.NotifyChange(ENotifyEntry, iStatus, iNotificationFilePath); 
+		}
+	}
+
+void CDiscoverer::CDirChangeNotifier::RunL()
+	{
+	RECORD_START_NOTIFIER_RUNL_TIMER_RESULT(iDriveUnit)
+	// Signal the notification
+	// If iStatus.Int() is not KErrNone
+	// then reactivation will not occur
+	if(iDiscoverer.NotificationL(iStatus.Int(), iDriveUnit))
+		Activate();
+	RECORD_END_NOTIFIER_RUNL_TIMER_RESULT(iDriveUnit)
+	}
+
+TInt CDiscoverer::CDirChangeNotifier::RunError(TInt aError)
+	{
+	// Entered most likely because of an error condition during file system
+    // rescanning and plugin registration that could not be handled locally. 
+    // As indexes in the registry are updated after each registration and the
+	// tree of registrations is updated at the end for some scan use-cases there
+	// is a chance that the registration tree and the indexes can get out of 
+    // sync when a leave occurs. 
+    // The code is not present to handle a recovery so the best policy 
+    // is to panic the server and have it restart on next use.
+    // We can't trap leaves in RunL() and continue as we can not be sure the 
+    // registry is in sync. The registry and discovery code would need to be 
+    // totally reviewed and reworked if panic's were not acceptable. So far they 
+    // have allowed error conditions found in the field to be reported as 
+    // incidents allowing us to idenitify and resovle the underlying causes.
+	__ECOM_LOG1("ECOM: PANIC in CDiscoverer::CDirChangeNotifier::RunError(), error= %d", aError);
+	User::Panic(KEComServerPanicCategory, EEComPanic_CDiscoverer_CDirChangeNotifier_RunError);
+	return KErrNone;   // dummy return to stop warnings on missing return
+	}
+
+// __________________________________________________________________________
+//
+/*
+	The timer Active object for providing plugin directory scanning on rediscovery events. 
+	The processing of notification will be performed only on drive(s) that has notification event(s)
+	triggered on it.
+	It uses data member iPendingDriveList to hold all pending drive nums, and executes only once. 
+	It is activated by the CDirChangeNotifier's notification call. 
+	The default priority is idle time execution only.
+*/
+CDiscoverer::CIdleScanningTimer* CDiscoverer::CIdleScanningTimer::NewL(CDiscoverer& aDiscoverer)
+	{
+	CIdleScanningTimer* self = new(ELeave) CIdleScanningTimer(aDiscoverer);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+CDiscoverer::CIdleScanningTimer::CIdleScanningTimer(CDiscoverer& aDiscoverer)
+: CTimer(CActive::EPriorityIdle), iDiscoverer(aDiscoverer), iPendingDriveList(2)
+	{
+	// Safe because it cannot fail
+	CActiveScheduler::Add(this);
+	}
+
+void CDiscoverer::CIdleScanningTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	}
+
+CDiscoverer::CIdleScanningTimer::~CIdleScanningTimer()
+	{
+	Cancel();
+	iPendingDriveList.Close();
+	}
+
+void CDiscoverer::CIdleScanningTimer::DoCancel()
+	{
+	// Call the base class to ensure the timer is cancelled
+	CTimer::DoCancel();
+
+	iDiscoverer.ScanDirectoryCancel();
+	}
+
+void CDiscoverer::CIdleScanningTimer::RunL()
+//	When the object activates on a specfic drive, this is method is called
+//  and delegates to the CDiscoverer to scan the Interface Implementation 
+//	Collection directories
+//
+	{
+	// Only carry out a rediscovery if SWI is not in progress
+	if(!iDiscoverer.SwiOperationInProgress()) 
+		{
+		RECORD_START_TIMER_RUNL_TIMER_RESULT
+		// Do scan on all pending drives stored in iPendingDriveList array
+		TInt length = iPendingDriveList.Count();
+		for(TInt count = 0; count < length; ++count)
+			{
+			iDiscoverer.RediscoveryScanDirectoryL(TDriveUnit(iPendingDriveList[count]));
+			}
+	
+		// Signal the observer that the scans have been completed successfully.
+		iDiscoverer.iDiscovererObserver.DiscoveriesComplete(ETrue, EPluginProcessingTypeAll);
+		// Reset pending drive list when finishes scan.
+		iPendingDriveList.Reset();
+		// Reset the state of discoverer as all notifications processed.
+		iDiscoverer.CompleteNotificationProcessing();
+		RECORD_END_TIMER_RUNL_TIMER_RESULT
+		}
+	}
+
+TInt CDiscoverer::CIdleScanningTimer::RunError(TInt aError)
+	{
+	// Entered most likely because of an error condition during file system
+    // rescanning and plugin registration that could not be handled locally. 
+    // As indexes in the registry are updated after each registration and the
+	// tree of registrations is updated at the end for some scan use-cases there
+	// is a chance that the registration tree and the indexes can get out of 
+    // sync when a leave occurs. 
+    // The code is not present to handle a recovery so the best policy 
+    // is to panic the server and have it restart on next use.
+    // We can't trap leaves in RunL() and continue as we can not be sure the 
+    // registry is in sync. The registry and discovery code would need to be 
+    // totally reviewed and reworked if panic's were not acceptable. So far they 
+    // have allowed error conditions found in the field to be reported as 
+    // incidents allowing us to idenitify and resovle the underlying causes.
+	__ECOM_LOG1("ECOM: PANIC in CDiscoverer::CIdleScanningTimer::RunError(), error = %d", aError);
+	User::Panic(KEComServerPanicCategory, EEComPanic_CDiscoverer_CIdleScanningTimer_RunError);
+	return KErrNone;	// dummy return to stop warnings on mising return
+	}
+	
+void CDiscoverer::CIdleScanningTimer::RestartScanPeriod()
+	{
+	if (!iSuspended)
+		{
+		Cancel();
+		After(KEComDefaultBeginScanPeriod);
+		}
+	}
+	
+void CDiscoverer::CIdleScanningTimer::Suspend()
+	{
+	Cancel();
+	iSuspended = ETrue;
+	}
+	
+void CDiscoverer::CIdleScanningTimer::Resume()
+	{
+	iSuspended = EFalse;
+	if(IsAnyNotificationProcessingPending())
+		{
+		RestartScanPeriod();
+		}
+	}
+// __________________________________________________________________________
+//
+/*
+		CDirScanner implements incremental scanning of the Interface Implementation 
+		Collection directory 
+		on behalf of the CDiscoverer.
+		It's methods are called in response to the timer task execution,
+		thereby requiring the incremental scheduling.
+*/
+CDiscoverer::CDirScanner* CDiscoverer::CDirScanner::NewL(CDiscoverer& aDiscoverer, RFs& aFs)
+	{
+	CDirScanner* self = new(ELeave)CDirScanner(aDiscoverer,aFs);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CDiscoverer::CDirScanner::ConstructL()
+	{
+	}
+
+CDiscoverer::CDirScanner::CDirScanner(CDiscoverer& aDiscoverer, RFs& aFs)
+: CBase(), iDiscoverer(aDiscoverer), iFs(aFs)
+	{
+	}
+
+CDiscoverer::CDirScanner::~CDirScanner()
+// D'tor
+	{
+	}
+	
+
+void CDiscoverer::CDirScanner::ScanDriveL(const TDriveUnit& aDrive,  TBool aIsRO)
+	{
+	RECORD_START_REDISCOVERYSCANDIRECTORY_RESULT(aDrive)
+	TDriveName driveName(aDrive.Name());
+	TBool scanDirectoryForPlugins = ETrue;
+	TBool found = EFalse;
+
+	
+	// If RO then attempt to discover plugins from SPI file
+	if(aIsRO)
+		{
+		TFileName spiFilePath;
+		spiFilePath.Append(driveName);
+		spiFilePath.Append(KEComSPIFilePath);
+		
+		TEntry entry;
+		//check if the path exists
+		if (iFs.Entry(spiFilePath,entry)==KErrNone)
+			{
+			TParse spiPath;
+			spiPath.Set(spiFilePath, NULL, NULL);
+			// Discover plugins from SPI
+			found = DoScanSpiFileL(spiPath);
+			}
+		scanDirectoryForPlugins = !found;
+		}
+ 
+ 	// scan directory for plugins if not already discovered from SPI file. SPI applies to RO.
+ 	if(scanDirectoryForPlugins)
+ 		{
+ 	
+		// Find plugins via resoure files
+		TUidType rscUidType(KNullUid,KUidInterfaceImplementationCollectionInfo,KNullUid);
+		TBool foundRsc = DoScanDriveL(aDrive, rscUidType, aIsRO);
+		found = found || foundRsc; 
+ 		}
+
+	if (!found)
+		{
+		iDiscoverer.DriveUnmountedL(aDrive);
+		}
+	RECORD_END_REDISCOVERYSCANDIRECTORY_RESULT(aDrive)
+	}
+
+TBool CDiscoverer::CDirScanner::DoScanSpiFileL(const TParse& aSpiPath)
+{
+	iDiscoverer.DriveMountedL(aSpiPath.Drive());
+
+	RResourceArchive resourceArchive;
+	//ECom server should continue if OpenL leaves because no spi exists 
+	TRAPD(err,resourceArchive.OpenL(iFs, aSpiPath.DriveAndPath(),_L("ecom")));
+	if(err==KErrNotFound || err==KErrPathNotFound)
+		return EFalse;
+	User::LeaveIfError(err);
+	CleanupClosePushL(resourceArchive);
+	// check SPI file type. On failure do not scan archives
+	if(resourceArchive.Type() != KEcomSpiFileTypeUid)
+		{
+		CleanupStack::PopAndDestroy(&resourceArchive);
+		return EFalse;
+		}
+	
+	CPluginBase* entryBase=NULL;
+	TBool resourceExistsIndicator = EFalse;
+	while(!resourceArchive.End())
+		{
+		TRAPD(error,iDiscoverer.ValidateEntryL(resourceArchive,entryBase));
+		CleanupStack::PushL(entryBase);
+		if (error==KErrNoMemory)
+			User::LeaveNoMemory();
+		if (error==KErrNone)
+			{
+			// When SPI is on no DAT file exists,and also RO Internal drive is not rediscovered. 
+			//Therefore this RO Internal drive is always at its initial discovery. No Dll
+			// is ever discovered before. Always pass EFalse to ProcessEntryL method.
+			iDiscoverer.ProcessEntryL(aSpiPath.Drive(),entryBase, EFalse);
+			// set to indicate at least 1 resource exists
+			resourceExistsIndicator = ETrue;	
+			}
+		else
+			{
+			__ECOM_TRACE1("ECOM: CDiscoverer::DoScanSpiFileL(). Fail Validate: %S\n.",&aSpiPath.FullName());
+			}	
+		CleanupStack::PopAndDestroy(entryBase);
+		entryBase=NULL;
+		}
+	CleanupStack::PopAndDestroy(&resourceArchive);
+	return resourceExistsIndicator;
+}
+
+TBool CDiscoverer::CDirScanner::DoScanDriveL(const TDriveUnit& aDrive, const TUidType& aUidType, TBool aIsRO)
+	{	
+	RDir dir;
+	
+	TDriveName driveName(aDrive.Name());
+	TParse searchDir;
+	User::LeaveIfError(searchDir.Set(KEComResourceFileSearch,NULL,&driveName));
+
+	// Match the directory list UID's to a Polymorphic DLL UID and Interface
+	// Implementation Collection UID.
+	// Resource files are sorted by UID. However, since these files have same UID,
+	// they are actually sorted by their names (alphanumerically).
+
+  	TInt error = dir.Open(iFs, searchDir.FullName(), aUidType);
+ 
+	if(error == KErrNone)
+		{
+		// Have found the plugin directory
+		CleanupClosePushL(dir);
+		
+		TFileName* lastRscNameBuf = new TFileName;
+		
+		if (!lastRscNameBuf) 
+		{
+			CleanupStack::PopAndDestroy(&dir); 
+			return EFalse;
+		}
+		CleanupStack::PushL(lastRscNameBuf);
+		
+		TEntryArray *dirEntriesArray = new TEntryArray;
+	 
+		if (!dirEntriesArray) 
+		{
+			CleanupStack::PopAndDestroy(lastRscNameBuf); 
+			CleanupStack::PopAndDestroy(&dir); 
+			return EFalse;
+		}
+		CleanupStack::PushL(dirEntriesArray);
+				
+		
+		TPtrC lastRscName(KNullDesC);
+		
+		// Iterate through the directory reading multiple entries at a 
+		// time
+		TInt count = 0;
+		TInt readError = KErrNone;
+		CPluginBase* entryBase=NULL;
+
+ 		iDiscoverer.DriveMountedL(aDrive);
+		TBool anyDllRegistered = iDiscoverer.IsAnyDllRegisteredWithDriveL(aDrive);
+ 
+	 
+
+		while (readError != KErrEof)  
+			{
+		
+ 			// Read the next set of entries
+ 			readError =	dir.Read(*dirEntriesArray);
+ 				
+			if ((readError != KErrNone) &&  (readError != KErrEof))
+				{
+				User::Leave(readError);	
+				}
+			else 
+				{
+ 				// for KErrEof, dirEntriesArray still has items to process 
+				count = dirEntriesArray->Count();
+ 				// Ok use the entries to populate the file list
+				for(TInt i = 0; i < count; ++i)
+					{
+ 
+ 					// Compare current file name against previous one ignoring extension. If it is same
+					// then there is no need to process it.
+					TPtrC currName = (*dirEntriesArray)[i].iName.Left((*dirEntriesArray)[i].iName.Length()-KExtensionLength);
+					if (lastRscName.Compare(currName) == 0)
+						{
+						continue;
+						}
+					else if (i < (count - 1))
+						{
+						lastRscName.Set(currName);
+						}
+					else
+						{
+						lastRscNameBuf->Copy(currName);
+						lastRscName.Set(*lastRscNameBuf);
+						}
+						
+ 
+					// Obtain a copy of the current directory entry
+					TRAP(error,iDiscoverer.ValidateEntryL((*dirEntriesArray)[i], driveName, entryBase, aIsRO));			
+					CleanupStack::PushL(entryBase);		
+ 
+					if (error==KErrNoMemory) 
+						User::LeaveNoMemory();
+				
+					if (error==KErrNone)
+						{
+						iDiscoverer.ProcessEntryL(driveName,entryBase,anyDllRegistered);
+ 						}
+					else
+						{
+						__ECOM_TRACE1("ECOM: CDiscoverer::DoScanDriveL(). Fail Validate entry: %S\n.",&(*dirEntriesArray)[i].iName);
+						}		
+				    CleanupStack::PopAndDestroy(entryBase);
+					entryBase=NULL;
+					}
+				}
+			}
+ 		CleanupStack::PopAndDestroy(dirEntriesArray); 
+		CleanupStack::PopAndDestroy(lastRscNameBuf); 
+		CleanupStack::PopAndDestroy(&dir); 
+		return ETrue; 
+		}
+
+	return EFalse;
+	}
+
+void CDiscoverer::CDirScanner::DiscoverPluginsL(TBool aDiscoverReadOnlyDrives)
+	{
+	// iterator which returns only the drives need to be scanned.
+	TEComCachedDriveInfoIterator iter(*iDiscoverer.iCachedDriveInfo);
+
+	// Iterate from highest drive letter (Z:) towards lowest drive letter (A:).
+	for (iter.Last(); iter.InRange(); iter.Prev())
+		{
+		if (iter.DriveIsReadOnlyInternal() == aDiscoverReadOnlyDrives)
+			{
+			ScanDriveL(iter.DriveUnit(), aDiscoverReadOnlyDrives);
+			}
+		}
+	}
+	
+
+// __________________________________________________________________________
+//
+/*
+	Responsible for identifying new Interface Implementation Collections,
+	installed in the Interface Implementation Collection directories.
+*/
+
+CDiscoverer* CDiscoverer::NewL(MDiscovererObserver& aDiscovererObserver, RFs& aFs)
+	{
+	CDiscoverer* self = new(ELeave) CDiscoverer(aDiscovererObserver, aFs);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+// Default d'tor
+
+CDiscoverer::~CDiscoverer()
+	{
+	// Cancel any scanning behaviour or notifications
+	if(iDirScanner != NULL)
+		{
+		// Left in the middle of a scan
+		// So clear up
+		delete iDirScanner;
+		iDirScanner = NULL;
+		// Signal the observer that the scan has 
+		// not been completed successfully.
+		iDiscovererObserver.DiscoveriesComplete(EFalse, EPluginProcessingTypeAll);
+		}
+	Suspend();
+	iDrivesDiscovered.Reset();
+	delete iSwiChangeNotifier;
+	delete iScanningTimer;
+	delete iLanguageChangeNotifier;
+	delete iCachedDriveInfo;
+	iRscDirNotifierList.ResetAndDestroy();
+
+	}
+
+// Default c'tor
+CDiscoverer::CDiscoverer(MDiscovererObserver& aDiscovererObserver, RFs& aFs)
+: CBase(),   iSwiChangeDiscoveryPending(EFalse), iLanguageChangeDiscoveryPending(EFalse),
+  iState(EDisc_Undefined), iDiscovererObserver(aDiscovererObserver), iFs(aFs)
+	{
+	// Do nothing here
+	}
+
+void CDiscoverer::ConstructL()
+	{
+	iCachedDriveInfo = CEComCachedDriveInfo::NewL(iFs);
+	
+	// Construct the Interface Implementation Collection
+	// directory change notifier list
+	// and the scan step control object.
+
+	CDirChangeNotifier *dirChangeNotifierPtr;
+
+	// iterator which returns only the drives need to be scanned.
+	TEComCachedDriveInfoIterator iter(*iCachedDriveInfo);
+
+	for (iter.First(); iter.InRange(); iter.Next())
+		{
+		//Don't need to monitor read-only drives. They don't change.
+		if ( !iter.DriveIsReadOnlyInternal() )
+			{				
+			dirChangeNotifierPtr = new(ELeave)CDirChangeNotifier(*this,iFs,iter.DriveUnit());
+		
+			CleanupStack::PushL(dirChangeNotifierPtr);				
+			iRscDirNotifierList.AppendL(dirChangeNotifierPtr);
+			CleanupStack::Pop();
+			}
+		}
+	iSwiChangeNotifier = CSwiChangeNotifier::NewL(*this);
+
+	iScanningTimer = CIdleScanningTimer::NewL(*this);
+
+	//Create the language change notifier and install the callback function
+	const TCallBack myCallBackFunction(&CDiscoverer::LocaleChangedL, this);
+	iLanguageChangeNotifier = CEnvironmentChangeNotifier::NewL(CActive::EPriorityStandard, myCallBackFunction);
+	
+	iDirScanner = CDirScanner::NewL(*this,iFs);	
+
+	InitialiseEvent();	
+	}
+
+
+TInt CDiscoverer::Resume()
+	{
+	// Reactivate the scanning timer if not NULL
+	if (iScanningTimer != NULL)
+		{
+		iScanningTimer->Resume();
+		}
+	
+	TCallBackState cbData = ECallBackState_EventEnd;
+	iBurChangeCallBack.CallBack(ECallBackId_BurEvent, &cbData);
+
+	/*
+	iLanguageChangeNotifier is not activated because it is not cancelled during CDiscoverer::Suspend().
+	It is not suspended because a language change should not occur whilst a backup/restore operation
+	is taking place.
+	*/
+
+	return KErrNone;
+	}
+
+
+TInt CDiscoverer::Suspend()
+	{
+	// Suspend the scanning timer if not NULL
+	if (iScanningTimer != NULL)
+		{
+		iScanningTimer->Suspend();
+		}
+
+	TCallBackState cbData = ECallBackState_EventStart;
+	iBurChangeCallBack.CallBack(ECallBackId_BurEvent, &cbData);
+
+	/*
+	iLanguageChangeNotifier is not cancelled because a language change should not occur
+	whilst a backup/restore operation is taking place.
+	*/
+
+	return KErrNone;
+	}
+
+
+TBool CDiscoverer::NotificationL(TInt aStatus, const TDriveUnit& aDriveUnit)
+	{
+
+	TBool okToContinue = ETrue;
+	if(aStatus != KErrNone)
+		{
+		// Big trouble with the notification
+		// Tell our observer
+		// Notifications will cease if EFalse is returned!!!!
+		okToContinue = iDiscovererObserver.NotifiedWithErrorCode(aStatus);	
+		}
+	else
+		{
+		//call ProcessDNEventL() to indicate Plugins have been added or removed on a specfic drive,
+		// then do the state transition and to start a re-discovery .
+		ProcessDNEventL(EPluginsModified,aDriveUnit );
+		ProcessDNEventL(EPluginsRediscover, aDriveUnit);
+		}
+	return okToContinue;
+	}
+
+void CDiscoverer::SwiChangeNotificationL(TInt aSwiOperation)
+	{
+	// Store the current SWI operation, ignore operation status
+	iSwiOperation = aSwiOperation & KSASwisOperationMask;
+	
+	TCallBackState cbData = SwiOperationInProgress() ? ECallBackState_EventStart : ECallBackState_EventEnd;
+	iSwiChangeCallBack.CallBack(ECallBackId_SwiEvent, &cbData);
+
+	// Test no SWI operation in progress
+	if(!SwiOperationInProgress())
+		{
+		TBool rediscoveryPending = EFalse;
+		if(!iSwiChangeDiscoveryPending)
+			{		 
+			// for each removable drive call ProcessDNEventL() to do the state transition and to start
+			// a re-discovery for that drive.
+			TInt count = iDrivesDiscovered.Count();		
+			for(TInt i=0; i < count; i++)
+				{
+				TDriveUnit drvUnit(iDrivesDiscovered[i]);
+				if(iCachedDriveInfo->DriveIsRemovableL(drvUnit))
+					{
+					rediscoveryPending = ETrue;
+					ProcessDNEventL(EPluginsModified, drvUnit );
+					ProcessDNEventL(EPluginsRediscover, drvUnit);
+					iSwiChangeDiscoveryPending = ETrue;
+					}
+				}
+			}
+		
+		//If there are no removable drives to be scanned check if there are any
+		//pending notifications that couldn't be processed during SWI
+		if(!rediscoveryPending && iScanningTimer->IsAnyNotificationProcessingPending())
+			{
+			// Activate timer if there is any notification processing pending
+			iScanningTimer->RestartScanPeriod();
+			}
+		}
+	}
+
+TBool CDiscoverer::SwiOperationInProgress()
+	{
+	return (iSwiOperation != ESASwisNone);
+	}
+
+void CDiscoverer::LanguageChangeNotificationL()
+	{
+	if (!iLanguageChangeDiscoveryPending)
+		{
+		// for each drive call ProcessDNEventL() to do the state transition and to start
+		// a re-discovery for that drive.
+		TInt count = iDrivesDiscovered.Count();
+		for(TInt i=0; i < count; i++)
+			{
+			ProcessDNEventL(EPluginsModified, iDrivesDiscovered[i] );
+			ProcessDNEventL(EPluginsRediscover, iDrivesDiscovered[i]);
+			}
+		iLanguageChangeDiscoveryPending = ETrue;
+		}
+	}
+void CDiscoverer::RediscoveryScanDirectoryL(const TDriveUnit& aDriveUnit) 
+	{
+	TBool doScan = EFalse;
+	if(iDrivesDiscovered.Find(aDriveUnit) != KErrNotFound)
+		{
+		// If the drive has plugins on it previously, do ScanDriveL on any notifications.
+		doScan = ETrue;
+		}
+	else // Otherwise the drive doesn't contain any plugin before, do further check.
+		{
+		TBuf<KEComPlugRSCPathMaxLen> pluginsDirPath(KEComResourceFolderPath);
+		pluginsDirPath[0] = ('A' + TInt(aDriveUnit));
+		TEntry entry;
+		if(iFs.Entry(pluginsDirPath,entry) == KErrNone)
+			{
+			// Now it has plugins folder on it, do ScanDriveL.
+			doScan = ETrue;
+			}
+		// If it still doesn't have plugins folder on it, skip unnecessary scanning.
+		// NOTE: other returned error code could be KErrPathNotFound, KErrNotReady etc.
+		//  As long as no plugin has been found, always skip scanning on this drive.
+		}
+	
+	// Performs scanning according to above checks.
+	if(doScan)
+		{
+		// Signal the observer that a scan has commenced.
+		iDiscovererObserver.DiscoveriesBegin();	
+		
+		iDirScanner->ScanDriveL(aDriveUnit, iCachedDriveInfo->DriveIsReadOnlyInternalL(aDriveUnit));
+		
+		// Signal the observer that the scan has 
+		// been completed successfully.
+		iDiscovererObserver.SetDiscoveryFlagL(aDriveUnit);
+		}
+	}
+
+void CDiscoverer::ScanDirectoryCancel()
+	{
+	if(iDirScanner != NULL)
+		{
+		// Signal the observer that the scan has 
+		// been completed un-successfully.
+		iDiscovererObserver.DiscoveriesComplete(EFalse, EPluginProcessingTypeAll);
+		}
+	}
+
+
+void CDiscoverer::CompleteNotificationProcessing()
+	{
+	iState = EDisc_AllPluginsDisc;
+	iSwiChangeDiscoveryPending = EFalse;
+	iLanguageChangeDiscoveryPending = EFalse;
+	}
+
+
+void CDiscoverer::ValidateEntryL(const TEntry& aEntry, const TDriveName& aDriveName, CPluginBase*& aEntryToFill, TBool aIsRO)
+   	{
+	aEntryToFill=CSecurePlugin::NewL(iFs,aEntry,aDriveName, aIsRO);
+  	}
+
+void CDiscoverer::ValidateEntryL(RResourceArchive& aRscArchive,CPluginBase*& aEntryToFill)
+   	{
+    aEntryToFill = CSpiPlugin::NewL(aRscArchive);
+	}
+  	
+
+void CDiscoverer::ProcessEntryL(const TDriveName& aDrive,CPluginBase*& aEntry, TBool aAnyDllDiscovered)
+	{
+	iDiscovererObserver.RegisterDiscoveryL(aDrive,aEntry,aAnyDllDiscovered);
+	}
+	
+void CDiscoverer::DriveMountedL(TDriveUnit aDrive)
+	{
+	TInt index = iDrivesDiscovered.Find(aDrive);
+	if(index == KErrNotFound)
+		{
+		User::LeaveIfError(iDrivesDiscovered.Append(aDrive));
+		iDiscovererObserver.DriveReinstatedL(aDrive);	// Wasn't there before
+		}
+	}
+	
+TBool CDiscoverer::IsAnyDllRegisteredWithDriveL(const TDriveUnit aDrive)const
+	{
+	return 	iDiscovererObserver.IsAnyDllRegisteredWithDriveL(aDrive);
+	}
+
+void CDiscoverer::DriveUnmountedL(TDriveUnit aDrive)
+	{
+	TInt index = iDrivesDiscovered.Find(aDrive);
+	if(index != KErrNotFound)
+		{
+		iDrivesDiscovered.Remove(index);
+		iDiscovererObserver.DriveRemovedL(aDrive);		// Was there before
+		}
+	}
+	
+CDiscoverer::TDiscovererState CDiscoverer::State() const
+	{
+	return iState;
+	}
+
+	
+void CDiscoverer::ProcessSSAEventL(TStartupStateIdentifier aKnownState)
+	{
+
+	if(iState == EDisc_NoPluginsDisc && aKnownState == EStartupStateCriticalStatic)
+		{
+		__ECOM_TRACE("ECOM: CDiscoverer::ProcessSSAEventL():EStartupStateCriticalStatic is reached,discover the RO Internal drives only.");
+
+		// Signal the observer that the scanning is started
+		iDiscovererObserver.DiscoveriesBegin();
+		
+		//scan the RO drives
+	    iDirScanner->DiscoverPluginsL(ETrue);
+
+		//change the state
+		iState = EDisc_CriticalPluginsDisc;
+
+		// Signal the observer that the scan has 
+		// been completed successfully.
+		iDiscovererObserver.DiscoveriesComplete(ETrue, EPluginProcessingTypeCriticalOnly);
+		}
+	else if(iState == EDisc_CriticalPluginsDisc && aKnownState == EStartupStateNonCritical)
+		{
+		__ECOM_TRACE("ECOM: CDiscoverer::ProcessSSAEventL():EStartupStateNonCritical is reached,discover the Non RO Internal drives.");
+
+		// Signal the observer that the scanning is started
+		iDiscovererObserver.DiscoveriesBegin();
+		
+		//scan the non-ro drives
+		iDirScanner->DiscoverPluginsL(EFalse);
+	
+		//change the state
+		iState = EDisc_AllPluginsDisc;
+		
+		// Signal the observer that the scan has 
+		// been completed successfully.
+		iDiscovererObserver.DiscoveriesComplete(ETrue, EPluginProcessingTypeNonCriticalOnly);
+
+	
+		StartNotifiers();		
+		}
+	else if(iState == EDisc_NoPluginsDisc && aKnownState == EStartupStateNonCritical)
+		{
+		__ECOM_TRACE("ECOM: CDiscoverer::ProcessSSAEventL():EStartupStateNonCritical is reached all at once,discover all the drives.");
+
+		// Signal the observer that the scanning is started
+		iDiscovererObserver.DiscoveriesBegin();
+		
+		//scan a specified the drives
+		iDirScanner->DiscoverPluginsL(ETrue);
+		iDirScanner->DiscoverPluginsL(EFalse);
+
+		//change the state
+		iState = EDisc_AllPluginsDisc;
+		
+		// Signal the observer that the scan has 
+		// been completed successfully.
+		iDiscovererObserver.DiscoveriesComplete(ETrue, EPluginProcessingTypeAll);
+
+		StartNotifiers();
+		}
+	}
+	
+void CDiscoverer::StartNotifiers()
+	{
+		
+	for(TInt i = 0; i<iRscDirNotifierList.Count(); i++)
+		{
+		if (iRscDirNotifierList[i] != NULL)
+		iRscDirNotifierList[i]->Activate();
+		}
+	iSwiChangeNotifier->Subscribe();
+	iLanguageChangeNotifier->Start();
+	}
+	
+void CDiscoverer::ProcessDNEventL(TNotificationFlag aFlag, const TDriveUnit& aDriveUnit)
+	{
+	if(iState == EDisc_AllPluginsDisc && aFlag == EPluginsModified)
+		{
+		iState = EDisc_PluginsDirty;
+		return;	
+		}
+	if(iState == EDisc_PluginsDirty && aFlag == EPluginsRediscover)
+		{
+		// Add drive number to the pending drive list and activate timer.
+		iScanningTimer->AddDriveL(aDriveUnit);
+		iScanningTimer->RestartScanPeriod();
+		}
+	}
+	
+void CDiscoverer::SetSwiChangeCallBack(const TCallBackWithArg& aCallBack)
+	{
+	iSwiChangeCallBack = aCallBack;
+	}
+
+void CDiscoverer::SetBurChangeCallBack(const TCallBackWithArg& aCallBack)
+	{
+	iBurChangeCallBack = aCallBack;
+	}
+
+void CDiscoverer::InitialiseEvent()
+	{
+	iState = EDisc_NoPluginsDisc;
+	}
+TInt CDiscoverer::LocaleChangedL(TAny* aPtr)
+	{
+	CDiscoverer* thisLocaleManager = (CDiscoverer *) aPtr ;
+	
+	if(!thisLocaleManager->iLanguageChangeNotifier)	
+		{
+	    __ECOM_TRACE("ECOM: LocaleChangedL: Bad Change Notification");
+		return KErrGeneral;
+		}
+
+ 	TInt stat = thisLocaleManager->iLanguageChangeNotifier->Change();
+	if((stat & EChangesLocale) && (!thisLocaleManager->iLanguageChangeDiscoveryPending))
+	   	{
+	   	//
+	   	// System Locale data has been updated 
+	   	// if the downgrade path has changed we 
+		// re-scan resource files for all drives and get the right language.
+		TBool isLanguageChanged;
+		thisLocaleManager->iDiscovererObserver.LanguageChangedL(isLanguageChanged);
+		if(isLanguageChanged)
+			{
+			thisLocaleManager->LanguageChangeNotificationL();
+			}
+	   	}
+	 return KErrNone;
+	}