pimprotocols/phonebooksync/Server/phbksyncsvr.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:29:52 +0100
branchRCL_3
changeset 20 f4a778e096c2
parent 0 e686773b3f54
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

// Copyright (c) 2002-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:
// Implementation of the CPhoneBookServer class which is the front-end server,
// responsible for processing all short and quick requests, and for passing
// longer requests to the engine.
// 
//

/**
 @file
 @internalComponent
*/

#include <connect/sbdefs.h>
#include <commsdattypesv1_1.h>
#include <cdblen.h>

#include "common.h"
#include "PhonebookManager.h"
#include "SyncContactICCEntry.h"
#include "phbksynclog.h"
#include "phbksyncsvr.h"
#include "phbksyncsess.h"
#include "SyncEngineRequest.h"
#include "ActiveNotifications.h"


using namespace CommsDat;


/**
 * Number of retries for connecting to Etel.
 */
const TInt KMaxConnectToEtelRetries = 3;


//
//  Definition of iPolicy dictating the capability checking for phbksyncsvr...
//
const TUint iRangeCount = 10;

const TInt CPhoneBookServer::iRanges[iRangeCount] = 
	{
	0,		//range is 0
	1,		//range is 1-3 inclusive
	4,		//range is 4-5 inclusive
	6,		//range is 6
	7,		//range is 7-13 inclusive
	14,		//range is 14
	15,		//range is 15-16 inclusive
	17,		//range is 17-24 inclusive
	25,		//range is 25 inclusive
	26,		//range is 26-KMaxTInt inclusive
	};

const TUint8 CPhoneBookServer::iElementsIndex[iRangeCount] = 
	{
	0,
	1,
	2,
	3,
	4,
	5,
	6,
	7,
	8,
	CPolicyServer::ENotSupported,
	};

