// Copyright (c) 2005-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:
// cwatcherssastartupmgr.cpp
//

#include "cwatcherssastartupmgr.h"

#include <ecom/ecom.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS 
#include "cwatcher.h"
#endif

static void CleanUpEComInfoArray(TAny* aInfoArray);

/** 
Factory function to create a new instance of the CWatcherSSAStartupMgr
SSA start-up manager.

The SSA start-up manager registers with the Domain Manager for the
domain KAppServicesDomain3 of the KDmHierarchyIdStartup.

@return
A new CWatcherSSAStartupMgr object.
*/

CWatcherSSAStartupMgr* CWatcherSSAStartupMgr::NewL()
	{
#ifndef SYMBIAN_SYSTEM_STATE_MANAGEMENT
	CWatcherSSAStartupMgr* self = new (ELeave) CWatcherSSAStartupMgr(KDmHierarchyIdStartup, KAppServicesDomain3);
#else
	CWatcherSSAStartupMgr* self = new (ELeave) CWatcherSSAStartupMgr(KDmHierarchyIdStartup, KSM2AppServicesDomain3);
#endif
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}
	 

/**
Constructor of CWatcherSSAStartupMgr

@param aHierarchyId
The Id of the domain hierarchy to connect to
@param aDomainId
The Id of the domain to connect to
*/
CWatcherSSAStartupMgr::CWatcherSSAStartupMgr(TDmHierarchyId aHierarchyId, TDmDomainId aDomainId)
: CDmDomain(aHierarchyId,aDomainId), iCurrentStartupState(EStartupStateUndefined)
	{
	}
	 

/** 
Destructor
*/
CWatcherSSAStartupMgr::~CWatcherSSAStartupMgr()
	{
	Cancel();
	iWatcherList.ResetAndDestroy();
	delete iLog,
	iFs.Close();
	REComSession::FinalClose();
	}
	

/** 
Second-stage constructor.

This method indicates how the CWatcherSSAStartupMgr interacts with the 
Domain manager to keep aware of the startup state change.
*/
void CWatcherSSAStartupMgr::ConstructL()
	{
	
	// Get ready to log in \logs\watcher\watcher.txt
	User::LeaveIfError(iFs.Connect());
	iLog = CWatcherLog::NewL(iFs);
	
	// Connect to the Domain Manager

	TRAPD( err, CDmDomain::ConstructL() );

	if( err != KErrNone )
		{
		// The connection to the Domain Manager cannot be established,
		// we perform a full initialisation
		iLog->Printf(_L("CWatcherSSAStartupMgr::InitialiseL(): cannot connect to the Domain Manager, performing full init."));
		PerformFullInitL();
		iCurrentStartupState = EStartupStateNonCritical;
		return;
		}
			
	// Get the start-up state from the Domain Manager.
	TDmDomainState state = GetState();
	
	// Either something wrong with the Domain Manager or final state is reached
	if( state <= EStartupStateUndefined || state >= EStartupStateNonCritical )
		{
		// We perform a full initialisation
		iLog->Printf(_L("CWatcherSSAStartupMgr::InitialiseL(): wrong state, performing full init."));
		PerformFullInitL();
		iCurrentStartupState = EStartupStateNonCritical;
		return;
		}
			
	// Perform the initialisation for this state
	TBool notFinished = ProcessSSAEventL((TStartupStateIdentifier)state);
	if( notFinished )
		{
		// Still need to get the state from Domain Manager.
		RequestTransitionNotification();
		}
	}


/**
Executed when the startup state change is done, it does the same thing 
as the method InitialiseL().
*/
void CWatcherSSAStartupMgr::RunL()
	{
	// Leave if our previous request to be notified a state change has
	// returned an error and let RunError handle this.
	User::LeaveIfError(iStatus.Int()); 

	// Get the start-up state from the Domain Manager.
	TDmDomainState state = GetState();
	
	//If the state is EStartupStateUndefined there must be something wrong.
	if( state == EStartupStateUndefined ) 
		{
		AcknowledgeLastState(KErrBadHandle);
		User::Leave(KErrBadHandle); //RunError will handle this.
		}
	
	// Perform the initialisation for this state
	TBool notFinished = ProcessSSAEventL((TStartupStateIdentifier)state);
	
	// Tell domain manager that we have processed the last state change
	AcknowledgeLastState(KErrNone);
	
	if( notFinished )
		{
		RequestTransitionNotification();
		}
	}


/**
Handle the error if RunL leaves. Here, we just do a full initialisation.

@param aError 
Error code generated by the RunL(), not used here.
@return
KErrNone to avoid CActiveScheduler to panic. 
*/	
TInt CWatcherSSAStartupMgr::RunError(TInt /*aError*/)
	{
	iLog->Printf(_L("CWatcherSSAStartupMgr::RunL() leaves, do a full initialisation"));
	TRAP_IGNORE(DoRunErrorL());
	return KErrNone;
	}

	
/**
Leaving method trapped by RunError

If RunError has been called that means RunL has left.
Because watcher.exe is started in the critical-dynamic state it is quite
likely the critical init has been done already (in ConstructL) when RunL
is called so we just want to do the non-critical part of the init.
We check our internal state for confirmation and run the non-critical init.
If our internal state is EStartupStateCriticalDynamic or before we
perform a full init.
*/	
void CWatcherSSAStartupMgr::DoRunErrorL()
	{
	if ( iCurrentStartupState > EStartupStateCriticalDynamic )
		{
		PerformNonCriticalInitL();
		}
	else
		{	
		PerformFullInitL();
		}
	}

	
/**
Perform the initialisation for a particular state.

@param aKnownState 
The startup state passed into the MStartupStateObserver objects
@return
Whether or not a transition notification should be requested
*/	
TBool CWatcherSSAStartupMgr::ProcessSSAEventL(TStartupStateIdentifier aKnownState)
	{
	TBool notFinished = ETrue;
	
	if ( aKnownState != iCurrentStartupState )
		{
		
		//if we have not performed critical dynamic initialisation and receive a 
		//notification for it or a state that comes after 

		if(iCurrentStartupState<EStartupStateCriticalDynamic &&
		   aKnownState >= EStartupStateCriticalDynamic)
			{
			PerformCriticalInitL();
			iCurrentStartupState = EStartupStateCriticalDynamic;
			}
		
		//if we have not performed non-critical initialisation and receive a notification
		//for it or a state that comes after 

		if(iCurrentStartupState<EStartupStateNonCritical &&
		   aKnownState >= EStartupStateNonCritical)
			{
			PerformNonCriticalInitL();
			notFinished = EFalse;
			iCurrentStartupState = EStartupStateNonCritical;
			}
		
		}
		
	return notFinished;
	}


/**
Perform a full initialisation = start all the watchers
*/	
void CWatcherSSAStartupMgr::PerformFullInitL()
	{
	// Watchers for the critical-dynamic state:
	PerformCriticalInitL();
	// Watchers for the non-critical state:
	PerformNonCriticalInitL();
	}


/**
Perform initialisation for the 'critical-dynamic' start-up state
*/	
void CWatcherSSAStartupMgr::PerformCriticalInitL()
	{
	StartWatchersL(KUidEComWatcher);             // Legacy watchers
	StartWatchersL(KUidMsgCriticalWatchers); 
	}

/**
Perform initialisation for the 'non-critical' start-up state
*/	
void CWatcherSSAStartupMgr::PerformNonCriticalInitL()
	{
	StartWatchersL(KUidMsgNonCriticalWatchers);
	}

/**
Starts all the watchers implementing the specified ECom interface

@param aEComInterface 
The ECom interface of the watchers to be started
*/	
void CWatcherSSAStartupMgr::StartWatchersL(TUid aEComInterface)
	{
	RImplInfoPtrArray watcherInfoArray;
	TCleanupItem cleanup(CleanUpEComInfoArray, &watcherInfoArray);
	CleanupStack::PushL(cleanup);
	TEComResolverParams eComResolverParams;

	// Get the list of active watcher implementations.
	// (list only watcher implementations that are ROM, as it would be 
	// insecure to load RAM implementations)
	REComSession::ListImplementationsL(	aEComInterface, eComResolverParams, 
										KRomOnlyResolverUid, watcherInfoArray);

	const TInt count = watcherInfoArray.Count();

	#ifdef __WINS__
		_LIT(KLaunchingWatcher, "Launching ECOM watcher %S");
	#endif
	
	for( TInt ii=0; ii < count; ++ii )
		{
		CImplementationInformation* info = watcherInfoArray[ii];
		
		// Log details of the watcher
		const TDesC& name = info->DisplayName();
		#ifdef __WINS__
			iLog->Printf(KLaunchingWatcher, &name);
		#endif
	
		// Create the watcher launcher
		CWatcherLauncher* launcher = CWatcherLauncher::NewL(name, 
															info->ImplementationUid(), 
															iFs, 
															*iLog);
		CleanupStack::PushL(launcher);
		iWatcherList.AppendL(launcher);
		CleanupStack::Pop(launcher);
		
		}
	CleanupStack::PopAndDestroy(&watcherInfoArray);
	}


// CleanupEComArray function is used for cleanup support 
// of locally declared arrays.
void CleanUpEComInfoArray(TAny* aInfoArray)
	{
	RImplInfoPtrArray* infoArray = (static_cast<RImplInfoPtrArray*>(aInfoArray));
	infoArray->ResetAndDestroy();
	infoArray->Close();
	}