const CPolicyServer::TPolicyElement CPhoneBookServer::iElements[] = 
	{
	{ _INIT_SECURITY_POLICY_C2( ECapabilityReadUserData, ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 0:  range 0 - 0
	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 1:  range 1 - 3
	{ _INIT_SECURITY_POLICY_C1( ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 2:  range 4 - 5
	{ _INIT_SECURITY_POLICY_C1( ECapabilityReadUserData), CPolicyServer::EFailClient},	// policy 3:  range 6 - 6
	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 4:  range 7 - 13
	{ _INIT_SECURITY_POLICY_C2( ECapabilityReadUserData, ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 5:  range 14 - 14
	{ _INIT_SECURITY_POLICY_C1( ECapabilityWriteUserData), CPolicyServer::EFailClient},	// policy 6:  range 15 - 16
	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient},	// policy 7:  range 17 - 24
#ifdef _DEBUG
	{ _INIT_SECURITY_POLICY_C1( ECapability_None), CPolicyServer::EFailClient}, // policy 8: range 25
#else
	{ _INIT_SECURITY_POLICY_FAIL}, // policy 8: range 25
#endif
	};

const CPolicyServer::TPolicy CPhoneBookServer::iPolicy = 
	{
	CPolicyServer::EAlwaysPass,
	iRangeCount,
	iRanges,
	iElementsIndex,
	iElements
	};


/**
 *  Static factory method used to create a CPhoneBookServer object.
 *
 *  @return  Pointer to the created CPhoneBookServer object, or NULL.
 */
CPhoneBookServer* CPhoneBookServer::NewL()
	{
	LOGSERVER1(_L8("NewL()"));
	CPhoneBookServer* self = new (ELeave) CPhoneBookServer();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);

	return self;
	} // CPhoneBookServer::NewL


/**
 *  Standard constructor.
 */
CPhoneBookServer::CPhoneBookServer()
	: CPolicyServer(EPriorityNormal, iPolicy, ESharableSessions), 
	iServerConfigLevel(EServerConfigurationNone),
	iICCCaps(RMobilePhone::KCapsSimAccessSupported), 
	iPhonebookManager(NULL),
	iShouldShutdownServer(EFalse),
	iShouldShutdownUnconditionally(EFalse)
	{
	__DECLARE_NAME(_L("CPhoneBookServer"));
	} // CPhoneBookServer::CPhoneBookServer


/**
 *  Second phase constructor. Ensures the server is created and ready to run.
 */
void CPhoneBookServer::ConstructL()
	{
	StartL(PHBKSYNC_SERVER_NAME);
	} // CPhoneBookServer::ConstructL


/**
 *  Destructor.
 */
CPhoneBookServer::~CPhoneBookServer()
	{
	//
	// Stop the server from starting if it has initiated a startup.
	//
	delete iPhoneBookSyncStarter;
	iPhoneBookSyncStarter = NULL;
	
	//
	// Unconfigure the server if needed...
	//
	TRAP_IGNORE(ConfigureServerL(EServerConfigurationNone));

	iCacheStateNotificationArray.Reset();
	iSyncEngineRequestArray.ResetAndDestroy();
	iSessionArray.Reset();
	} // CPhoneBookServer::~CPhoneBookServer


/**
 *  Attempts to configure or unconfigure the server as specified. If this
 *  function leaves, the configuration state will be invalid. This function
 *  is therefore a private function and callers should use ConfigureServerL()
 *  instead which protects against such problems.
 *  
 *  @param aLevel  The desired configuration level to initialise the server to.
 */
void CPhoneBookServer::AttemptToConfigureServerL(TServerConfiguration aLevel)
	{
	LOGSERVER2(_L8("AttemptToConfigureServerL() to level %d"), aLevel);

	//
	// Are we configuring or unconfiguring?
	//
	if (iServerConfigLevel < aLevel)
		{
		//
		// Configure from level 'None' to 'Idle'...
		//
		if (iServerConfigLevel == EServerConfigurationNone)
			{
			iServerConfigLevel = EServerConfigurationConfiguringToIdle;
			LOGSERVER1(_L8("Configuration changed from 'None' to 'ConfiguringToIdle'."));

			iNotifyBackupAndRestore = CNotifyBackupAndRestore::NewL(*this);
			User::LeaveIfError(iNotifyBackupAndRestore->Start());

			iServerConfigLevel = EServerConfigurationIdle;
			LOGSERVER1(_L8("Configuration changed from 'ConfiguringToIdle' to 'Idle'."));
			}

		//
		// Configure from level 'Idle' to 'Full'...
		//
		if (iServerConfigLevel < aLevel  &&
			iServerConfigLevel == EServerConfigurationIdle)
			{
			iServerConfigLevel = EServerConfigurationConfiguringToFull;
			LOGSERVER1(_L8("Configuration changed from 'Idle' to 'ConfiguringToFull'."));

			//
			// Connect to ETel...
			//
			TInt  etelErr(KErrNone), retryCount;

   			for (retryCount = 0;  retryCount < KMaxConnectToEtelRetries;  retryCount++)
   				{
   				TRAP(etelErr, ConnectToEtelL());	//This can leave due to a denied access 										
													//to CommDb or Etel
   				if (etelErr == KErrNone)
					{
   					break;
					}

				if (retryCount < KMaxConnectToEtelRetries - 1)
					{
   					User::After(1000000);
					}
   				}
   			User::LeaveIfError(etelErr);

			//
			// Setup the phonebook parameters and create the Phonebook Manager.
			// This will also read the settings from the INI file.
			//
			if (iPhone.GetIccAccessCaps(iICCCaps) != KErrNone)
				{
				iICCCaps = RMobilePhone::KCapsSimAccessSupported;
				}

			iPhonebookManager = CPhoneBookManager::NewL(iICCCaps);

			//
			// Initialise and start the Active Objects...
			//
			iGetPhoneStoreInfo = CGetPhoneStoreInfo::NewL(*this,
														  *iPhonebookManager,
														  iPhone);
			User::LeaveIfError(iGetPhoneStoreInfo->Start());

			iNotifyLockInfoChange = CNotifyLockInfoChange::NewL(*this, iPhone);
			User::LeaveIfError(iNotifyLockInfoChange->Start());

			iGetSecurityEvent = CNotifySecurityEvent::NewL(*this, iPhone);
			User::LeaveIfError(iGetSecurityEvent->Start());

			if (IsUsimAppTsy()  &&
				(iICCCaps & RMobilePhone::KCapsUSimAccessSupported))
				{
				iNotifyAppInfoChange = CNotifyAppInfoChange::NewL(*this, iPhone);
				User::LeaveIfError(iNotifyAppInfoChange->Start());
				}

			if (iIsSatSupported)
				{
				iNotifySatUpdates = CNotifySATUpdates::NewL(*this, iSat);
	 			User::LeaveIfError(iNotifySatUpdates->Start());
				}

			//
			// Start the Sync Engine...
			//
			User::LeaveIfError(iSyncEngine.Connect(iPhonebookManager));

			iServerConfigLevel = EServerConfigurationFull;
			LOGSERVER1(_L8("Configuration changed from 'ConfiguringToFull' to 'Full'."));
			}
		}
	else if (iServerConfigLevel > aLevel)
		{
		//
		// Unconfigure from level 'Full' to 'Idle'...
		//
		if (iServerConfigLevel == EServerConfigurationFull  ||
			iServerConfigLevel == EServerConfigurationConfiguringToFull)
			{
			iCacheStateNotificationArray.Reset();
			iSyncEngineRequestArray.ResetAndDestroy();

			iSyncEngine.Close();

			delete iNotifySatUpdates;
			iNotifySatUpdates = NULL;

			delete iGetSecurityEvent;
			iGetSecurityEvent = NULL;

			delete iNotifyLockInfoChange;
			iNotifyLockInfoChange = NULL;

			delete iGetPhoneStoreInfo;
			iGetPhoneStoreInfo = NULL;

			delete iPhonebookManager;
			iPhonebookManager = NULL;

			delete iNotifyAppInfoChange;
			iNotifyAppInfoChange = NULL;

			iSat.Close();
			iPhone.Close();
			iEtelServer.Close();
				
			iServerConfigLevel = EServerConfigurationIdle;
			LOGSERVER1(_L8("Configuration changed to 'Idle'."));
			}
		//
		// Unconfigure from level 'Idle' to 'None'...
		//
		if (iServerConfigLevel > aLevel  &&
			(iServerConfigLevel == EServerConfigurationIdle  ||
			 iServerConfigLevel == EServerConfigurationConfiguringToIdle))
			{
			delete iNotifyBackupAndRestore;
			iNotifyBackupAndRestore = NULL;

			iServerConfigLevel = EServerConfigurationNone;
			LOGSERVER1(_L8("Configuration changed to 'None'."));
			}
		}
	} // CPhoneBookServer::AttemptToConfigureServerL


/**
 *  Configures or unconfigures the server as specified.
 *  
 *  @param aLevel  The desired configuration level to initialise the server to.
 */
void CPhoneBookServer::ConfigureServerL(TServerConfiguration aLevel)
	{
	//
	// Check parameter is valid...
	//
	if (aLevel != EServerConfigurationNone  &&
		aLevel != EServerConfigurationIdle  &&
		aLevel != EServerConfigurationFull)
		{
		User::Leave(KErrArgument);
		}

	//
	// Save the current configuration level in case AttemptToConfigureServerL()
	// leaves.
	//
	TServerConfiguration  oldConfigLevel = iServerConfigLevel;

	TRAPD(configErr, AttemptToConfigureServerL(aLevel));

	//
	// If the attempt to configure the server failed, then reset the config
	// state and leave.
	//
	if (configErr != KErrNone)
		{
		LOGSERVER2(_L8("ConfigureServerL: Could not config server (Error %d)."),
			     configErr);

		TRAPD(unconfigErr, AttemptToConfigureServerL(oldConfigLevel));
		if (unconfigErr != KErrNone)
			{
			LOGSERVER2(_L8("ConfigureServerL: Could not unconfig server either (Error %d)."),
				     unconfigErr);
			}

		iServerConfigLevel = oldConfigLevel;

		User::Leave(configErr);
		}
	} // CPhoneBookServer::ConfigureServerL


/**
 *  This method is called by the system to indicate that a backup or restore has
 *  started.
 */
void CPhoneBookServer::HandleBackupOrRestoreStarting()
	{
	LOGSERVER1(_L8("HandleBackupOrRestoreStarting()"));

	TRAPD(err, ConfigureServerL(EServerConfigurationIdle));
	if (err != KErrNone)
		{
		LOGSERVER2(_L8("ConfigureServerL() failed with error %d."), err);
		}
	} // CPhoneBookServer::HandleBackupOrRestoreStarting


/**
 *  This method is called by the system to indicate that a backup or restore has
 *  completed.
 */
void CPhoneBookServer::HandleBackupOrRestoreComplete()
	{
	LOGSERVER1(_L8("HandleBackupOrRestoreComplete()"));

	TRAPD(err, ConfigureServerL(EServerConfigurationFull));
	if (err != KErrNone)
		{
		LOGSERVER2(_L8("ConfigureServerL() failed with error %d."), err);
		}
	} // CPhoneBookServer::HandleBackupOrRestoreComplete


/**
 *  Create a new client session.
 *
 *  @note You can not create a new session if the server is shuting down
 *        unconditionally.
 */
CSession2* CPhoneBookServer::NewSessionL(const TVersion&, const RMessage2& /*aMessage*/) const
	{
	LOGSERVER1(_L8("NewSessionL"));

	if (iShouldShutdownUnconditionally)
		{
		User::Leave(KErrPermissionDenied);
		}

	return new(ELeave) CPhoneBookSession();
	} // CPhoneBookServer::NewSessionL


/**
 *  Called by the session class when it is being created.
 *
 *  @param aSession  Server side session.
 */
void CPhoneBookServer::AddSessionL(CPhoneBookSession* aSession)
	{
	LOGSERVER1(_L8("AddSession"));

	//
	// Store this session in the list of sessions...
	//
	iSessionArray.Append(aSession);

	//
	// Queue an Active Object to configure the server straight after this
	// session is created.
	//
	if (iPhoneBookSyncStarter == NULL  &&
	    iServerConfigLevel == EServerConfigurationNone)
		{
		iPhoneBookSyncStarter = new (ELeave) CPhoneBookSyncStarter(*this);
		iPhoneBookSyncStarter->Call();
		}
	} // CPhoneBookServer::AddSessionL


/**
 *  Called by the session class when it is being destroyed.
 *
 *  @param aSession  Server side session.
 */
void CPhoneBookServer::DropSession(CPhoneBookSession* aSession)
	{
	LOGSERVER1(_L8("DropSession"));

	//
	// Remove this session from the session array list...
	//
	TInt  position;
	
	position = iSessionArray.Find(aSession);
 	if (position != KErrNotFound) 
		{
 		iSessionArray.Remove(position);
 		
 		// 
 		// Also remove any outstanding notifications owned by this session
 		//
		for (TInt index = 0;  index < iCacheStateNotificationArray.Count();  index++)
			{
			TCacheStateNotification  notifyRequest = iCacheStateNotificationArray[index];

			if (notifyRequest.iSession == aSession)
				{
				//
				// If it is in the list then just remove and complete it since session has been closed...
				//
				aSession->CompleteRequest(notifyRequest.iMessage, KErrDisconnected);
				iCacheStateNotificationArray.Remove(index);
				index--;
				}
			} 		
		}

	//
	// If we are shuting down then unconfigure and stop...
	//
	if (iSessionArray.Count() == 0  &&  iShouldShutdownServer)
		{
		TRAP_IGNORE(ConfigureServerL(EServerConfigurationNone));
		CActiveScheduler::Stop();
		}
	} // CPhoneBookServer::DropSession


/**
 *  Requests a synchronisation from the engine. This is an asynchronous
 *  request that will result in an engine request being posted to the engine.
 *
 *  @param aSession       A reference to the client session.
 *  @param aMessage       A reference to the client request.
 *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
 */
void CPhoneBookServer::DoSynchronisationL(CPhoneBookSession& aSession,
										  const RMessage2& aMessage,
										  TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("DoSynchronisationL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		aSession.CompleteRequest(aMessage, KErrNotReady);
		return;
		}

	//
	// Validate the phonebook UID to ensure sync requests can only be posted
	// for valid phonebooks. Although no harm would come of sending the engine
	// a sync request with an invalid UID (it will check it as well) we can
	// easily check it now, and reduce the load on the engine.
	//
	if (iPhonebookManager->ValidatePhonebookUid(aPhonebookUid) != KErrNone)
		{
		aSession.CompleteRequest(aMessage, KErrNotSupported);
		return;
		}
	
	//
	// Check a sync request is not in progress or queued for this phonebook...
	//
	if (IsEngineRequestQueued(ESyncDoSynchronisation, aPhonebookUid, ETrue, ETrue))
		{
		aSession.CompleteRequest(aMessage, KErrInUse);
		return;
		}

	//
	// Queue the request to the engine...
	//
	QueueEngineRequestL(ESyncDoSynchronisation, aPhonebookUid, KNullContactId,
						&aSession, aMessage);
	} // CPhoneBookServer::DoSynchronisationL
	
	
/**
 *  Requests the supported phonebook field formats for the specified phonebook.
 *
 *  @param aPhonebookUid   UID of the ICC phonebook to perform the operation on.
 *  @param aContactFields  The returned contact fields.
 *
 *  @return KErrNone if successful, a system-wide error code if not.
 */
TInt CPhoneBookServer::GetContactFormatL(TUid aPhonebookUid,
										 RPhoneBookSession::TContactFieldsV3& aContactFields)
	{
	LOGSERVER2(_L8("GetContactFormatL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return(KErrNotReady);
		}

	//	
	// Get the Contact Fields from the phonebook via the Phonebook Manager.
	//
	TInt result = iPhonebookManager->GetContactFields(aPhonebookUid,
													  aContactFields);
	
	return(result);
	} // CPhoneBookServer::GetContactFormatL


/**
 *  Return the current setting for the Synchronisation Mode for the specified
 *  phonebook. The request will complete with KErrNone if the setting is
 *  successfully returned.
 * 
 *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
 *  @param aSyncMode      The returned sync mode.
 *
 *  @return KErrNone if successful, a system-wide error code if not.
 */
TInt CPhoneBookServer::GetSyncModeL(TUid aPhonebookUid,
									RPhoneBookSession::TPhonebookSyncMode& aSyncMode)
	{
	LOGSERVER2(_L8("GetSyncModeL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return(KErrNotReady);
		}

	//	
	// Get the Sync Mode from the Phonebook via the Phonebook Manager.
	//
	TInt  result = iPhonebookManager->GetSyncMode(aPhonebookUid, aSyncMode);
	
	return result;
	} // CPhoneBookServer::GetSyncModeL


/**
 *  Set the Synchronisation Mode for the specified phonebook.
 *
 *  If the mode is changing to EAutoCurrentIcc and no sync has yet been performed,
 *  then an automatic sync will be performed.
 * 
 *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
 *  @param aSyncMode      The new sync mode.
 *
 *  @return KErrNone if successful, a system-wide error code if not.
 */
TInt CPhoneBookServer::SetSyncModeL(TUid aPhonebookUid,
									RPhoneBookSession::TPhonebookSyncMode aSyncMode)
	{
	LOGSERVER2(_L8("SetSyncModeL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return(KErrNotReady);
		}

	//
	// If the new sync mode is EAutoCurrentIcc then get the current Sync
	// Mode and Sync State. This will determine if an AutoSync should be
	// queued following the change.
	//
	TBool  autoSyncNeeded(EFalse);
	TInt  result(KErrUnknown);

	if (aSyncMode == RPhoneBookSession::EAutoCurrentIcc)
		{
		//
		// Get the current sync mode...
		//
		RPhoneBookSession::TPhonebookSyncMode  oldSyncMode;

		result = iPhonebookManager->GetSyncMode(aPhonebookUid,
												oldSyncMode);
		if (result != KErrNone)
			{
			return result;
			}
		
		//
		// Get the current sync state...
		//
		RPhoneBookSession::TSyncState  syncState;

		result = iPhonebookManager->GetSyncState(aPhonebookUid,
												 syncState);
		if (result != KErrNone)
			{
			return result;
			}
		
		//
		// Find out if the phonebook info has been retrieved...
		//
		TInt  phBkInfoRetrievedResult;
		
		result = iPhonebookManager->GetPhBkInfoRetrievedResult(aPhonebookUid,
														       phBkInfoRetrievedResult);
		if (result != KErrNone)
			{
			return result;
			}
		
		//
		// If the sync mode is changing, the phonebook was previously
		// unsynchronised and the phonebook info is retrieved then
		// an auto-sync will be needed.
		//
		if (oldSyncMode != RPhoneBookSession::EAutoCurrentIcc  &&
			syncState == RPhoneBookSession::EUnsynchronised  &&
			phBkInfoRetrievedResult != KErrNotReady)
			{
			autoSyncNeeded = ETrue;
			}
		}

	//
	// Set the Sync Mode in the Phonebook via the Phonebook Manager.
	//
	result = iPhonebookManager->SetSyncMode(aPhonebookUid, aSyncMode);

	//
	// Queue an auto sync if needed...
	//
	if (autoSyncNeeded)
		{
		QueueAutoSyncRequest(aPhonebookUid);
		}

	return result;
	} // CPhoneBookServer::SetSyncModeL


/**
 *  Delete an entry specified by aContactId from the ICC phonebook store
 *  it exists in.  This is an asynchronous request that will result in an
 *  engine request being posted to the engine.
 *
 *  If a DoSynchronisation or an existing DeleteCntFromICC request has already
 *  been posted for the same phonebook, KErrInUse will be returned.
 *
 *  @param aSession    A reference to the client session.
 *  @param aMessage    A reference to the client request.
 *  @param aContactId  ID of the contact to delete.
 */
void CPhoneBookServer::DeleteCntFromICCL(CPhoneBookSession& aSession,
										 const RMessage2& aMessage,
										 TContactItemId aContactId)
	{
	LOGSERVER2(_L8("DeleteCntFromICCL(0%d)"), aContactId);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		aSession.CompleteRequest(aMessage, KErrNotReady);
		return;
		}

	//
	// Get the phonebook UID from the contact UID.
	//
	TUid phonebookUid = iPhonebookManager->GetPhonebookUidFromContactId(aContactId);

	if (phonebookUid == KUidIccPhonebookNotSpecified)
		{
		aSession.CompleteRequest(aMessage, KErrArgument);
		return;
		}

	//
	// Check a sync or delete request is not in progress or queued for this
	// phonebook...
	//
	if (IsEngineRequestQueued(ESyncDoSynchronisation, phonebookUid, ETrue, ETrue)  ||
		IsEngineRequestQueued(ESyncDeleteCntFromICC, phonebookUid, ETrue, ETrue))
		{
		aSession.CompleteRequest(aMessage, KErrInUse);
		return;
		}

	QueueEngineRequestL(ESyncDeleteCntFromICC, phonebookUid, aContactId,
						&aSession, aMessage);
	} // CPhoneBookServer::DeleteCntFromICCL


/**
 *  Write an entry to a specified ICC phonebook store. This is an asynchronous
 *  request that will result in an engine request being posted to the engine.
 *
 *  If a DoSynchronisation or WriteCntToICCL request has already been posted
 *  for the same phonebook then KErrInUse will be returned.
 *
 *  @param aSession     A reference to the client session.
 *  @param aMessage     A reference to the client request.
 *  @param aSlotNum     The desired slot number or KSyncIndexNotSupplied.
 *  @param aTemplateId  ID of the contact template to base the entry on.
 */
void CPhoneBookServer::WriteCntToICCL(CPhoneBookSession& aSession,
									  const RMessage2& aMessage,
									  TInt aSlotNum,
									  TContactItemId aTemplateId)
	{
	LOGSERVER2(_L8("WriteCntToICCL(%d)"), aTemplateId);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		aSession.CompleteRequest(aMessage, KErrNotReady);
		return;
		}
	
	//
	// Check a sync or write request is not in progress or queued for this
	// phonebook...
	//
	TUid  phonebookUid = iPhonebookManager->GetPhonebookUidFromTemplateId(aTemplateId);

	if (IsEngineRequestQueued(ESyncDoSynchronisation, phonebookUid, ETrue, ETrue)  ||
		IsEngineRequestQueued(ESyncWriteCntToICC, phonebookUid, ETrue, ETrue))
		{
		aSession.CompleteRequest(aMessage, KErrInUse);
		return;
		}

	//
	// This request can only proceed if the cache is valid. Since no sync
	// requests are pending or in progress, we can check this now.
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(phonebookUid, syncState);
	if (result != KErrNone)
		{
		aSession.CompleteRequest(aMessage, result);
		return;
		}
	
	if (syncState != RPhoneBookSession::ECacheValid)
		{
		aSession.CompleteRequest(aMessage, KErrNotReady);
		return;
		}

	//
	// If a slot number is supplied, check it is valid. Again we can do this
	// now as it won't change...
	//
	if (aSlotNum != KSyncIndexNotSupplied)
		{
		RMobilePhoneBookStore::TMobilePhoneBookInfoV5  phBkInfo;
		TInt  result;
		
		result = iPhonebookManager->GetPhoneBookInfo(phonebookUid, phBkInfo);
		if (result != KErrNone)
			{
			aSession.CompleteRequest(aMessage, result);
			return;
			}

		if (aSlotNum < 1  ||  aSlotNum > phBkInfo.iTotalEntries)
			{
			aSession.CompleteRequest(aMessage, KErrArgument);
			return;
			}
		}

	//
	// Queue the request for the engine...
	//
	QueueEngineRequestL(ESyncWriteCntToICC, phonebookUid, KNullContactId,
						&aSession, aMessage);
	} // CPhoneBookServer::WriteCntToICCL


/**
 *  Check whether an ICC entry is allowed to be read/write/edit on behalf of
 *  the client. The request will complete with KErrNone if the operation could
 *  be performed.
 * 
 *  @param aContactId   Contact Item ID
 *  @param aValidateOp  Type of operation that needs to be validated
 *
 *  @return KErrNone if validation was successful, otherwise returns an error.
 */
TInt CPhoneBookServer::ValidateContactL(TContactItemId aContactId,
									    MContactSynchroniser::TValidateOperation aValidateOp)
	{
	LOGSERVER2(_L8("ValidateContactL(%d)"), aContactId);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// Get the contact UID from the phonebook...
	//
	TUid  phonebookUid = iPhonebookManager->GetPhonebookUidFromContactId(aContactId);

	if (phonebookUid == KUidIccPhonebookNotSpecified)
		{
		return KErrNotFound;
		}

	//
	// This request can only proceed if the cache is valid...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(phonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}
	
	if (syncState != RPhoneBookSession::ECacheValid)
		{
		return KErrNotReady;
		}

	//
	// First check whether phonebook can be accessed, i.e. whether ICC is
	// pin-locked/blocked...
	//
	if (iPhonebookManager->IsPin1Valid() == EFalse  ||
		(phonebookUid == KUidUsimAppAdnPhonebook  &&
		 iPhonebookManager->IsUsimAppPinValid() == EFalse))
		{
		return KErrAccessDenied;
		}

	//
	// Check the available types of access...
	//
	RMobilePhoneBookStore::TMobilePhoneBookInfoV5  phBkInfo;
	
	result = iPhonebookManager->GetPhoneBookInfo(phonebookUid, phBkInfo);
	if (result != KErrNone)
		{
		return result;
		}

	switch (aValidateOp)
		{
		case MContactSynchroniser::ERead:
		case MContactSynchroniser::ESearch:
			{
			if (phBkInfo.iCaps & RMobilePhoneStore::KCapsReadAccess == 0)
				{
				return KErrAccessDenied;
				}
			}
			break;

		case MContactSynchroniser::EEdit:
			{
			if (phBkInfo.iCaps & RMobilePhoneStore::KCapsWriteAccess == 0)
				{
				return KErrAccessDenied;
				}
			
			 //
			 // For FDN Phonebook, can only edit if PIN2 is set
			 //
			 if (phonebookUid == KUidIccGlobalFdnPhonebook  &&
			 	 iPhonebookManager->IsPin2Valid() == EFalse)
				{
				return KErrAccessDenied;
				}
			}	
			break;

		default:
			{
			return(KErrNotSupported);
			}
		}

	//
	// Finally, check whether the entry exists in the Look-Up Table...
	//
	return iPhonebookManager->IsEntryInTable(phonebookUid, aContactId);
	} // CPhoneBookServer::ValidateContactL


/**
 *  Update an entry in the Phonebook Sync's look-up table for the specified
 *  phonebook by adding the Contacts ID supplied by the client (Contacts Model)
 *  into the specified slot number.
 *
 *  @param aPhonebookUid  UID of the ICC phonebook to perform the operation on.
 *  @param aContactId     Contact Item ID
 *  @param aSlotNum       The slot number of the entry.
 *
 *  @return KErrNone if the table was updated successfully, otherwise returns an error.
 */
TInt CPhoneBookServer::UpdateLookupTableL(TUid aPhonebookUid,
										  TContactItemId aContactId,
									      TInt aSlotNum)
	{
	LOGSERVER2(_L8("UpdateLookupTableL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// This request can only proceed if the cache is valid...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(aPhonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}

	if (syncState != RPhoneBookSession::ECacheValid)
		{
		return KErrNotReady;
		}

	//
	// Update the entry in the loop-up-table...
	//
	result = iPhonebookManager->UpdateEntryInTable(aPhonebookUid,
												   aSlotNum, aContactId);

	return result;
	} // CPhoneBookServer::UpdateLookupTableL


/**
 *  Return the ICC entry template or group ID for a specified phonebook.
 *
 *  @param aPhonebookUid   UID of the ICC phonebook to perform the operation on.
 *  @param aSyncIdType     Either ESyncTemplateId or ESyncGroupId.
 *  @param aContactItemId  Returned contact ID.
 *
 *  @return KErrNone if the ID was returned successfully, otherwise returns an error.
 */
TInt CPhoneBookServer::GetPhoneBookIdL(TUid aPhonebookUid,
								       RPhoneBookSession::TSyncIdType aSyncIdType,
									   TContactItemId& aContactItemId)
	{
	LOGSERVER2(_L8("GetPhoneBookIdL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}
	
	//
	// Get the required contact ID...
	//
	TInt  result(KErrArgument);
	
	if (aSyncIdType == RPhoneBookSession::ESyncTemplateId)
		{
		result = iPhonebookManager->GetTemplateId(aPhonebookUid,
												  aContactItemId);
		}
	else if (aSyncIdType == RPhoneBookSession::ESyncGroupId)
		{
		result = iPhonebookManager->GetGroupId(aPhonebookUid,
											   aContactItemId);
		}

	return(result);
	} // CPhoneBookServer::GetPhoneBookIdL


/**
 *  Return the current phonebook server cache state.
 * 
 *  @param aPhonebookUid   UID of the ICC phonebook to perform the operation on.
 *  @param aContactItemId  Returned cache state.
 *
 *  @return KErrNone if the cache state was returned successfully, otherwise returns
 *          an error.
 */
TInt CPhoneBookServer::GetCacheStateL(TUid aPhonebookUid,
								      RPhoneBookSession::TSyncState& aSyncState)
	{
	LOGSERVER2(_L8("GetCacheStateL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// Check if the phonebook is supported...
	//
	TInt  result, phBkInfoRetrievedResult;
	
	result = iPhonebookManager->GetPhBkInfoRetrievedResult(aPhonebookUid,
														   phBkInfoRetrievedResult);
	if (result != KErrNone)
		{
		return result;
		}
	
	if (phBkInfoRetrievedResult != KErrNone)
		{
		return phBkInfoRetrievedResult;
		}

	return iPhonebookManager->GetSyncState(aPhonebookUid, aSyncState);
	} // CPhoneBookServer::GetCacheStateL


/**
 *  Marks the start of checking the server's and engine's heap. 
 *  This function only works in debug releases.
 *
 *  Calls to this function can be nested but each call must be matched by
 *  corresponding DbgMarkEnd().
 *
 *  @return  KErrNone.
 */
TInt CPhoneBookServer::DbgMarkHeap() const
	{
#ifdef _DEBUG
	__UHEAP_MARK;
#endif

	return(KErrNone);
	} // CPhoneBookServer::DbgMarkHeap


/**
 *  Checks that the number of allocated cells at the current nested level on
 *  the server's and engine's heaps are the same as the specified value.
 *  This function only works for debug builds.
 *
 *  @param aCount  The number of heap cells expected to be allocated at
 *                 the current nest level.
 *
 *  @return  KErrNone.
 */
TInt CPhoneBookServer::DbgCheckHeap(TInt aCount) const
	{
#ifdef _DEBUG 
	__UHEAP_CHECK(aCount);
#else
	(void) aCount;
#endif

	return(KErrNone);
	} // CPhoneBookServer::DbgCheckHeap


/**
 *  Marks the end of checking the current server's and engine's heap. 
 *
 *  The function expects aCount heap cells to remain allocated at the
 *  current nest level. This function must match an earlier call to
 *  DbgMarkHeap() and only functions on debug releases.
 *
 *  @param aCount  The number of heap cells expected to remain allocated
 *                 at the current nest level.
 *
 *  @return  KErrNone.
 */
TInt CPhoneBookServer::DbgMarkEnd(TInt aCount) const
	{
#ifdef _DEBUG
	__UHEAP_MARKENDC(aCount);
#else
	(void) aCount;
#endif

	return(KErrNone);
	} // CPhoneBookServer::DbgMarkEnd


/**
 *  Simulates heap allocation failure for the sever and engine.
 *
 *  The failure occurs on the next call to new or any of the functions which 
 *  allocate memory from the heap. This is defined only for debug builds.
 *
 *  @param aCount  Determines when the allocation will fail.
 *
 *  @return  KErrNone.
 */
TInt CPhoneBookServer::DbgFailNext(TInt aCount) const
	{
#ifdef _DEBUG
	if (aCount == 0)
		{
		__UHEAP_RESET;
		}
	else
		{
		__UHEAP_FAILNEXT(aCount);
		}
#else
	(void) aCount;
#endif

	return(KErrNone);
	} // CPhoneBookServer::DbgFailNext


/**
 *  Cancel a previous synchronisation request.
 *
 *  @param aSession       Reference to the client's session.
 *  @param aPhonebookUid  UID of the ICC phonebook to cancel the sync request.
 *
 *  @return KErrNone if the request was cancelled, otherwise returns an error.
 */
TInt CPhoneBookServer::DoSynchronisationCancelL(CPhoneBookSession& aSession,
												TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("DoSynchronisationCancelL(): 0x%08x"), aPhonebookUid);

	CancelEngineRequest(ESyncDoSynchronisation, aPhonebookUid,
						KNullContactId, &aSession);

	return(KErrNone);
	} // CPhoneBookServer::DoSynchronisationCancelL


/**
 *  Cancel a previous delete contact request.
 *
 *  @param aSession       Reference to the client's session.
 *  @param aPhonebookUid  UID of the ICC phonebook to cancel the delete request.
 *
 *  @return KErrNone if the request was cancelled, otherwise returns an error.
 */
TInt CPhoneBookServer::DeleteCntFromICCCancelL(CPhoneBookSession& aSession,
												TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("DeleteCntFromICCCancelL(0x%08x)"), aPhonebookUid);

	CancelEngineRequest(ESyncDeleteCntFromICC, aPhonebookUid,
						KNullContactId, &aSession);

	return(KErrNone);
	} // CPhoneBookServer::DeleteCntFromICCCancelL


/**
 *  Cancel a previous write contact request.
 *
 *  @param aSession       Reference to the client's session.
 *  @param aPhonebookUid  UID of the ICC phonebook to cancel the write request.
 *
 *  @return KErrNone if the request was cancelled, otherwise returns an error.
 */
TInt CPhoneBookServer::WriteCntToICCCancelL(CPhoneBookSession& aSession,
											TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("WriteCntToICCCancelL(0x%08x)"), aPhonebookUid);

	CancelEngineRequest(ESyncDoSynchronisation, aPhonebookUid,
						KNullContactId, &aSession);

	return(KErrNone);
	} // CPhoneBookServer::WriteCntToICCCancelL


/**
 *  Stores the current message for later completion when the cache state of the
 *  specified phonebook changes.
 *
 *  @param aSession       Reference to the client's session.
 *  @param aMessage       Reference to the client's request.
 *  @param aPhonebookUid  UID of the ICC phonebook to monitor.
 */
void CPhoneBookServer::NotifyCacheStateChangeL(CPhoneBookSession& aSession,
											   const RMessage2& aMessage,
											   TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("NotifyCacheStateChangeL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		aSession.CompleteRequest(aMessage, KErrNotReady);
		return;
		}

	//	
	// Validate the phonebook UID to ensure notifications can only be posted
	// for valid phonebooks.
	//
	TInt  result;

	result = iPhonebookManager->ValidatePhonebookUid(aPhonebookUid);
	if (result != KErrNone)
		{
		aSession.CompleteRequest(aMessage, result);
		return;
		}

	//
	// Create Notification Request Object and add to the array...
	//
	TCacheStateNotification  notifyRequest;

	notifyRequest.iSession      = &aSession;
	notifyRequest.iMessage      = aMessage;
	notifyRequest.iPhonebookUid = aPhonebookUid;

	result = iCacheStateNotificationArray.Append(notifyRequest);
	if (result != KErrNone)
		{
		aSession.CompleteRequest(aMessage, result);
		return;
		}
	} // CPhoneBookServer::NotifyCacheStateChangeL


/**
 *  Cancel a previous notify state change request.
 *
 *  @param aSession       Reference to the client's session.
 *  @param aPhonebookUid  UID of the ICC phonebook to cancel the notify request.
 *
 *  @return KErrNone if the request was cancelled, otherwise returns an error.
 */
TInt CPhoneBookServer::NotifyCacheStateChangeCancelL(CPhoneBookSession& aSession,
													 TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("NotifyCacheStateChangeCancelL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}
	
	//
	// Cancel the notification by completing the request with KErrCancel...
	//
	for (TInt index = 0;  index < iCacheStateNotificationArray.Count();  index++)
		{
		TCacheStateNotification  notifyRequest = iCacheStateNotificationArray[index];

		if (notifyRequest.iSession == &aSession  &&
			notifyRequest.iPhonebookUid == aPhonebookUid)
			{
			//
			// If it is in the list then cancel it, and remove it...
			//
			aSession.CompleteRequest(notifyRequest.iMessage, KErrCancel);

			iCacheStateNotificationArray.Remove(index);
			index--;
			}
		}

	return(KErrNone);
	} // CPhoneBookServer::NotifyCacheStateChangeCancelL


/**
 *  Returns the last error found during synchronisation on the specified phonebook.
 *
 *  @param aPhonebookUid   UID of the ICC phonebook to examine.
 *  @param aLastSyncError  Returned value of the last error.
 *
 *  @return KErrNone if the last error was returned, otherwise returns an error.
 */
TInt CPhoneBookServer::GetLastSyncErrorL(TUid aPhonebookUid,
										 TInt& aLastSyncError)
	{
	LOGSERVER2(_L8("GetLastSyncErrorL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// This request can only proceed if a sync request has been actioned...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(aPhonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}
	
	if (syncState == RPhoneBookSession::EUnsynchronised)
		{
		return KErrNotReady;
		}

	//
	// Get the last sync error form the phonebook parameters.
	//
	iPhonebookManager->GetLastSyncError(aPhonebookUid, aLastSyncError);

	return KErrNone;
	} // CPhoneBookServer::GetLastSyncErrorL


/**
 *  Returns the total number of slots for a specified phonebook.
 *
 *  @param aPhonebookUid  UID of the ICC phonebook to examine.
 *  @param aNumSlots      Returned value of the number of slots.
 *
 *  @return KErrNone if the value was returned, otherwise returns an error.
 */
TInt CPhoneBookServer::GetNumSlotsL(TUid aPhonebookUid, TInt& aNumSlots)
	{
	LOGSERVER2(_L8("GetNumSlotsL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// Make sure we do not complete with the default value...
	//
	TInt  phBkInfoRetrievedResult;
	TInt  result;

	result = iPhonebookManager->GetPhBkInfoRetrievedResult(aPhonebookUid,
														   phBkInfoRetrievedResult);
	if (result != KErrNone)
		{
		return result;
		}

	if (phBkInfoRetrievedResult != KErrNone)
		{
		return phBkInfoRetrievedResult;
		}

	//
	// This information can be obtained from the Phonebook Info data
	// (TMobilePhoneBookInfoV5) or the RMobilePhoneStore data (which populates)
	// the Look-Up Table size. Since the first option is not always supported,
	// it's easiest (and quickest) to return the Loop-Up Table size...
	//
	result = iPhonebookManager->GetLookUpTableSize(aPhonebookUid, aNumSlots);
	
	return result;
	} // CPhoneBookServer::GetNumSlotsL


/**
 *  Return number of free slots for a specified phonebook.
 *
 *  @param aPhonebookUid  UID of the ICC phonebook to examine.
 *  @param aNumFreeSlots  Returned value of the number of free slots.
 *
 *  @return KErrNone if the value was returned, otherwise returns an error.
 */
TInt CPhoneBookServer::GetNumFreeSlotsL(TUid aPhonebookUid,
										TInt& aNumFreeSlots)
	{
	LOGSERVER2(_L8("GetNumFreeSlotsL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// Get the current sync state for this phonebook...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(aPhonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}

	//
	// If the phone book is synchronised then take the value from the LUT.
	// Otherwise, take the value from the phone info structure. However, 
	// the phone may not support returning this information.
	//
	if (syncState == RPhoneBookSession::ECacheValid)
		{
		result = iPhonebookManager->GetNumFreeSlots(aPhonebookUid, aNumFreeSlots);
		if (result != KErrNone)
			{
			return result;
			}
		}
	else
		{
		RMobilePhoneBookStore::TMobilePhoneBookInfoV5  phBkInfo;
		
		result = iPhonebookManager->GetPhoneBookInfo(aPhonebookUid, phBkInfo);
	
		if (phBkInfo.iTotalEntries < 0  ||  phBkInfo.iUsedEntries < 0)
			{
			//
			// The TSY does not support this information.
			//
			return KErrUnknown;
			}
			
		aNumFreeSlots = phBkInfo.iTotalEntries - phBkInfo.iUsedEntries;
		}
	
	return KErrNone;
	} // CPhoneBookServer::GetNumFreeSlotsL


/**
 *  Return an array of free slot numbers for a specified phonebook.
 *
 *  @param aPhonebookUid  UID of the ICC phonebook to examine.
 *  @param aFreeSlots     Returned list of free slots.
 *
 *  @return KErrNone if the list was returned, otherwise returns an error.
 */
TInt CPhoneBookServer::GetFreeSlotsL(TUid aPhonebookUid,
									 RArray<TInt>& aFreeSlots)
	{
	LOGSERVER2(_L8("GetFreeSlotsL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// This request can only proceed if the cache is valid...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(aPhonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}

	if (syncState != RPhoneBookSession::ECacheValid)
		{
		return KErrNotReady;
		}

	//
	// Return the list of free slots...
	//
	result = iPhonebookManager->GetMatchingEntries(aPhonebookUid, ESlotEmpty,
												   aFreeSlots);
	
	return result;
	} // CPhoneBookServer::GetFreeSlotsL
	

/**
 *  Return the contact ID assigned to an entry at a given slot in a specified
 *  phonebook.
 *
 *  @param aPhonebookUid   UID of the ICC phonebook to examine.
 *  @param aSlotNum        Slot number to examine.
 *  @param aContactItemId  Returned contact ID.
 *
 *  @return KErrNone if the contact ID was returned, otherwise returns an error.
 */
TInt CPhoneBookServer::GetSlotIdL(TUid aPhonebookUid, TInt aSlotNum,
								  TContactItemId& aContactItemId)
	{
	LOGSERVER2(_L8("GetSlotIdL(0x%08x)"), aPhonebookUid);

	//
	// Is the server configured for use?
	//
	if (iServerConfigLevel != EServerConfigurationFull)
		{
		return KErrNotReady;
		}

	//
	// This request can only proceed if the cache is valid...
	//
	RPhoneBookSession::TSyncState  syncState;
	TInt  result;
	
	result = iPhonebookManager->GetSyncState(aPhonebookUid, syncState);
	if (result != KErrNone)
		{
		return result;
		}

	if (syncState != RPhoneBookSession::ECacheValid)
		{
		return KErrNotReady;
		}

	//
	// Get the ID from the phonebook's Look Up Table...
	//
	result = iPhonebookManager->GetContactIdFromSlotNum(aPhonebookUid,
														aSlotNum,
														aContactItemId);
	
	return result;
	} // CPhoneBookServer::GetSlotIdL


/**
 *  Requests the server to shut down when it no longer has any connected
 *  sessions. This procedure is only premitted in debug builds and is provided
 *  for testing purposes.
 *
 *  The server will shutdown when the last session disconnects.
 *
 *  @param aUnconditionally  If True it allows future connects to the server up
 *                           until the last session disconnects.
 *
 *  @return KErrNone if the shutdown request was accepted, otherwise returns
 *          an error.
 */
TInt CPhoneBookServer::ShutdownServer(TBool aUnconditionally)
	{
	LOGSERVER1(_L8("ShutdownServer()"));

	iShouldShutdownServer          = ETrue;
	iShouldShutdownUnconditionally = aUnconditionally;

	//
	// Unconfigure the server now. Otherwise the engine may be holding a
	// handle on the Contacts DB, which could be holding a handle on the
	// PhBkSync plugin, which could be holding the last session on PhBkSync
	// and prevent the server from unconfiguring the engine, etc.
	//
	TRAP_IGNORE(ConfigureServerL(EServerConfigurationNone));
	
	return(KErrNone);
	} // CPhoneBookServer::ShutdownServer


/**
 *  If a notification request is outstanding for the specified phonebook it is
 *  completed.
 *
 *  @param aPhonebookUid  The phonebook for which the sync state has changed.
 */
void CPhoneBookServer::CompleteNotifyStateChange(TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("CompleteNotifyStateChange(): 0x%08x"), aPhonebookUid);

	for (TInt index = 0;  index < iCacheStateNotificationArray.Count();  index++)
		{
		TCacheStateNotification  notifyRequest = iCacheStateNotificationArray[index];

		if (notifyRequest.iPhonebookUid == aPhonebookUid)
			{
			//
			// If it is in the list then  complete the message and then remove
			// from the list.
			//
			notifyRequest.iSession->CompleteRequest(notifyRequest.iMessage,
												    KErrNone);

			iCacheStateNotificationArray.Remove(index);
			index--;
			}
		}
	} // CPhoneBookServer::CompleteNotifyStateChange


/**
 *  Connect to the ETel Sever, obtains the name of the currently selected TSY and
 *  then loads the TSY.
 */
void CPhoneBookServer::ConnectToEtelL()
	{
	LOGSERVER1(_L8("Connect to ETel")); 
	// Obtain the name of the currently selected TSY 	
	
#ifdef SYMBIAN_NON_SEAMLESS_NETWORK_BEARER_MOBILITY
	CMDBSession* db = CMDBSession::NewLC(KCDVersion1_2);
#else
	CMDBSession* db = CMDBSession::NewLC(KCDVersion1_1);
#endif
	
	CMDBField<TUint32>* globalSettingField = new(ELeave) CMDBField<TUint32>(KCDTIdModemPhoneServicesSMS);
	CleanupStack::PushL(globalSettingField);

	globalSettingField->SetRecordId(1);
	globalSettingField->LoadL(*db);
	TUint32 modemId = *globalSettingField;

	CMDBField<TDesC>* tsyField = new(ELeave) CMDBField<TDesC>(KCDTIdTsyName);
	CleanupStack::PushL(tsyField);

	tsyField->SetRecordId(modemId);
	TRAPD(err, tsyField->LoadL(*db));
	if (err != KErrNone)
		{
		LOGSERVER1(_L8("Unable to get default TSY"));
		}
	User::LeaveIfError(err);

	TBuf<KCommsDbSvrMaxFieldLength> tsyName;
	tsyName = *tsyField;

	CleanupStack::PopAndDestroy(3, db); // db, tsyField & globalSettingField
	
	TInt ret(iEtelServer.Connect()); // First connect to the ETel server
	if(ret==KErrNone)
		{
		LOGSERVER1(_L8("Loading TSY")); // Now load the TSY 
		ret=iEtelServer.LoadPhoneModule(tsyName);
		RTelServer::TPhoneInfo phoneInfo;
		if(ret==KErrNone)
			{
			// Determine if TSY supports V2 functionality
			// in event of a problem here assume that it does not
			ret = iEtelServer.IsSupportedByModule(tsyName, KETelExtMultimodeV2, iIsV2Tsy);
			if (ret != KErrNone)
				{
				iIsV2Tsy = EFalse;
				}

			// Determine if TSY supports V5 functionality
 			// in event of a problem here assume that it does not
 			ret = iEtelServer.IsSupportedByModule(tsyName, KEtelExtMultimodeV5, iIsV5Tsy);
 			if (ret != KErrNone)
 				{
 				iIsV5Tsy = EFalse;
 				}

			// Determine if TSY supports USIM Apps
			// in event of a problem here assume that it does not
			ret = iEtelServer.IsSupportedByModule(tsyName, KEtelFuncMobileUSIMApplications, iIsUsimAppTsy);
			if (ret != KErrNone)
				{
				iIsUsimAppTsy = EFalse;
				}

			TInt phoneIndex(0);
			iEtelServer.EnumeratePhones(phoneIndex); // Get total number of phones supported by all currently loaded TSY modules
			while(phoneIndex-->0)
				{
				TName searchTsyName;
				// Check whether this phone belongs to loaded TSY
				if((iEtelServer.GetTsyName(phoneIndex,searchTsyName)==KErrNone) && (searchTsyName.CompareF(tsyName)==KErrNone)) 
					break;
				}
			iEtelServer.GetPhoneInfo(phoneIndex,phoneInfo); // Get information associated with specified phone

			LOGSERVER1(_L8("Open the phone"));
			ret = iPhone.Open(iEtelServer,phoneInfo.iName); // Open and intialise the phone
			if (ret==KErrNone)
				{
				RPhone::TStatus phoneStatus;
				iPhone.GetStatus(phoneStatus); 
				if(phoneStatus.iMode==RPhone::EModeUnknown) // Check whether phone has already been initialised
					{	
					ret=iPhone.Initialise();
					
					if(ret!=KErrNone)
						{	
						LOGSERVER2(_L8("Phone initialisation failed - closing phone (ret=%d)"), ret);
						iPhone.Close();
						}
					}

				if(ret==KErrNone) // Do not even try to open SAT if phone not successfully initialised
					{
					TInt retSat=iSat.Open(iPhone);
					if(retSat==KErrNone)
						{
						iIsSatSupported=ETrue; // SAT supported by TSY so proceed with SAT processing
						// Determine if TSY supports V2 SAT - in event of a problem assume that it does not
						ret = iEtelServer.IsSupportedByModule(tsyName, KETelExtSatV2, iIsV2SAT);
						if (ret != KErrNone)
							{
							iIsV2SAT = EFalse;
							}
						}
					else
						{
						LOGSERVER2(_L8("Could not start ETel SAT (retSat=%d)"), retSat);
						iIsSatSupported = EFalse;
						}
					}
				}
			}
		else
			{
			LOGSERVER2(_L8("Could not load the TSY (ret=%d)"), ret);
			}
		}
	else
		{
		LOGSERVER2(_L8("Could not connect to ETel (ret=%d)"), ret);
		}

	User::LeaveIfError(ret);
	} // CPhoneBookServer::ConnectToEtelL


/**
 *  Complete an outstanding GetPhoneStoreInfo request.
 *
 *  @note  Any errors will cause the phonebook setup to be ignored. This puts
 *         the phonebook in limbo as it is supported but not usable.
 *
 *  @param aRetVal        Result of the request.
 *  @param aStoreInfo     Phonebook store information containing the phonebook ID.
 *  @param aPhonebookUid  Type of the ICC phonebook used internally by the server 
 */
void CPhoneBookServer::CompleteGetPhoneStoreInfo(TInt aRetVal, 
											     RMobilePhoneBookStore::TMobilePhoneBookInfoV5& aStoreInfo, 
					 							 TUid aPhonebookUid)
	{
	LOGSERVER4(_L8("CompleteGetPhoneStoreInfo(): retVal=%d phonebook=0x%08x "
				   "Identity=\"%S\""), aRetVal, aPhonebookUid,
				   &aStoreInfo.iIdentity);

	if (aRetVal == KErrNone)
		{
		//
		// First log the structure as the contents have been the cause of a
		// number of defects so far...
		//		
#ifdef _DEBUG
		TBuf8<RMobilePhone::KMaxMobileNameSize>  nameIn8bit;
		nameIn8bit.Copy(aStoreInfo.iName);
#endif

		LOGSERVER2(_L8("PhoneBookInfo: iExtensionId=%d"), aStoreInfo.ExtensionId());
		LOGSERVER2(_L8("PhoneBookInfo: iType=%d"), aStoreInfo.iType);
		LOGSERVER2(_L8("PhoneBookInfo: iTotalEntries=%d"), aStoreInfo.iTotalEntries);
		LOGSERVER2(_L8("PhoneBookInfo: iUsedEntries=%d"), aStoreInfo.iUsedEntries);
		LOGSERVER2(_L8("PhoneBookInfo: iCaps=0x%08x"), aStoreInfo.iCaps);
		LOGSERVER2(_L8("PhoneBookInfo: iName=\"%S\""), &nameIn8bit);

		if (aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV1  ||
			aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV2  ||
			aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV5)
			{
			LOGSERVER2(_L8("PhoneBookInfo: iMaxNumLength=%d"), aStoreInfo.iMaxNumLength);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxTextLength=%d"), aStoreInfo.iMaxTextLength);
			LOGSERVER2(_L8("PhoneBookInfo: iLocation=%d"), (TInt) aStoreInfo.iLocation);
			LOGSERVER2(_L8("PhoneBookInfo: iChangeCounter=%d"), (TInt) aStoreInfo.iChangeCounter);
			LOGSERVER2(_L8("PhoneBookInfo: iIdentity=\"%S\""), &aStoreInfo.iIdentity);
			}
		
		if (aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV2  ||
			aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV5)
			{
#ifdef _DEBUG
			TBuf8<KMaxName>  phBkModeIn8bit;
			phBkModeIn8bit.Copy(aStoreInfo.iPhBkMode);
#endif

			LOGSERVER2(_L8("PhoneBookInfo: iPhBkMode=\"%S\""), &phBkModeIn8bit);
			}
		
		if (aStoreInfo.ExtensionId() == RMobilePhoneBookStore::KETelMobilePhonebookStoreV5)
			{
			LOGSERVER2(_L8("PhoneBookInfo: iMaxSecondNames=%d"), aStoreInfo.iMaxSecondNames);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxTextLengthSecondName=%d"), aStoreInfo.iMaxTextLengthSecondName);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxAdditionalNumbers=%d"), aStoreInfo.iMaxAdditionalNumbers);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxNumLengthAdditionalNumber=%d"), aStoreInfo.iMaxNumLengthAdditionalNumber);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxTextLengthAdditionalNumber=%d"), aStoreInfo.iMaxTextLengthAdditionalNumber);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxGroupNames=%d"), aStoreInfo.iMaxGroupNames);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxTextLengthGroupName=%d"), aStoreInfo.iMaxTextLengthGroupName);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxEmailAddr=%d"), aStoreInfo.iMaxEmailAddr);
			LOGSERVER2(_L8("PhoneBookInfo: iMaxTextLengthEmailAddr=%d"), aStoreInfo.iMaxTextLengthEmailAddr);
			}
		
		//
		// Should a TSY return iTotalEntries as 0 for ADN then we are in big trouble and
		// no sync will be possible. It is an error in the TSY so we must panic.
		//
		if (aPhonebookUid == KUidIccGlobalAdnPhonebook  &&
			aStoreInfo.iTotalEntries == 0)
			{
			PhBkSyncPanic(EPhBkSyncPanicGetPhoneStoreInfoError);
			}
		
		//
		// Create the default contacts field structure for this phonebook.
		// These are default settings for all phonebooks regardless of type
		// of ICC.
		//
		RPhoneBookSession::TContactFieldsV3  contactFieldsV3;
		TInt  result;
	
		contactFieldsV3.iNameField.iLength    = aStoreInfo.iMaxTextLength;
		contactFieldsV3.iNameField.iCount     = 1;
		contactFieldsV3.iNameField.iDisplayed = ETrue;

		contactFieldsV3.iNumberField.iLength    = aStoreInfo.iMaxNumLength;
		contactFieldsV3.iNumberField.iCount     = 1;
		contactFieldsV3.iNumberField.iDisplayed = ETrue;
	
		contactFieldsV3.iIccSlotField.iLength    = -1;
		contactFieldsV3.iIccSlotField.iCount     = 1;
		contactFieldsV3.iIccSlotField.iDisplayed = ETrue;
	
		contactFieldsV3.iPhonebook = aPhonebookUid;

		if (iICCCaps & RMobilePhone::KCapsUSimAccessSupported)
			{
			//
			// Set up the 3G ICC contact settings.  There are additional fields
			// for ADN and USIM App phonebooks.
			//
			if (aPhonebookUid == KUidIccGlobalAdnPhonebook  || 
				aPhonebookUid == KUidUsimAppAdnPhonebook)
				{
				if (aStoreInfo.ExtensionId()==RMobilePhoneBookStore::KETelMobilePhonebookStoreV5)
					{
			 		contactFieldsV3.iAdditionalNumString.iLength    = aStoreInfo.iMaxNumLengthAdditionalNumber;
			 		contactFieldsV3.iAdditionalNumString.iCount     = aStoreInfo.iMaxAdditionalNumbers;
			 		contactFieldsV3.iAdditionalNumString.iDisplayed = ETrue;
			 
			 		contactFieldsV3.iAdditionalNumAlphaString.iLength    = aStoreInfo.iMaxTextLengthAdditionalNumber;
			 		contactFieldsV3.iAdditionalNumAlphaString.iCount     = aStoreInfo.iMaxAdditionalNumbers;
			 		contactFieldsV3.iAdditionalNumAlphaString.iDisplayed = ETrue;
			 
			 		contactFieldsV3.iGroupField.iLength    = aStoreInfo.iMaxTextLengthGroupName;
			 		contactFieldsV3.iGroupField.iCount     = aStoreInfo.iMaxGroupNames;
			 		contactFieldsV3.iGroupField.iDisplayed = ETrue;
			 
			 		contactFieldsV3.iEmailField.iLength    = aStoreInfo.iMaxTextLengthEmailAddr;
			 		contactFieldsV3.iEmailField.iCount     = aStoreInfo.iMaxEmailAddr;
			 		contactFieldsV3.iEmailField.iDisplayed = ETrue;
			 
			 		contactFieldsV3.iSecondNameField.iLength    = aStoreInfo.iMaxTextLengthSecondName;
			 		contactFieldsV3.iSecondNameField.iCount     = aStoreInfo.iMaxSecondNames;
			 		contactFieldsV3.iSecondNameField.iDisplayed = ETrue;
			 		}
			 	else
			 		{
					contactFieldsV3.iAdditionalNumString.iLength    = aStoreInfo.iMaxTextLength;
					contactFieldsV3.iAdditionalNumString.iCount     = -1;
					contactFieldsV3.iAdditionalNumString.iDisplayed = ETrue;

					contactFieldsV3.iAdditionalNumAlphaString.iLength    = aStoreInfo.iMaxTextLength;
					contactFieldsV3.iAdditionalNumAlphaString.iCount     = -1;
					contactFieldsV3.iAdditionalNumAlphaString.iDisplayed = ETrue;

					contactFieldsV3.iGroupField.iLength    = aStoreInfo.iMaxTextLength;
					contactFieldsV3.iGroupField.iCount     = -1;
					contactFieldsV3.iGroupField.iDisplayed = ETrue;

					contactFieldsV3.iEmailField.iLength    = aStoreInfo.iMaxTextLength;
					contactFieldsV3.iEmailField.iCount     = -1;
					contactFieldsV3.iEmailField.iDisplayed = ETrue;			 		

					contactFieldsV3.iSecondNameField.iLength    = aStoreInfo.iMaxTextLength;
					contactFieldsV3.iSecondNameField.iCount     = -1;
					contactFieldsV3.iSecondNameField.iDisplayed = ETrue;
			 		}
				}
			}
		
		result = iPhonebookManager->SetContactFields(aPhonebookUid, contactFieldsV3);
		if (result != KErrNone)
			{
			return;
			}

		//
		// Initialise the Look Up Table...
		//
		result = iPhonebookManager->SetLookUpTableSize(aPhonebookUid, aStoreInfo.iTotalEntries);
		if (result != KErrNone)
			{
			return;
			}

		//
		// Get the current sync mode. This is used later and also to decide if
		// we need to get the previous phonebook info...
		//
		RPhoneBookSession::TPhonebookSyncMode  syncMode;
		
		result = iPhonebookManager->GetSyncMode(aPhonebookUid, syncMode);
		if (result != KErrNone)
			{
			return;
			}
		
		//
		// Get the previous phonebook info if we need it...
		//
		RMobilePhoneBookStore::TMobilePhoneBookInfoV5  oldPhBkInfo;

		if (syncMode == RPhoneBookSession::EAutoSameIcc)
			{
			result = iPhonebookManager->GetPhoneBookInfo(aPhonebookUid, oldPhBkInfo);
			if (result != KErrNone)
				{
				return;
				}
			}
		
		//
		// Store the new phonebook info...
		//
		iPhonebookManager->SetPhoneBookInfo(aPhonebookUid, aStoreInfo);
		iPhonebookManager->SetPhBkInfoRetrievedResult(aPhonebookUid, aRetVal);
		
		//
		// If the phonebook is in auto-sync mode then queue a sync request...
		//
		if (syncMode == RPhoneBookSession::EAutoCurrentIcc  ||
		    (syncMode == RPhoneBookSession::EAutoSameIcc  &&  aStoreInfo.iIdentity == oldPhBkInfo.iIdentity))
			{
			QueueAutoSyncRequest(aPhonebookUid);
			}
		}
	else
		{
		//
		// The phonebook is not available or supported.
		//
		iPhonebookManager->SetPhBkInfoRetrievedResult(aPhonebookUid, aRetVal);

		//
		// If the sync mode is auto-sync current then perform an auto sync in
		// case any entries exist in the database from previous syncs. The
		// sync will fail, but the engine will silently clear the entries out.
		//
		RPhoneBookSession::TPhonebookSyncMode  syncMode;
		TInt  result;
		
		result = iPhonebookManager->GetSyncMode(aPhonebookUid, syncMode);
		if (result == KErrNone  &&
		    syncMode == RPhoneBookSession::EAutoCurrentIcc)
			{
			QueueAutoSyncRequest(aPhonebookUid);
			}

		}
	} // CPhoneBookServer::CompleteGetPhoneStoreInfo


/**
 *  Complete an outstanding NotifySecurityEvent request. The Active Notification Base
 *  class is responsible for reposting the notification.
 *
 *  @param aRetVal         Result of the notification request.
 *  @param aSecurityEvent  Type of Security Event received.
 */
void CPhoneBookServer::CompleteNotifySecurityEvent(TInt aRetVal,
												   RMobilePhone::TMobilePhoneSecurityEvent aSecurityEvent)
	{
	if (aRetVal == KErrNone)
		{
#ifdef _DEBUG
		const char*  KEventNames[23] = {"ENoICCFound", "EICCTerminated",
									    "EPin1Required", "EPuk1Required",
									    "EPin2Required", "EPuk2Required",
									    "EPhonePasswordRequired",
									    "ESPCRequired", "EPin1Verified",
									    "EPin2Verified", "EPuk1Verified",
									    "EPuk2Verified",
									    "EPhonePasswordVerified", "ESPCVerified",
									    "EHiddenKeyRequired", "EHiddenKeyVerified",
									    "EUSIMAppPinRequired", "EUSIMAppPinVerified",
									    "ESecondUSIMAppPinRequired",
									    "ESecondUSIMAppPinVerified",
									    "EUniversalPinRequired",
									    "EUniversalPinVerified",
									    "ESPCChanged"};
#endif

		LOGSERVER2(_L8("CompleteNotifySecurityEvent(): %s."), KEventNames[aSecurityEvent]);

		iPhonebookManager->RecordSecurityEvent(aSecurityEvent);
		}
	} // CPhoneBookServer::CompleteNotifySecurityEvent


/**
 *  Complete an outstanding NotifyLockInfoChange request. The Active Notification Base
 *  class is responsible for reposting the notification.
 *
 *  @param aRetVal    Result of the notification request.
 *  @param aLock      Mobile phone lock.
 *  @param aLockInfo  Status of the changed lock.
 */
void CPhoneBookServer::CompleteNotifyLockInfoChange(TInt aRetVal,
													RMobilePhone::TMobilePhoneLock& aLock,
		                                            RMobilePhone::TMobilePhoneLockInfoV1& aLockInfo)
	{
	if (aRetVal == KErrNone)
		{
#ifdef _DEBUG
		const char*  KStatusNames[4] = {"unknown", "locked", "unlocked", "blocked"};
		const char*  KSettingNames[4] = {"unknown", "enabled", "disabled", "replaced"};
#endif

		if (aLock == RMobilePhone::ELockICC)
			{
			LOGSERVER3(_L8("Lock Info: PIN1 lock is %s and %s."),
					 KStatusNames[aLockInfo.iStatus], KSettingNames[aLockInfo.iSetting]);
			iPhonebookManager->SetPin1LockStatus(aLockInfo.iStatus);

			//
			// If the status has become unlocked, then ensure all phonebooks that
			// have previously attempted to be sync'd (and failed) are sync'd now.
			//
			if (aLockInfo.iStatus == RMobilePhone::EStatusUnlocked)
				{
				TInt  phonebookCount(iPhonebookManager->GetPhonebookCount());

				for(TInt phonebook = 0;  phonebook < phonebookCount;  phonebook++)
					{
					TUid  phonebookUid;
					TInt  result = iPhonebookManager->GetPhonebookUid(phonebook, phonebookUid);
					
					if (result == KErrNone)
						{
						RPhoneBookSession::TSyncState  syncState;

						result = iPhonebookManager->GetSyncState(phonebookUid, syncState);
			
						if (result == KErrNone  &&
						    syncState != RPhoneBookSession::EUnsynchronised)
							{
							iPhonebookManager->SetSyncState(phonebookUid,
							                                RPhoneBookSession::EUnsynchronised);
							QueueAutoSyncRequest(phonebookUid);
							}
						}
					}
				}
			}
		else if (aLock == RMobilePhone::ELockPin2)
			{
			LOGSERVER3(_L8("Lock Info: PIN2 lock is %s and %s."),
					 KSettingNames[aLockInfo.iStatus], KStatusNames[aLockInfo.iSetting]);
			iPhonebookManager->SetPin2LockStatus(aLockInfo.iStatus);
			}
		else if (aLock == RMobilePhone::ELockHiddenKey)
			{
			LOGSERVER3(_L8("Lock Info: Hidden key lock is %s and %s."),
					 KSettingNames[aLockInfo.iStatus], KStatusNames[aLockInfo.iSetting]);
			iPhonebookManager->SetHiddenKeyLockStatus(aLockInfo.iStatus);

			if (aLockInfo.iStatus != RMobilePhone::EStatusLockUnknown)
				{
				//
				// If the USIM ADN phonebook is currently synchronised, or
				// being synchronised, then re-sync it.
				//
				RPhoneBookSession::TSyncState  syncState;
				TInt  result;
			
				result = iPhonebookManager->GetSyncState(KUidUsimAppAdnPhonebook,
														 syncState);
			
				if ((result == KErrNone  &&
				    syncState != RPhoneBookSession::EUnsynchronised)  ||
				    IsEngineRequestQueued(ESyncDoSynchronisation,
										  KUidUsimAppAdnPhonebook,
										  ETrue, ETrue))
					{
					QueueAutoSyncRequest(KUidUsimAppAdnPhonebook);
					}
				}
			}
		else if (aLock == RMobilePhone::ELockUSimApp)
			{
			LOGSERVER3(_L8("Lock Info: USim App lock is %s and %s."),
					 KSettingNames[aLockInfo.iStatus], KStatusNames[aLockInfo.iSetting]);
			iPhonebookManager->SetUsimAppLockStatus(aLockInfo.iStatus);
			}
		else if (aLock == RMobilePhone::ELockUniversalPin)
			{
			LOGSERVER3(_L8("Lock Info: USim Universal PIN is %s and %s."),
					 KSettingNames[aLockInfo.iStatus], KStatusNames[aLockInfo.iSetting]);
			iPhonebookManager->SetUsimUniversalPinLockStatus(aLockInfo.iStatus);
			}
		else
			{
			LOGSERVER3(_L8("Lock Info: Unknown lock is %s (%s)."),
					 KStatusNames[aLockInfo.iStatus], KSettingNames[aLockInfo.iSetting]);
			}
		}
	} // CPhoneBookServer::CompleteNotifyLockInfoChange


/**
 *  Complete an outstanding NotifySATUpdates request. The Active Notification Base
 *  class is responsible for reposting the notification.
 *
 *  @param aRetVal         Result of the notification request.
 *  @param aRefreshType    Type of SAT refresh.
 *  @param aPhonebookList  List of phonebooks affected the update. 
 */
void CPhoneBookServer::CompleteNotifySATUpdates(TInt aRetVal,
												RSat::TRefreshType aRefreshType,
												RArray<TUid>& aPhonebookList)
	{
	LOGSERVER2(_L8("CompleteNotifySATUpdates(): %d"), aRetVal);

	if (aRetVal == KErrNone)
		{
		//
		// Is a full re-synchronisation needed???
		//
		if (aRefreshType == RSat::EFileChangeNotification  || 
			aRefreshType == RSat::ESimInitFileChangeNotification)
			{
			//
			// Set cache state to unsynchronised for all affected phonebooks
			// from phonebook list and queue new sync requests...
			//
			TInt  phonebookCount(aPhonebookList.Count());

			for(TInt phonebook = 0;  phonebook < phonebookCount;  phonebook++)
				{
				TUid  phonebookUid = aPhonebookList[phonebook];
				
				iPhonebookManager->SetSyncState(phonebookUid, RPhoneBookSession::EUnsynchronised);
				CompleteNotifyStateChange(phonebookUid);
				iGetPhoneStoreInfo->QueueGetInfoAndSync(phonebookUid);
				}
			}
		else if (aRefreshType == RSat::ESimInitFullFileChangeNotification)
			{
			//
			// Set cache state to unsynchronised for all phonebooks and
			// then sync them all...
			//
			TInt phonebookCount(iPhonebookManager->GetPhonebookCount());
			TUid phonebookUid;

			for(TInt phonebook = 0; phonebook < phonebookCount; phonebook++)
				{
				TInt  err = iPhonebookManager->GetPhonebookUid(phonebook, phonebookUid);
				
				if (err == KErrNone)
					{
					iPhonebookManager->SetSyncState(phonebookUid,RPhoneBookSession::EUnsynchronised);
					CompleteNotifyStateChange(phonebookUid);
					iGetPhoneStoreInfo->QueueGetInfoAndSync(phonebookUid);
					}
				}
			}
		}
	} // CPhoneBookServer::CompleteNotifySATUpdates


/**
 *  Complete an outstanding NotifyAppInfoChange request by passing the AID of
 *  the currently active  USIM application in the aActiveUsimAID parameter. The
 *  Active Notification Base class is responsible for reposting the notification.
 *
 *  If the Active App has changed and the USIM App phonebook is synchronised (or
 *  has attempted to be synchronised) this a re-synchornisation is performed.
 *
 *  @param aRetVal         Result of the notification request.
 *  @param aInitialValue   True if this is the first value, false if it is an update.
 *  @param aActiveUsimAID  The AID of the currently active USIM Application.
 */
void CPhoneBookServer::CompleteNotifyAppInfoChange(TInt aRetVal,
												   TBool aInitialValue,
												   RMobilePhone::TAID& aActiveUsimAID)
	{
	LOGSERVER2(_L8("CompleteNotifyAppInfoChange(): aRetVal=%d"), aRetVal);

	if (aRetVal == KErrNone)
		{
		LOGSERVER2(_L8("CompleteNotifyAppInfoChange(): aActiveUsimAID=\"%S\""),
				   &aActiveUsimAID);

		//
		// Is this the initial value or an update?
		//
		if (aInitialValue)
			{
			iActiveUsimAID = aActiveUsimAID;
			}
		else
			{
			//
			// The App may have been updated, so see if it has changed...
			//
			if (iActiveUsimAID.CompareF(aActiveUsimAID) != 0)
				{
				//
				// If the USIM ADN phonebook is currently synchronised, or being
				// synchronised then re-sync it.
				//
				RPhoneBookSession::TSyncState  syncState;
				TInt  result;

				result = iPhonebookManager->GetSyncState(KUidUsimAppAdnPhonebook,
														 syncState);

				if ((result == KErrNone  &&
				    syncState != RPhoneBookSession::EUnsynchronised)  ||
				    IsEngineRequestQueued(ESyncDoSynchronisation,
										  KUidUsimAppAdnPhonebook,
										  ETrue, ETrue))
					{
					QueueAutoSyncRequest(KUidUsimAppAdnPhonebook);
					}

				iActiveUsimAID = aActiveUsimAID;
				}
			}
		}
	} // CPhoneBookServer::CompleteNotifyAppInfoChange


/**
 *  Standard Active Object RunError() method, called when the RunL() method
 *  leaves, which will be when the CPhoneBookSession::ServiceL() leaves.
 *
 *  Find the current message and complete it before restarting the server.
 *
 *  @param aError  Leave code from CPhoneBookSession::ServiceL().
 *
 *  @return KErrNone
 */
TInt CPhoneBookServer::RunError(TInt aError)
	{
	LOGSERVER2(_L8("RunError %d"), aError);

	//
	// Complete the request with the available error code.
	//
	if (Message().IsNull() == EFalse)
		{
		Message().Complete(aError);
		}

	//
	// The leave will result in an early return from CServer::RunL(), skipping
	// the call to request another message. So do that now in order to keep the
	// server running.
	//
	ReStart();

	return KErrNone;
	} // CPhoneBookServer::RunError


/**
 *  Queues an engine request. The request will be sent to the engine when the
 *  engine is not busy.
 *
 *  @param aPhonebookSyncRequest  Function request to queue.
 *  @param aPhonebookUid          Phonebook to perform the request on.
 *  @param aContactId             Optional Contact ID parameter.
 *  @param aClientSession         Pointer to the client session, or NULL.
 *  @param aClientMessage         The client message request.
 */
void CPhoneBookServer::QueueEngineRequestL(TPhonebookSyncRequest aPhonebookSyncRequest,
										   TUid aPhonebookUid,
										   TContactItemId aContactId,
										   CPhoneBookSession* aClientSession,
										   const RMessage2& aClientMessage)
	{
	LOGSERVER5(_L8("QueueEngineRequest(): IPC=%d Phonebook=0x%08x Contact=0x%08x "
				   "Session=0x%08x"),
			   aPhonebookSyncRequest, aPhonebookUid, aContactId,
			   (TInt) aClientSession);

	//
	// Create a new request and append it to the list of requests...
	//
	CSyncEngineRequest*  request = CSyncEngineRequest::NewL(*this, iSyncEngine,
															aPhonebookSyncRequest,
															aPhonebookUid, aContactId,
															aClientSession, aClientMessage);
	CleanupStack::PushL(request);
	iSyncEngineRequestArray.AppendL(request);
	CleanupStack::Pop(request);
	
	//
	// Ensure that the request runs if nothing else is waiting...
	//
	StartNextEngineRequest();
	} // CPhoneBookServer::QueueEngineRequest


/**
 *  Start the next engine request if one is available and the engine is not
 *  busy.
 */
void CPhoneBookServer::StartNextEngineRequest()
	{
	LOGSERVER1(_L8("StartNextEngineRequest()"));

	//
	// If the next request in the queue is not running then start it...
	//
	if (iSyncEngineRequestArray.Count() > 0  &&
	    iSyncEngineRequestArray[0]->IsActive() == EFalse)
		{
		iSyncEngineRequestArray[0]->StartRequest();
		}
	} // CPhoneBookServer::StartNextEngineRequest


/**
 *  Completes an Engine Request. This method is called be the Engine Request
 *  itself when it finishes. This will cause the request to be removed from
 *  the queue, and a new request to be started if one is available.
 *
 *  @param aEngineRequest  Reference to the Engine Request.
 */
void CPhoneBookServer::CompleteEngineRequest(CSyncEngineRequest& aEngineRequest)
	{
	LOGSERVER4(_L8("CompleteEngineRequest(): IPC=%d Phonebook=0x%08x "
				   "Session=0x%08x"),
			   aEngineRequest.PhonebookSyncRequest(),
			   aEngineRequest.PhonebookUid(),
			   (TInt) aEngineRequest.ClientSession());

	//
	// Remove this request from the list...
	//
	TInt  position = iSyncEngineRequestArray.Find(&aEngineRequest);
	
	if (position != KErrNotFound)
		{
		iSyncEngineRequestArray.Remove(position);
		}
	
	//
	// Start any queued requests that can now begin since the engine finished
	// the last request...
	//
	StartNextEngineRequest();
	} // CPhoneBookServer::CompleteEngineRequest


/**
 *  Cancel a previous engine request. A request that is in progress is cancelled,
 *  while a request that has not reached the engine will be deleted from the queue.
 *
 *  @param aPhonebookSyncRequest  Function request to cancel.
 *  @param aPhonebookUid          Phonebook UID used in the request.
 *  @param aContactId             Optional Contact ID parameter.
 *  @param aClientSession         Pointer to the client session, or NULL.
 */
void CPhoneBookServer::CancelEngineRequest(TPhonebookSyncRequest aPhonebookSyncRequest,
										   TUid aPhonebookUid,
										   TContactItemId aContactId,
										   CPhoneBookSession* aClientSession)
	{
	LOGSERVER5(_L8("CancelEngineRequest(): IPC=%d Phonebook=0x%08x Contact=0x%08x "
				   "Session=0x%08x"), aPhonebookSyncRequest, aPhonebookUid,
			   aContactId, (TInt) aClientSession);

	//
	// Search through the list of engine requests and if we find this request,
	// then cancel it.
	//
	TInt  requestCount(iSyncEngineRequestArray.Count());

	for (TInt index = 0;  index < requestCount;  index++)
		{
		CSyncEngineRequest*  request = iSyncEngineRequestArray[index];
		
		if (request->PhonebookSyncRequest() == aPhonebookSyncRequest  &&
			request->PhonebookUid() == aPhonebookUid  &&
			request->ContactId() == aContactId  &&
			request->ClientSession() == aClientSession)
			{
			if (request->IsActive())
				{
				//
				// The request is running, so cancel it.
				//
				request->DoCancel();
				}
			else
				{
				//
				// The request never ran so we need to pretend it was cancelled.
				//
				request->iStatus = KErrCancel;
				TRAP_IGNORE(request->CompleteClientRequestL());
				}

			break;
			}
		}
	} // CPhoneBookServer::CompleteEngineRequest


/**
 *  Checks if a previous engine request is queued.
 *
 *  @param aPhonebookSyncRequest   Function request to look for.
 *  @param aPhonebookUid           Phonebook UID used in the request.
 *  @param aIncludeUserRequests    If true then requests from the client
 *                                 should be included in the search.
 *  @param aIncludeActiveRequests  If true then currently executing requests
 *                                 should be included in the search.
 */
TBool CPhoneBookServer::IsEngineRequestQueued(TPhonebookSyncRequest aPhonebookSyncRequest,
											  TUid aPhonebookUid,
											  TBool aIncludeUserRequests,
											  TBool aIncludeActiveRequests)
	{
	LOGSERVER5(_L8("IsEngineRequestQueued(): %d 0x%08x %d %d"),
			   aPhonebookSyncRequest, aPhonebookUid, aIncludeUserRequests,
			   aIncludeActiveRequests);
	
	//
	// Search through the list of engine requests and looking for the request,
	// including active and user requests if required.
	//
	TInt  requestCount(iSyncEngineRequestArray.Count());

	for (TInt index = 0;  index < requestCount;  index++)
		{
		CSyncEngineRequest*  request = iSyncEngineRequestArray[index];
		
		if (request->PhonebookSyncRequest() == aPhonebookSyncRequest  &&
			request->PhonebookUid() == aPhonebookUid  &&
			(aIncludeUserRequests  ||  request->ClientSession() == NULL)  &&
			(aIncludeActiveRequests  ||  request->IsActive() == EFalse))
			{
			LOGSERVER1(_L8("IsEngineRequestQueued(): Found"));
			return ETrue;
			}
		}

	LOGSERVER1(_L8("IsEngineRequestQueued(): Not found"));
	return EFalse;
	} // CPhoneBookServer::IsEngineRequestQueued


/**
 *  Queue an automatic synchronisation to the engine.
 *
 *  @param aPhonebookUid   Phonebook to synchronise.
 */
void CPhoneBookServer::QueueAutoSyncRequest(TUid aPhonebookUid)
	{
	LOGSERVER2(_L8("QueueAutoSyncRequest(): 0x%08x"), aPhonebookUid);

	//
	// First check if a request has already been made for an auto-sync on the
	// same phonebook (excluding user requests and requests in progress).
	// If a request already exists then we can ignore this request.
	//
	if (IsEngineRequestQueued(ESyncDoSynchronisation, aPhonebookUid,
							  EFalse, EFalse))
		{
		return;
		}

	//
	// Queue an engine request for a synchronisation...
	//
	TRAPD(err, QueueEngineRequestL(ESyncDoSynchronisation, aPhonebookUid,
						           KNullContactId, NULL, RMessage2()));
	if (err != KErrNone)
		{
		//
		// The request could not be queued!!!
		//
		LOGSERVER2(_L8("QueueAutoSyncRequest(): Error %d"), err);

		iPhonebookManager->SetLastSyncError(aPhonebookUid, err);
		iPhonebookManager->SetSyncState(aPhonebookUid, RPhoneBookSession::EErrorDuringSync);

		CompleteNotifyStateChange(aPhonebookUid);
		}
	} // CPhoneBookServer::QueueAutoSyncRequest


/**
 *  Factory method for producing a CPhoneBookSyncScheduler object. This creates
 *  and installs the Active Scheduler object.
 *
 *  @return Returns a pointer to the CPhoneBookSyncScheduler object or NULL.
 */
CPhoneBookSyncScheduler* CPhoneBookSyncScheduler::NewL()
	{
	LOGSERVER1(_L8("CPhoneBookSyncScheduler::NewL()"));

	CPhoneBookSyncScheduler*  self = new(ELeave) CPhoneBookSyncScheduler();
	CPhoneBookSyncScheduler::Install(self);

	return self;
	} // CPhoneBookSyncScheduler::NewL


/**
 *  Standard Active Scheduler Error() method. This is called if any of the
 *  RunErrorL() functions leave. Hopefully this should not happen!
 *
 *  @param aError  Leave code from the RunErrorL() function.
 */
void CPhoneBookSyncScheduler::Error(TInt aError) const
	{
#ifdef _DEBUG
	LOGSERVER2(_L8("CPhoneBookSyncScheduler::Error %d"), aError);
#else
	(void) aError;
#endif

	PhBkSyncPanic(EPhBkSyncPanicUnexpectedLeave);
	} // CPhoneBookSyncScheduler::Error


/**
 *  Standard constructor.
 *
 *  @param aServer  Reference to the main server.
 */
CPhoneBookSyncStarter::CPhoneBookSyncStarter(CPhoneBookServer& aServer)
  : CAsyncOneShot(EPriorityHigh),
    iServer(aServer)
	{
	// NOP
	} // CPhoneBookSyncStarter::CPhoneBookSyncStarter


/**
 *  RunL() for the starter object. This configures the server fully or
 *  shuts it down.
 */
void CPhoneBookSyncStarter::RunL()
	{
	LOGSERVER2(_L8("CPhoneBookSyncStarter::RunL(): iStatus=%d."), iStatus.Int());

	//
	// Configure the server...
	//
	TRAPD(configErr, iServer.ConfigureServerL(CPhoneBookServer::EServerConfigurationFull));
	if (configErr != KErrNone)
		{
		LOGSERVER2(_L8("ConfigureServerL() failed with error %d."), configErr);

		//
		// Shutdown the server in this case, so it can be restarted later.
		//
		TRAP_IGNORE(iServer.ConfigureServerL(CPhoneBookServer::EServerConfigurationNone));
		CActiveScheduler::Stop();
		}
	} // CPhoneBookSyncStarter::RunL


/**
 *  Perform all server initialisation, in particular creation of the
 *  scheduler and server and then run the scheduler.
 */
static void RunServerL()
	{
	//
	// Naming the server thread after the server helps to debug panics.
	//
	User::LeaveIfError(User::RenameThread(PHBKSYNC_SERVER_NAME));

	//	
	// Create a new Active Scheduler...
	//
	CPhoneBookSyncScheduler*  scheduler = CPhoneBookSyncScheduler::NewL();
	CleanupStack::PushL(scheduler);	
	
	//
	// Create a new PhoneBookServer...
	//
	CPhoneBookServer*  server = CPhoneBookServer::NewL();
	CleanupStack::PushL(server);
	
	//
	// Initialisation complete, now signal the client thread...
	//
	LOGSERVER1(_L8("RunServerL(): Meeting Rendezvous..."));
	RProcess::Rendezvous(KErrNone);

	//
	// Run the server...
	//
	LOGSERVER1(_L8("RunServerL(): Starting server..."));
	CPhoneBookSyncScheduler::Start();
	
	CleanupStack::PopAndDestroy(2, scheduler);
	} // RunServerL


/**
 *  Server process entry-point.
 *
 *  @return  KErrNone or a standard Symbian error code.
 */
TInt E32Main()
	{
	__UHEAP_MARK;

	CTrapCleanup* cleanup=CTrapCleanup::New();
	TInt ret(KErrNone);
	if (cleanup)
		{
		TRAP(ret,RunServerL());
		delete cleanup;
		}
	else
		{
		ret = KErrNoMemory;
		}

	__UHEAP_MARKEND;
	return ret;
	} // E32Main