srsf/nssvascontacthdlr/src/nssvasccontacthandlerimpl.cpp
branchRCL_3
changeset 19 e36f3802f733
child 20 bf318c0ae8c0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srsf/nssvascontacthdlr/src/nssvasccontacthandlerimpl.cpp	Wed Sep 01 12:29:17 2010 +0100
@@ -0,0 +1,3445 @@
+/*
+* Copyright (c) 2005-2006 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:  Responsible for maintaining synchronization between the contact DB
+*               and the VAS DB
+*
+*/
+
+
+/** @todo Read the whole contact in one go.
+
+  Currently Contact handler usually opens the same contact (ReadContactL) two to 
+  three times in order to read its data (mostly often three times).
+  One is in GetContactNickname
+  Second in GetContactTitle
+  Third in DoAddNamesL when deciding what number field should the voice tag 
+        be trained for
+  If we could unite these reads, "reading contacts" phase will be 2-3 times faster.
+  It would make about 10-20% improvement for the training of some 300 contacts */
+
+// INCLUDE FILES
+#include "nssvasccontacthandlerimpl.h"
+#include "nssvasctrainingparameters.h"
+#include <nssvascvasdbmgr.h>
+#include <centralrepository.h>
+#include <f32file.h>
+#include <bautils.h>
+#include <barsc.h>
+#include <barsread.h>
+#include <sysutil.h>
+#include "srsfprivatecrkeys.h"
+
+// Publish & Subscribe of the contact handler activity
+#include <nssvascoreconstant.h>
+
+#include <vascvpbkhandler.h>
+#include <featmgr.h>
+#include "nssvasdatasyncwatcher.h"
+#include "nsschbackupobserver.h"
+#include <StringLoader.h> 
+#include <nssvascontacthandler.rsg>
+#include <data_caging_path_literals.hrh>
+#include "rubydebug.h"
+
+// CONSTANTS
+// Name of the context in the SIND DB. Voice tags for contacts are saved there
+_LIT( KNssCHNameDialContext, "NAMEDIAL" );
+
+// Languages which use lastname-firstname order
+const TLanguage KSwappedLanguages[] = 
+    {
+    ELangHungarian,       // 17
+    ELangTaiwanChinese,   // 29
+    ELangHongKongChinese, // 30
+    ELangJapanese,        // 32
+    ELangPrcChinese,      // 31
+    ELangKorean,          // 65
+    ELangVietnamese       // 96
+    };
+
+// Number of languages that use lastname-firstname order
+const TInt KNumberOfSwappedLanguages = sizeof( KSwappedLanguages ) / sizeof ( TLanguage );
+
+
+// Maximal number of retrials to save tags
+const TInt KVASCHMaxRetry = 2;
+
+// Size of the event 'pack'. Tags are added and deleted in groups.
+// KMaxNamesAtOnce is the maximal size of a group
+const TInt KMaxNamesAtOnce = 100;
+
+// Initial value of the time interval used to detect periodic action. In 
+// microseconds. If KEventMomentsSize events happened  within the 
+// KInitialPeriodicActionInterval, we assume, that periodic action has started
+const TInt KInitialPeriodicActionInterval = 15000000;
+
+// Time interval used to periodically check if there are pending 
+// events to process. In microseconds
+const TInt KPeriodicTimerInterval = 1000000;
+
+// If there are this many consecutive full resyncs initiated by errors during
+// handling normal changes (or during previous full resync), there is probably
+// something consistently wrong at the lower layers. 
+// Full resync trials (and the whole contact handler) should be disabled until
+// reboot after KMaxConsecutiveErrorFullResync consecutive unsuccessful trials
+const TInt KMaxConsecutiveErrorFullResync = 2;
+
+// List of phonebook fields, that are supported by contact handler.
+// I.e. fields, that can be dialed and voice tagged
+/** @todo Consider using information from phonebook about which fields contain numbers
+and using the following lists for priorities only */
+const TPbkFieldId KTaggableFields[] = 
+    { 
+    EPbkFieldIdPhoneNumberMobile, 
+    EPbkFieldIdPhoneNumberGeneral,
+    EPbkFieldIdPhoneNumberStandard, 
+    EPbkFieldIdPhoneNumberHome,
+    EPbkFieldIdPhoneNumberWork, 
+    EPbkFieldIdPhoneNumberVideo,
+    EPbkFieldIdVOIP,
+    EPbkFieldIdXsp
+    };
+const TInt KTaggableFieldsCount = sizeof( KTaggableFields ) / sizeof ( TPbkFieldId );
+
+// List of phonebook fields that are supported by video extension
+const TPbkFieldId KVideoExtensionFields[] = 
+    {
+    EPbkFieldIdPhoneNumberVideo,
+    EPbkFieldIdPhoneNumberMobile,
+    EPbkFieldIdPhoneNumberGeneral,
+    EPbkFieldIdPhoneNumberHome,
+    EPbkFieldIdPhoneNumberWork
+    };
+const TInt KVideoExtensionFieldsCount = sizeof( KVideoExtensionFields ) / sizeof ( TPbkFieldId );
+
+// List of phonebook fields that are supported by message extension
+const TPbkFieldId KMessageExtensionFields[] = 
+    {
+    EPbkFieldIdPhoneNumberMobile,
+    EPbkFieldIdPhoneNumberGeneral,
+    EPbkFieldIdPhoneNumberHome,
+    EPbkFieldIdPhoneNumberWork,
+    EPbkFieldIdPhoneNumberVideo
+    };
+const TInt KMessageExtensionFieldsCount = sizeof( KMessageExtensionFields ) / sizeof ( TPbkFieldId );   
+
+// List of phonebook fields that are supported by VOIP extension
+const TPbkFieldId KVOIPExtensionFields[] = 
+    {
+    EPbkFieldIdVOIP,
+    EPbkFieldIdXsp
+    };
+const TInt KVOIPExtensionFieldsCount = sizeof( KVOIPExtensionFields ) / sizeof ( TPbkFieldId ); 
+
+// Size of the client data per context. Is used to store the context language
+// In bytes
+const TInt KClientDataSize = sizeof(TNssContextClientData);
+
+
+// Number of retries to try to lock the same contact
+// 600 times should stand for 10 mins
+// -1 means trying forever
+const TInt KMaxContactLockRetrials = 600;
+
+// Interval between the ticks of the contact wait timer
+// in microseconds
+const TInt32 KContactWaitInterval = 1000000;
+
+// Bytes that should be free in the disk drive in addition to critical level 
+// before operation is started (100kB)
+const TInt KIncreaseOfDatabaseSizes = 100000;
+
+// Define this if name free-order is enabled
+//#define NSSVAS_CONTACTHANDLER_FREEORDER
+// LOCAL FUNCTION DECLARATIONS
+
+
+// ================= MEMBER FUNCTIONS =======================
+
+
+#ifdef _DEBUG
+_LIT( KFile, "VasCContactHandlerImpl.cpp" );
+#endif
+
+/**
+* Is used in DoHandleEventFailedL to *asynchronously* simulate successful tag
+*  deletion
+*/
+NONSHARABLE_CLASS( CSuccessfulDeletionSimulator ) : public CAsyncOneShot 
+	{
+	public:
+		CSuccessfulDeletionSimulator( CNssContactHandlerImplementation& aHost );
+		~CSuccessfulDeletionSimulator();
+	protected:
+	    /** Is called by the system after we asked it to Call() closer
+	    */
+		virtual void RunL();
+    private:
+        CNssContactHandlerImplementation& iHost;
+	};
+
+// -----------------------------------------------------------------------------
+// CSuccessfulDeletionSimulator::~CSuccessfulDeletionSimulator
+// Destructor
+// -----------------------------------------------------------------------------
+//	
+CSuccessfulDeletionSimulator::~CSuccessfulDeletionSimulator() 
+    {
+    RUBY_DEBUG0( "~CSuccessfulDeletionSimulator" );
+    if( IsAdded() ) 
+        {
+        RUBY_DEBUG0( "~CSuccessfulDeletionSimulator Request is still in progress, deque it" );
+        Deque();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CSuccessfulDeletionSimulator::CSuccessfulDeletionSimulator
+// Constructor
+// -----------------------------------------------------------------------------
+//
+CSuccessfulDeletionSimulator::CSuccessfulDeletionSimulator( CNssContactHandlerImplementation& aHost ) :
+	CAsyncOneShot( CActive::EPriorityStandard ),
+	iHost( aHost )
+	{
+	//nothing
+	}
+	
+// -----------------------------------------------------------------------------
+//  CAppCloser::RunL
+//  Will be called by the system and exits the application
+// -----------------------------------------------------------------------------
+//
+void CSuccessfulDeletionSimulator::RunL() 
+	{
+	RUBY_DEBUG0( "CSuccessfulDeletionSimulator::RunL Start" );
+	if( iStatus == KErrNone ) 
+		{
+		CArrayPtrFlat<MNssTag>* empty = new (ELeave) CArrayPtrFlat<MNssTag>( 1 );
+    	iHost.DoRemoveNamesAfterGetTagList( empty );
+    	// empty will be destroyed in the DoRemoveNamesAfterGetTagList
+		}
+	else 
+		{
+		RUBY_ERROR1( "CSuccessfulDeletionSimulator::RunL Unexpected iStatus [%d]", iStatus.Int() );
+		}
+	RUBY_DEBUG0( "CSuccessfulDeletionSimulator::RunL End" );
+	}
+	
+
+
+// -----------------------------------------------------------------------------
+// CNssContactHandlerImplementation::CNssContactHandlerImplementation
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CNssContactHandlerImplementation::CNssContactHandlerImplementation( )
+: iState( ECHInitializing ),
+  iSeparator( KNameSeparator()[0] ),
+  iEndOfPeriodicActionInterval( KInitialPeriodicActionInterval )
+    {
+    iClientData.iLanguage = ELangNone;
+    
+    // initialize last RestartTimer calls to be long ago in 
+    // the past (January 1st, 0 AD nominal Gregorian)
+    for( TInt i = 0; i < KEventMomentsSize; i++)
+    	{
+    	iEventMoments[i] = 0;
+    	}
+    }
+
+// -----------------------------------------------------------------------------
+// CNssContactHandlerImplementation::ConstructL
+// EPOC second phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::ConstructL()
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::ConstructL" );
+
+    User::LeaveIfError( iFSession.Connect() );
+
+    // Sets up TLS, must be done before FeatureManager is used.
+    FeatureManager::InitializeLibL();
+    iVoIPFeatureSupported = FeatureManager::FeatureSupported( KFeatureIdCommonVoip );
+    iVideoCallFeatureSupported = FeatureManager::FeatureSupported( KFeatureIdCsVideoTelephony );
+    // Frees the TLS! Must be done after FeatureManager is used.
+    FeatureManager::UnInitializeLib();
+
+    // Before enabling event, start monitoring the PC-suite state
+    iSyncBackupWaiter = new (ELeave) CActiveSchedulerWait;
+    
+    iDataSyncWatcher = CNssDataSyncWatcherFactory::ConstructDataSyncWatcherL( *this );
+    
+    iBackupObserver = CNssChBackupObserver::NewL( *this );
+    
+    iContactWaiter = new (ELeave) CActiveSchedulerWait;
+    
+    iDeletionSimulator = new (ELeave) CSuccessfulDeletionSimulator( *this );
+
+    EnableEventHandling();
+
+    iEventArray.Reset();    // Ready to be used
+
+    TInt errProp = iBusyProperty.Define( KSINDUID, ETrainingContactsState, RProperty::EInt );
+    if( ( errProp != KErrNone ) && ( errProp != KErrAlreadyExists ) ) 
+        {
+        RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to define the property. Error [%d]", 
+                    errProp);
+        User::Leave( errProp );
+        }
+        
+    // Publish contact handler as busy before the DB operations
+    errProp = iBusyProperty.Attach( KSINDUID, ETrainingContactsState );
+    if( errProp != KErrNone ) 
+        {
+        // KErrNotFound is also an error as we just defined the property
+        RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to attach to property. Error [%d]", 
+                    errProp);
+        User::Leave( errProp );
+        }
+        
+    errProp = iBusyProperty.Set( EContactsTraining );
+    if ( errProp != KErrNone )
+        {
+        RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]", 
+                     errProp );
+        User::Leave( errProp )  ;
+        }
+
+    
+    // central repository
+    iRepository = CRepository::NewL( KCRUidSRSFSettings );
+    iRepositoryObserver = CCenRepNotifyHandler::NewL( *this, *iRepository,
+                                                      CCenRepNotifyHandler::EIntKey,
+                                                      KSRSFFullResyncNeeded );
+    iRepositoryObserver->StartListeningL();
+    
+	//Get VAS DB manager
+	iVasDbManager = CNssVASDBMgr::NewL();
+	iVasDbManager->InitializeL();
+	RUBY_DEBUG0( "CNssContactHandlerImplementation::ConstructL iVasDbManager initialized" );
+
+	//Get Tag Manager and Context Manager interfaces
+	iTagManager = iVasDbManager->GetTagMgr();
+	iContextManager = iVasDbManager->GetContextMgr();
+
+    // Prepare training parameters
+    iTrainingParams = CNssTrainingParameters::NewL();
+    
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::ConstructL iTrainingParams prepared" );
+
+    // Set languages which have lastname-firstname order
+    for ( TInt iCounter = 0; iCounter < KNumberOfSwappedLanguages; iCounter++ )
+        {
+        User::LeaveIfError( iSwappedLanguages.Append( KSwappedLanguages[iCounter] ) );
+        }
+
+#ifdef __SINDE_TRAINING
+    // Main array for languages
+    RArray<RTrainingLanguageArray> languages;
+    
+    // Languages for names
+    RArray<TLanguage> nameLanguages;
+    User::LeaveIfError( nameLanguages.Append( User::Language() ) );
+    User::LeaveIfError( nameLanguages.Append( ELangOther ) );
+    User::LeaveIfError( languages.Append( nameLanguages ) );
+    
+    // Languages for extensions
+    RArray<TLanguage> extLanguages;
+    User::LeaveIfError( extLanguages.Append( User::Language() ) );
+    User::LeaveIfError( languages.Append( extLanguages ) );
+    
+    // Store languages to training parameters
+    iTrainingParams->SetSindeLanguages( languages );
+
+    // Close arrays, iTrainingParams has done a copy of these
+    extLanguages.Close();
+    nameLanguages.Close();
+    languages.Close();
+    
+#else
+    // Set the languages
+    RArray<TLanguage> *languages = new (ELeave) RArray<TLanguage>;
+    CleanupStack::PushL( languages );
+    // Always generate a pronunciation in UI language
+    User::LeaveIfError( languages->Append( User::Language() ) );
+    // give some extra languages, which can be selected in engine
+    User::LeaveIfError( languages->Append( ELangOther ) );
+    iTrainingParams->SetLanguages( languages );
+    CleanupStack::Pop( languages );
+#endif
+    
+    // The separator between the first name and the last name
+    iTrainingParams->SetSeparator( iSeparator );
+    
+#ifdef __SIND_EXTENSIONS
+
+    ReadExtensionsL();
+#endif // __SIND_EXTENSIONS    	
+
+	ConnectToPhonebookL();
+	
+    TLitC<9> name = KNssCHNameDialContext;
+
+    // Get context.
+
+    // (1) if we have synced earlier -> we have created a context, and saved the language
+    // (2) if we haven't synced      -> we need to create context
+    //
+    // In case (2), GetContext fails.
+    // We'll assume that GetContext is successful until it fails.
+    iHaveWeEverSynced = ETrue;
+
+    TInt err = iContextManager->GetContext( this, name );
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::ConstructL Requested GetContext. err [%d]", err);
+
+    if ( err != KErrNone )
+        {
+        // The same function is called, whether GetContext fails synchronously
+        // or asynchronously.
+        GetContextCompleted( NULL, KErrNotFound );
+        }
+   	iDeleteTagListArray = new (ELeave) CArrayPtrFlat<MNssTag>(50);
+   	
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NewL
+// Two-phased constructor.
+// ---------------------------------------------------------
+// 
+CNssContactHandlerImplementation* CNssContactHandlerImplementation::NewL( )
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::NewL" );
+    CNssContactHandlerImplementation* self = new (ELeave) CNssContactHandlerImplementation();
+
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+
+    return self;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::~CNssContactHandlerImplementation
+// Destructor
+// ---------------------------------------------------------
+// 
+CNssContactHandlerImplementation::~CNssContactHandlerImplementation()
+   {
+   RUBY_DEBUG0( "CNssContactHandlerImplementation::~CNssContactHandlerImplementation FIXED" );
+   
+   iFSession.Close();
+
+   iEventArray.Close();     // Close the RArray
+
+   delete iDataSyncWatcher;
+   delete iBackupObserver;
+   
+   if ( iSyncBackupWaiter != NULL )
+       {
+       if( iSyncBackupWaiter->IsStarted() ) 
+           {
+           iSyncBackupWaiter->AsyncStop();
+           }
+       }
+   delete iSyncBackupWaiter;
+   
+   if ( iContactWaiter != NULL )
+       {
+       if( iContactWaiter->IsStarted() ) 
+           {
+           iContactWaiter->AsyncStop();
+           }
+       }
+   
+
+   delete iContactWaiter;
+   delete iDeletionSimulator;
+   delete iPeriodicHandleEventTimer;
+
+   delete iContext;
+
+   delete iVasDbManager;
+   RProperty::Delete( KSINDUID, ETrainingContactsState );
+   iBusyProperty.Close();
+   
+   delete iRepositoryObserver;
+   delete iRepository;
+   
+   delete iTrainingParams;
+   iTagArray.ResetAndDestroy();
+
+   for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+       {
+       delete iContactQueue[k].iTitle;
+       }
+   iContactQueue.Close();
+
+   iDelList.Close();
+   iAddList.Close();
+   iRemoveIconList.Close();
+   iVoiceTagAddedList.Close();
+   iSwappedLanguages.Close();
+   
+   DisconnectFromPhonebook();
+   
+#ifdef __SIND_EXTENSIONS
+    
+    for ( TInt i(0); i < iExtensionList.Count(); i++ )
+       {
+       delete iExtensionList[i].iText;
+       }
+
+    iExtensionList.Close();
+#endif // __SIND_EXTENSIONS    
+   }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleEventL
+// Called by phonebook handler when a contact database event
+// occurs
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::HandleEventL( const TPhonebookEvent& aEvent )
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::HandleEventL" );
+    RUBY_DEBUG2( "CNssContactHandlerImplementation::HandleEventL(). Contact id [%d], event type [%d]", aEvent.iContactId, aEvent.iType );
+    
+    if ( aEvent.iType == EContactChanged ||
+         aEvent.iType == EContactDeleted ||
+         aEvent.iType == EContactAdded   ||
+         aEvent.iType == EUnknownChanges )
+        {
+        if( !CausedByVoiceTagAddition( aEvent ) )
+        	{
+        	// Check if operations can be finished
+        	// There should be enough disk space
+        	if ( NoDiskSpace() )
+        	    {
+        	    RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL not enough disk space, disabling event handling" );
+        	    DisableEventHandling();
+        	    }
+        	else
+        	    {
+                TInt errProp = iBusyProperty.Set( EContactsTraining );
+                if ( errProp != KErrNone )
+                    {
+                    RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]", 
+                                 errProp );
+                    User::Leave( errProp )  ;
+                    }
+        	    UpdatePeriodicActionVars();
+	            iEventArray.Append( aEvent );
+
+    	        if ( iState == ECHIdle )
+        	     	{
+            	    RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL() iState == ECHIdle => DoHandleEvent" );
+
+            	    // Process the first new event from the queue
+            	    TRAPD( err, DoHandleEventsL() );
+            	    RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleEventL. Assert would fail and Panic IF err != KErrNone. err [%d]", err );
+                    RUBY_ASSERT_DEBUG( err == KErrNone, User::Panic( KFile, __LINE__ ) );
+                
+                    if ( err != KErrNone )
+                        {
+                        RUBY_ERROR1( "CNssContactHandlerImplementation::HandleEventL. Error [%d] in DoHandleEventsL", err );
+                        }
+                    }
+         	    else 
+	        	    {
+	        	    RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleEventL() iState == [%d] => Do not handle the event right now", iState );
+	        	    } // if iState else
+        	    }
+            }  // if caused be voice tag addition
+	    else
+        	{
+			RUBY_DEBUG2( "CNssContactHandlerImplementation::HandleEventL(). Event for Contact id [%d] was caused by a voice icon addition and will be ignored, eventArray size [%d]", aEvent.iContactId, iEventArray.Count() );
+			if ( ( iVoiceTagAddedList.Count() == 0 ) && ( iState == ECHIdle ) && ( iEventArray.Count() == 0 ) )  
+			    {
+			    RUBY_DEBUG0( "HandleEventL No more voice icon events expected, nothing pending." );
+			    }
+			else 
+			    {
+			    RUBY_DEBUG1( "HandleEventL [%d] voice icon events are still expected", iVoiceTagAddedList.Count() );
+			    }
+        	}
+        }
+    else if ( aEvent.iType == EStoreRestoreBeginning )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL restore begin" );
+        iRestoreRunning = ETrue; // set to differentiate ending of restore or backup
+        }
+    else if ( aEvent.iType == EStoreBackupRestoreCompleted && iRestoreRunning )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL restore end" );
+        // Set value to make full resync
+        SetFullResyncCrFlagL( ETrue );
+        iRestoreRunning = EFalse;
+        } // if event type
+    }
+
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoHandleEventL
+// Process the Event from phonebook
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoHandleEventsL()
+	{
+	RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoHandleEventsL" );
+	RUBY_DEBUG1( "CNssContactHandlerImplementation::DoHandleEventsL, eventArray size [%d]", iEventArray.Count() );
+
+	// If there is no lengthy higher level routine executed
+	if( !PeriodicAction() ) 
+		{
+		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL !PeriodicAction()" );
+		if ( iHandlerEnabled )
+        	{
+   	    	if ( IsFullResyncNeeded() )
+       		   	{
+       			RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Full resync if needed. Setting iState to ECHFullResync" );
+				SetState( ECHFullResync );
+           		
+            	iEventArray.Close();
+	
+   	        	FullResyncStart();
+       	    	}
+   			else
+       	   		{
+       			RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Full resync if NOT needed. Setting iState to ECHHandlingNormalChanges" );
+       			SetState( ECHHandlingNormalChanges );
+
+       			// There are 3 phases in handling events:
+	          	// (1) Make two lists:
+   	     	 	//  * Names to be added
+       	  		//  * Names to be deleted
+          		CollectAddRemoveListsL();
+
+   	      		// (2) Delete the names to be deleted. This operation is asynchronous.
+       	  		DoRemoveNamesL();
+
+       			// (3) After removing names, add the names to be added.
+                // DoRemoveNamesL() starts the step (3)
+         		}
+    		}
+    	else 
+    		{
+    		// In UREL, empty "else" branch will be eliminated by the compiler
+			RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Handler was disabled" );
+    		}
+		
+   		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() completed" );
+		
+		}// if !PeriodicAction
+		
+	else 
+		{
+		// In UREL, empty "else" branch will be eliminated by the compiler
+		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL PeriodicAction detected, DoHandleEventsL omitted" );
+		}
+		
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DeleteTagCompleted
+// Called when the deletion of a tag is completed successfully
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DeleteTagCompleted( TInt aErrorCode )
+	{
+	if( aErrorCode == KErrNone )
+	    {
+    	RUBY_DEBUG1( "CNssContactHandlerImplementation::DeleteTagCompleted. Assert iState == ECHHandlingNormalChanges. iState [%d]", iState);
+        RUBY_ASSERT_DEBUG( iState == ECHHandlingNormalChanges, User::Panic( KFile, __LINE__ ) );	    
+	    }
+    else
+        {
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteTagCompleted: tag deletion failed" );
+    	
+    	iNormalChangesError = aErrorCode;
+        }	
+    
+    DoRemoveNamesAfterDeleteTagCompleted();
+	}
+
+// -----------------------------------------------------------------
+// CNssContactHandlerImplementation::GetTagCompleted
+// Called by tag manager when getting of a tag list completes
+// -----------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetTagListCompleted(
+    MNssTagListArray* aTagList, TInt aErrorCode )
+    {
+    if( aErrorCode == KErrNone )
+        {
+        if ( iState == ECHHandlingNormalChanges )
+            {
+            DoRemoveNamesAfterGetTagList( aTagList );
+            }
+        else
+            {
+            RUBY_DEBUG1( "CNssContactHandlerImplementation::GetTagListCompleted : Unknown state(%d)\n", iState );
+    		}
+        }
+    else
+        {
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::GetTagListCompleted. Error [%d]", aErrorCode );
+        
+        TRAPD( err, DoHandleEventFailedL() );    // Process the event fail
+        if ( err != KErrNone )
+            {
+            RUBY_ERROR1( "CNssContactHandlerImplementation::GetTagFailed. Error [%d] in DoHandleEventFailedL", err );
+            }
+        }
+	}
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DisableEventHandling
+// Disables the event handling. Is used in case of unrecoverable error
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DisableEventHandling()
+   {
+   RUBY_DEBUG0( "CNssContactHandlerImplementation::DisableEventHandling" );
+   iHandlerEnabled = EFalse;
+   }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::EnableEventHandling
+// Enables event handling
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::EnableEventHandling()
+   {
+   RUBY_DEBUG0( "CNssContactHandlerImplementation::EnableEventHandling" );
+   iHandlerEnabled = ETrue;
+   }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContextListCompleted
+// Called by tag manager when GetContextList() completes successfully
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetContextListCompleted(
+    MNssContextListArray*  /*aContextList*/, TInt /*aErrorCode*/ )
+    {
+    RUBY_ERROR0( "CNssContactHandlerImplementation::GetContextListCompleted - this function should never be called." );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::CheckErrorSigns
+// Compares client data (saved in VAS context)
+// to state of the phonebook to find out if
+// full synchronization is needed.
+//
+// If full resync is needed initiates it by saving into the event queue
+// special 'full resync event'
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::CheckErrorSigns()
+    {
+    
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::CheckErrorSigns" );
+    TBool errorFound( EFalse );
+    // Full resync, if this is the first time we launch.
+    //              if the UI language has changed.
+    //              if VAS Db and Contact Db time stamps do not match
+    if  ( iClientData.iLanguage == ELangNone ||
+        iClientData.iLanguage != User::Language() )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::CheckErrorSigns language changed." );
+        errorFound = ETrue;
+        }
+    else
+        {
+        // check that is full resyncronization needed
+        iRepository->Get( KSRSFFullResyncNeeded, errorFound );
+        }
+        
+    if ( errorFound )
+        {
+        TPhonebookEvent event;
+       	event.iType = ENullEvent;
+       	event.iContactId = 0;
+        
+        if ( iEventArray.Append( event ) != KErrNone )
+            {
+            RUBY_ERROR0( "CNssContactHandlerImplementation::CheckErrorSigns Failed to append to iEventArray" );
+            }        
+        }
+    
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContextCompleted
+// Called by tag manager, when GetContext() completes successfully
+//
+// Depending on the contact handler state, continues full resync sequence or
+// sets the idle state
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetContextCompleted(
+    MNssContext* aContext, TInt aErrorCode )
+	{
+	if( aErrorCode == KErrNone )
+	    {
+        RUBY_ASSERT_DEBUG( iState == ECHInitializing || iState == ECHFullResync,
+                        User::Panic( KFile, __LINE__ ) );
+
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted" );
+        iContext = aContext;
+        TRAPD(err,
+
+            iClientData.InternalizeL( iContext->ClientData() );
+
+    	    if ( iState == ECHInitializing )
+    	        {
+    	        // If there are warning sings of error,
+    	        // do full resynchronization.
+    	        CheckErrorSigns();
+
+    	        SetState( ECHIdle );
+    	        RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted. Setting iState to ECHIdle" );
+    		
+    	        if ( iEventArray.Count() ) // Verify whether more events in the queue
+    	            {
+    	            
+    	    		RUBY_DEBUG1( "CNssContactHandlerImplementation::GetContextCompletedhere are still [%d] events in the queue. Call DoHandleEvents for them", iEventArray.Count() );
+    				
+    	            DoHandleEventsL(); // Process the 1st new event from the queue
+    	            }
+    	        }
+    	    else if ( iState == ECHFullResync )
+    	        {
+    	        TRAPD( error,  FullResyncCreateChangesL() );
+    	        if( error != KErrNone ) 
+    	            {
+    	            RUBY_ERROR1( "FullResyncCreateChangesL failed with [%d]. Panic, nothing can be done", error );
+    	            RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+    	            }
+    	        }
+    	    else 
+    	    	{
+    	    	RUBY_ERROR0( "CNssContactHandlerImplementation::GetContextCompleted Unexpected state" );
+    	    	RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+    	    	}
+    	    );  // TRAPD
+    	if( err != KErrNone ) 
+    		{
+    		RUBY_ERROR1( "CNssContactHandlerImplementation::GetContextCompleted Leaves with [%d] inside", err );
+    		}
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted completed" );	    
+	    }
+	    
+	else // aErrorCode != KErrNone
+	    {
+        RUBY_ASSERT_DEBUG( iState == ECHInitializing || iState == ECHFullResync,
+                        User::Panic( KFile, __LINE__ ) );
+
+        if ( iState == ECHInitializing )
+            {
+            iHaveWeEverSynced = EFalse;
+            SetState ( ECHFullResync );
+            FullResyncStart();
+            }	    
+	    }
+
+	}
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SaveContextCompleted
+// Called when SaveContext() completes successfully
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SaveContextCompleted( TInt aErrorCode )
+    {
+    if( aErrorCode == KErrNone )
+        {
+        RUBY_ASSERT_DEBUG( iState == ECHInitializing || 
+                        iState == ECHFullResync,
+                        User::Panic( KFile, __LINE__ ) );
+
+        iHaveWeEverSynced = ETrue;
+
+        if ( iState == ECHInitializing )
+            {
+            // Retrive the Context to get the new ContextId from VasDB
+            iContextManager->GetContext( this, KNssCHNameDialContext );
+            }
+        else if ( iState == ECHFullResync )
+            {
+            TRAPD( error, FullResyncCreateChangesL() );
+            if ( error != KErrNone ) 
+                {
+                RUBY_ERROR1( "SaveContextCompleted Failed to create full resync changes. Critical error [%d]", 
+                            error );
+                DisableEventHandling();
+                }  // if error != KErrNone
+            }  // else if ( iState == ECHFullResync )
+        else 
+        	{
+        	// we should never be here, because the condition is checked 
+        	// by ASSERT_DEBUG at the start of the function
+        	RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted Unexpected state!" );
+        	}        
+        }
+        
+    else // aErrorCode != KErrNone
+        {
+        // Failed to initialize will collect the events forever and will never process them
+        // Will try to initialize again after reboot
+        RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted failed. Staying forever in ECHFullResync" );
+        RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted failed. Reboot to try initializing again" );
+        DisableEventHandling();        
+        }
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SaveTagCompleted
+// Called when SaveTag() completes
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SaveTagCompleted( TInt aErrorCode )
+    {
+    RUBY_ASSERT_DEBUG( iState == ECHHandlingNormalChanges, User::Panic( KFile, __LINE__ ) );
+    
+    DoAddNamesSaveTagCompleted( aErrorCode );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactTitleL
+// Populates aTitle with either "FirstName LastName" or "CompanyName".
+// "CompanyName" is given only if both First and Last names are missing.
+//---------------------------------------------------------
+//
+HBufC* CNssContactHandlerImplementation::GetContactTitleL( TContactItemId aContactId, 
+                                                           TBool aUseDefaultOrder,
+                                                           TBool& aBothFirstAndLastFound,
+                                                           TVasTagType& aTagType )
+	{
+	RUBY_DEBUG_BLOCKL( "CNssContactHandlerImplementation::GetContactTitleL" );
+    // Find out if lastname-firstname should be used
+    TBool useLastFirstOrder = EFalse;
+    
+    // By default, this function returns firstname and/or lastname
+    aTagType = ETagTypeName;
+
+    if ( aUseDefaultOrder )
+        {
+        useLastFirstOrder = SwapNameOrder();
+        }
+    else
+        {
+        useLastFirstOrder = !SwapNameOrder();
+        }
+    WaitUntilNoSyncBackupIsRunning();
+    ReadContactL( aContactId );
+    
+    HBufC* firstNameText = NULL;
+    HBufC* lastNameText = NULL;
+    HBufC* companyNameText = NULL;
+    
+    TInt firstLength( 0 );
+    TInt lastLength( 0 );
+    TInt companyLength( 0 );    
+    
+    TInt error = KErrNone;
+    
+    // name reading fields are used in Japanese
+	TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdFirstNameReading ) );
+    if ( error == KErrNotFound ) 
+        {
+        // not found
+        TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdFirstName ) );
+        }
+        
+    if ( error == KErrNone )
+        {
+        firstNameText = iPbkHandler->TextL().AllocLC();
+        TrimName( firstNameText );
+        firstLength = firstNameText->Length();
+        if ( firstLength == 0 )
+            {
+            CleanupStack::PopAndDestroy( firstNameText );
+            firstNameText = NULL;
+            }
+        }
+
+    TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdLastNameReading ) );
+    if ( error == KErrNotFound ) 
+        {
+        // not found
+        TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdLastName ) );
+        }
+        
+    if ( error == KErrNone )
+        {
+        lastNameText = iPbkHandler->TextL().AllocLC();
+        TrimName( lastNameText );
+        lastLength = lastNameText->Length();
+        if ( lastLength == 0 )
+            {
+            CleanupStack::PopAndDestroy( lastNameText );
+            lastNameText = NULL;
+            }        
+        }
+
+    TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdCompanyName ) );
+    
+    if ( error == KErrNone )
+        {
+        companyNameText = iPbkHandler->TextL().AllocLC();
+        TrimName( companyNameText );
+        companyLength = companyNameText->Length();
+        if ( companyLength == 0 )
+            {
+            CleanupStack::PopAndDestroy( companyNameText );
+            companyNameText = NULL;
+            }         
+        }
+
+    // Use third parameter to return a flag if both lastname and firstname are found
+    if ( ( firstLength > 0 ) && ( lastLength > 0 ) )
+        {
+        aBothFirstAndLastFound = ETrue;
+        }
+    else
+        {
+        aBothFirstAndLastFound = EFalse;
+        }
+
+    HBufC* title( NULL );
+
+#ifdef __SIND_EXTENSIONS
+    if ( firstLength + lastLength > 0 )
+        {
+        TInt spaceForIndexes( 0 );
+        // Find out how much more space is needed for "training indexes"
+        if ( aBothFirstAndLastFound )
+            {
+            spaceForIndexes = 2 * KTrainingIndexSize;
+            }
+        else
+            {
+            spaceForIndexes = KTrainingIndexSize;
+            }
+            
+        title = HBufC::NewL( firstLength + lastLength + spaceForIndexes );
+        
+        // Check the order 
+        if ( useLastFirstOrder )
+            {
+            // Lastname-firstname
+            if ( lastNameText ) 
+                {
+                AppendName( title, lastNameText );
+                }
+            
+            if ( firstNameText )
+                {
+                AppendName( title, firstNameText );
+                }
+            }
+        else
+            {
+            // Firstname-lastname
+            if ( firstNameText )
+                {
+                AppendName( title, firstNameText );
+                }
+                
+            if ( lastNameText ) 
+                {
+                AppendName( title, lastNameText );
+                }
+            }
+        }
+    // Try company name, if the personal name fails.
+    else if ( companyLength > 0 )
+        {
+        aTagType = ETagTypeCompanyName;
+        title = HBufC::NewL( companyLength + KTrainingIndexSize );
+        AppendName( title, companyNameText );
+        }
+#else
+    // Use personal name, if there is one.
+    if ( firstLength + lastLength > 0 )
+        {
+        title = HBufC::NewL( firstLength + 1 + lastLength );
+
+        // Check the order 
+        if ( useLastFirstOrder )
+            {
+            // Lastname-firstname
+            if ( lastNameText ) 
+                {
+                title->Des().Append( lastNameText->Des() );
+
+                if ( firstNameText )
+                    {
+                    // Separator marker
+                    title->Des().Append( iSeparator );
+                    }
+                }
+            
+            if ( firstNameText )
+                {
+                title->Des().Append( firstNameText->Des() );
+                }
+
+
+            }
+        else
+            {
+            // Firstname-lastname
+            if ( firstNameText ) 
+                {
+                title->Des().Append( firstNameText->Des() );
+
+                if ( lastNameText )
+                    {
+                    // Separator marker
+                    title->Des().Append( iSeparator );
+                    }
+                }
+                
+            if ( lastNameText )
+                {
+                title->Des().Append( lastNameText->Des() );
+                }
+
+            }
+        }
+    // Try company name, if the personal name fails.
+    else if ( companyLength > 0 )
+        {
+        aTagType = ETagTypeCompanyName;
+        title = HBufC::NewL( companyLength );
+        title->Des().Append( companyNameText->Des() );
+        }
+#endif // __SIND_EXTENSIONS
+    // It is possible to make a contact without a name.
+    else
+        {
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::ContactTitle: No name, ContactId=[%d]" , aContactId );
+        }
+
+    if ( companyNameText )
+        {
+        CleanupStack::PopAndDestroy( companyNameText );
+        }
+        
+    if ( lastNameText )
+        {
+        CleanupStack::PopAndDestroy( lastNameText );
+        }
+        
+    if ( firstNameText )
+        {
+        CleanupStack::PopAndDestroy( firstNameText );
+        }
+    
+    return title;
+	}
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SwapNameOrder
+// Returns ETrue if name order for current UI language should
+// be lastname-firstname, EFalse otherwise
+//---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::SwapNameOrder()
+    {
+    // Find UI language
+    TLanguage uiLanguage = User::Language();
+
+    if ( iSwappedLanguages.Find( uiLanguage ) == KErrNotFound )
+        {
+        return EFalse;
+        }
+    else
+        {
+        return ETrue;
+        }
+    }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactNicknameL
+// Returns nickname for a given contact
+//---------------------------------------------------------
+//
+HBufC* CNssContactHandlerImplementation::GetContactNicknameL( TContactItemId aContactId )
+	{
+	RUBY_DEBUG_BLOCKL( "CNssContactHandlerImplementation::GetContactNicknameL" );
+	WaitUntilNoSyncBackupIsRunning();
+    ReadContactL( aContactId );
+
+	// Get the nick name
+	iPbkHandler->FindFieldL( EPbkFieldIdSecondName );
+
+    // Get the length of the additional name.
+    TInt nickLength = iPbkHandler->TextL().Length();
+
+    HBufC* title = NULL;
+
+#ifdef __SIND_EXTENSIONS
+    // Use personal name, if there is one.
+    if ( nickLength > 0 )
+        {
+        title = HBufC::NewL( nickLength + KTrainingIndexSize );
+        
+        HBufC* nickName = iPbkHandler->TextL().AllocL();
+        TrimName( nickName );
+        AppendName( title, nickName );
+        delete nickName;
+        }
+#else
+    // Use personal name, if there is one.
+    if ( nickLength > 0 )
+        {
+        title = HBufC::NewL( nickLength );
+        title->Des().Append( iPbkHandler->TextL() );
+        }
+#endif // __SIND_EXTENSIONS
+
+    return title;
+	}
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoHandleEventFailed
+// Routine to process when handling the contact event failed
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoHandleEventFailedL()
+    {
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL start" );
+    
+    // Errors are normal if user deletes contact without a voice tag
+    // E.g. because this contact had no number
+    if( ( iDeleteTagListArray != NULL ) && ( iDeleteTagListArray->Count() > 0 ) ) 
+    	{
+    	// simulate successful deletion, just return no tags for further deletions
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL Deletion failed. Simulating successful empty deletion" );
+    	iDeletionSimulator->Call();
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL Called deletion simulator" );
+    	}
+    else 
+    	{
+    	
+	    // Try full resync couple of times. If still fails, stop contact handler. 
+	    // When the phone is booted for the next time, try resyncing again
+    	if( iConsecutiveErrorFullResync < KMaxConsecutiveErrorFullResync ) 
+    		{
+    		iConsecutiveErrorFullResync++;
+    		RUBY_DEBUG1( "CNssContactHandlerImplementation::DoHandleEventFailedL Trying full resync for the [%d] time", iConsecutiveErrorFullResync);
+    		// clean structures that might be already full
+    		iTagArray.ResetAndDestroy();
+
+		    for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+		    	{
+		       	delete iContactQueue[k].iTitle;
+		       	}
+		   	iContactQueue.Close();
+
+		   	iDelList.Close();
+		   	iAddList.Close();
+		   	iVoiceTagAddedList.Close();
+    		// generate full resync event
+    		TPhonebookEvent event;
+        	event.iType = ENullEvent;
+        	event.iContactId = 0;
+        	iEventArray.Insert( event, 0 );
+        	// will start full resync
+        	iTagArray.ResetAndDestroy();
+        	iSaveCallbackCounter = 0; // simulate completed action
+        	RUBY_DEBUG0( "DoHandleEventFailedL No leaving in the FullResync branch, retrying" );
+        	DoHandleEventsL(); // No leaving in the FullResync branch
+    		} 
+    	else
+	    	{
+	    	RUBY_ERROR1( "CNssContactHandlerImplementation::DoHandleEventFailedL Tried full resync for %d times. Disabling contact handler until reboot", KMaxConsecutiveErrorFullResync );
+    		// Tried several consecutive resyncs, didn't help
+    		// Disable itself until reboot
+    		DisableEventHandling();
+    		}  
+    	}  // if deletion else
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL end" );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::PhonebookOrder
+// TLinearOrder comparison function for sorting phonebook events
+// according to their Phonebook ID.
+//---------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::PhonebookOrder( const TPhonebookEvent& a1, const TPhonebookEvent& a2 )
+    {
+    if ( a1.iContactId != a2.iContactId )
+        {
+        return( a1.iContactId - a2.iContactId );
+        }
+
+    if ( a1.iTime < a2.iTime )
+        {
+        return( -1 );
+        }
+    else if ( a1.iTime > a2.iTime )
+        {
+        return( 1 );
+        }
+    else{
+        return( 0 );
+        }
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::IsFullResyncNeeded
+// Checks if the event queue contains an event, which
+// launches full resynchronization. If it does, there is
+// no point in processing the other events.
+// ---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::IsFullResyncNeeded()
+    {
+    for( TInt k( 0 ); k < iEventArray.Count(); k++ )
+        {
+        // Null event is used inside contact handler to signal full resync.
+        if ( iEventArray[k].iType == ENullEvent
+             || iEventArray[k].iType == EStoreBackupRestoreCompleted 
+             || iEventArray[k].iType == EUnknownChanges )
+            {
+            return ETrue;
+            }
+        }
+
+    return EFalse;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::CollectAddRemoveListsL
+// Given a list of Contact DB Events, this function compiles
+// a list of ids to-be-deleted and ids to-be-added.
+// A Contact DB Event contains:
+//  * Contact ID
+//  * Event type (added, modified, removed)
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::CollectAddRemoveListsL()
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::CollectAddRemoveListsL" );
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). iEventArray.Count [%d]", iEventArray.Count() );
+    TInt k( 0 );
+	
+	RUBY_ASSERT_DEBUG( iAddList.Count() == 0, User::Panic( KFile, __LINE__ ) );
+    RUBY_ASSERT_DEBUG( iDelList.Count() == 0, User::Panic( KFile, __LINE__ ) );
+
+    // We collect two lists:
+    //  * Contacts to be deleted.
+    //  * Contacts to be added.
+
+    TLinearOrder<TPhonebookEvent> order( PhonebookOrder );
+
+    // Sort events according to the phonebook ID. This way, the events
+    // modifying the same name are sequentially.
+    iEventArray.Sort( order );
+	
+
+    // If several events happen for the same contact, fuse them into one event.
+    for ( k = 0; k < iEventArray.Count() - 1; k++ )
+        {
+        if ( iEventArray[k].iContactId == iEventArray[k+1].iContactId )
+            {
+            // Sorting should have preserved the order of the events
+            RUBY_ASSERT_DEBUG(iEventArray[k+1].iTime >= iEventArray[k].iTime, User::Panic( KFile, __LINE__ ) );
+            RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). Fusing events for ContactId [%d]", iEventArray[k].iContactId );
+            iEventArray[k+1].iType = EContactChanged;
+            iEventArray.Remove( k );
+
+            // We have removed an element
+            k--;
+            }
+        }
+
+	RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). Events sorted and fused iEventArray.Count [%d]", iEventArray.Count() );
+
+    for( k = 0; k < iEventArray.Count() && k < KMaxNamesAtOnce; k++ )
+        {
+        TPhonebookEvent* event = &iEventArray[k];
+
+        switch ( event->iType )
+            {
+            case EContactChanged:
+                {
+                // events caused by saving voice tag field are skipped 
+                // earlier, in HandleDatabaseEventL
+
+                // We need to delete the old name
+                User::LeaveIfError( iDelList.Append( *event ) ); 
+                // and after that, add the new name.
+                User::LeaveIfError( iAddList.Append( *event ) ); 
+                break;
+                }
+            case EContactDeleted:
+                {
+                User::LeaveIfError( iDelList.Append( *event ) );
+                break;
+                }
+            case EContactAdded:
+                {
+                User::LeaveIfError( iAddList.Append( *event ) );
+                break;
+                }
+            default:
+                RUBY_ERROR1( "CNssContactHandlerImplementation::CollectAddRemoveListsL() Unkown event type: %d", event->iType );
+                break;
+            }
+        }
+
+    if ( k < KMaxNamesAtOnce )
+        {
+        iEventArray.Close();
+        }
+    else
+        {
+    	RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). k >= KMaxNamesAtOnce [%d]. Deleting first KMaxNamesAtOnce events, they've been sorted into corresponding lists already",
+    	            KMaxNamesAtOnce);
+        for ( k = 0; k < KMaxNamesAtOnce; k++ )
+            {
+            iEventArray.Remove(0);
+            }
+        }
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL() iEventArray.Count [%d]",
+                iEventArray.Count());
+	RUBY_DEBUG2( "CNssContactHandlerImplementation::CollectAddRemoveListsL iDelList.Count() [%d], iAddList.Count() [%d]", iDelList.Count(), iAddList.Count());
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesL
+// Starts name removal sequence
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesL()
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoRemoveNamesL" );
+    if ( iDelList.Count() == 0 )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL Nothing to delete - skipping" );
+
+        RemovingNamesCompletedL();
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL completed early <= nothing to remove" );
+        return;
+        }
+
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL - removing" );
+
+    // we have: a list of tag IDs, which should be removed
+    // we need: a list of tags, which should be removed.
+
+    // First, we get the tags which correspond to the tag ids.
+    // We need k async calls to VAS,
+    // where k is the number of tag ids in iDelList.
+    // We get a list of k' tags, where k' >= k.
+    // If some tag has both proper name and a nickname, then k' > k.
+    
+    iContactId = iDelList[0].iContactId;
+
+    if ( iDeleteTagListArray )
+        {
+        iDeleteTagListArray->ResetAndDestroy();
+        }
+    else
+        {
+        iDeleteTagListArray = new (ELeave) CArrayPtrFlat<MNssTag>(50);
+        }
+
+    TInt retCode = iTagManager->GetTagList( this, iContext, iContactId, 0 );
+
+    if ( retCode != KErrNone )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL failed to GetTagList" );
+        // In the case of change event, try to add names even though they are not found
+        // Might happen in scenario like this:
+        // 1. User adds a name without any phone number -> no voice tag is created
+        // 2. Contact is modified by adding a phone number
+        iDelList.Close();
+        DoAddNamesL();
+        }
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL completed" );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList
+// If contact database announces that n contacts need to be
+// deleted, we do n calls to GetTagList (the first is from DoRemoveNamesL). 
+// Each callback returns 1 or more tags (name + nickname = 2 tags). 
+// This function processes the callbacks by adding the tags to the 
+// to-be-deleted list.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList( MNssTagListArray* aTagList )
+    {
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList. iState [%d]", iState );
+
+    if ( iDelList.Count() == 0 ) 
+		{
+		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList iDelList.Count() == 0" );
+		}
+
+    // Sieve out "false" tags: Those which have a wrong contact ID.
+    /** Why? Maybe user was fast enough to delete these contacts from the phonebook 
+    *   Anyway, why should we keep "false" tags in our db?
+    */
+    for ( TInt i=0; i < aTagList->Count(); i++ )
+        {
+        CArrayFixFlat<TInt>* intArray = aTagList->At(i)->RRD()->IntArray();
+	
+        RUBY_ASSERT_DEBUG( intArray->Count() > 0, User::Panic( KFile, __LINE__ ) );
+
+        if ( intArray->At(0) != iContactId )
+            {
+            RUBY_ERROR2( "DoRemoveNamesAfterGetTagList expected contact id %d, but got contact id: %d", iContactId, intArray->At(0) );
+            delete aTagList->At( i );
+            aTagList->Delete( i );
+            }
+        }
+        
+    TRAPD( err, // trapping is a fix to make the function non-leaving
+
+	    // Add the tags to the to-be-deleted list.
+	    // (there are 1 or 2 tags in aTagList depending on
+	    //  whether nickname is also there or not)
+	    for ( TInt k( 0 ); k < aTagList->Count(); k++ )
+	        {
+	        iDeleteTagListArray->AppendL( (*aTagList)[k] );
+	        }
+
+	    // We have processed this ID
+	    RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Before removal. iDelList.Count [%d]", iDelList.Count() );
+	    if( iDelList.Count() > 0 ) 
+	    	{
+	    	iDelList.Remove( 0 );
+	    	} 
+	    else 
+	    	{
+	    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Empty iDelList!" );
+	    	}
+	    RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList After removal. iDelList.Count [%d]", iDelList.Count() );
+
+	    // If there are still more IDs, convert the next ID into a tag.
+	    if ( iDelList.Count() > 0 )
+	        {
+	        RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList There are [%d] contacts to delete. Calling GetTagList for them", iDelList.Count() );
+	        iContactId = iDelList[0].iContactId;
+
+	        TInt retCode 
+	            = iTagManager->GetTagList( this, iContext, iContactId, 0 );
+
+	        if ( retCode != KErrNone )
+	            {
+	            RUBY_DEBUG0( "DoRemoveNamesAfterGetTagList Failed to GetTagList, going to DoHandleEventFailedL" );
+	            TRAP_IGNORE( DoHandleEventFailedL() );      // to handle the process event failed
+	            /** @todo reengineer into single return path */
+	            // Destroy tag list
+	    		aTagList->Reset();
+	    		delete aTagList;
+	    		aTagList = 0;
+	            return;
+	            }
+	        }
+	        
+	    // If we just finished converting the last ID, move to the next phase.
+	    else{
+	        RUBY_DEBUG0( "DoRemoveNamesAfterGetTagList Moving to the next phase" );
+	        DoRemoveNamesCallDeleteTag();
+	        }
+        );  // TRAPD
+        if( err != KErrNone ) 
+        	{
+        	RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Leaves with [%d] inside", err );
+        	}
+		// Destroy tag list
+	    aTagList->Reset();
+	    delete aTagList;
+	    aTagList = 0;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag
+// Sends the DeleteTag() calls to lower layers when removing names.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag()
+    {
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag. iDeleteTagListArray->Count() [%d] Setting iDeleteCallbackCounter = 0", 
+                iDeleteTagListArray->Count() );
+    
+    if( iDeleteTagListArray->Count() > 0 ) 
+    	{
+    	
+	    iDeleteCallbackCounter = 0;
+
+	    for ( TInt k( 0 ); k < iDeleteTagListArray->Count(); k++ )
+	        {
+	        TInt ret = 
+	            iTagManager->DeleteTag( this, (*iDeleteTagListArray)[k] );
+
+	        if ( ret != KErrNone )
+	            {
+	            TRAPD( err, DoHandleEventFailedL(); );
+	            if( err != KErrNone ) 
+	            	{
+	            	RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag Error handling Left with [%d]", err );
+	            	}
+	            return;  /** @todo reengineer into single return path */
+	            }
+	        }
+    	} // if there are tags to delete
+    else
+    	{
+    	// if there were no tags to delete (attempt to delete non-existing tags)
+    	// simulating callback from successful deletion of.. 0 tags
+    	//iDeleteCallbackCounter = -1;
+    	//DeleteTagCompleted();
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag Here!" );
+    	}
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted
+// DeleteTag() callback calls this funciton. It counts
+// the number of callbacks, and when all callbacks have arrived,
+// goes to the next phase.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted()
+	{
+    iDeleteCallbackCounter++;
+    
+    RUBY_DEBUG2( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. iDeleteCallbackCounter [%d], Count() [%d]", iDeleteCallbackCounter, iDeleteTagListArray->Count() );
+    
+    if ( iDeleteCallbackCounter == iDeleteTagListArray->Count() )
+        {
+        if( iNormalChangesError == KErrNone ) 
+        	{
+        	TRAPD( err, RemovingNamesCompletedL() );
+    		if ( err != KErrNone )
+        		{
+        		RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. Error [%d] in RemovingNamesCompletedL", err );
+        		}
+        	}
+        else 
+        	{
+        	// There is not much we can do about the deletion error
+        	// Retrying deletion can cause endless cycle
+        	// Sometimes failed deletions are ok (if we tried to delete non-existing tag)
+        	/** @todo reengineer SpeechItemTrainer (and/or SRS plugin) to report deletion 
+        	    of non-existing tag as success. After all they are not in the DB */
+        	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted Deletion failed. Still simulating a successful one" );
+        	TRAPD( err, RemovingNamesCompletedL() );
+    		if ( err != KErrNone )
+        		{
+        		RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. Error [%d] in RemovingNamesCompletedL", err );
+        		}
+        	}  // if (iNormalChangesError == KErrNone) else
+        }  // if count
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::RemovingNamesCompletedL
+// Cleans up after some names have been removed
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::RemovingNamesCompletedL()
+    {
+	RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::RemovingNamesCompletedL" );
+    iDelList.Close();
+
+    if ( iDeleteTagListArray )
+        {
+        iDeleteTagListArray->ResetAndDestroy();
+        iDeleteCallbackCounter = 0;
+        }
+
+    DoAddNamesL();
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesL
+// Starts name addition sequence by fetching contacts data and ordering
+// voice tags training
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesL()
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoAddNamesL" );
+    TInt k( 0 );
+
+    if ( iAddList.Count() == 0 )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL Nothing to add. Setting iState = ECHIdle. Return" );
+    	SetState ( ECHIdle );
+    	// If there are events on the list, DoAddNamesCompleted will fire them
+        DoAddNamesCompletedL();  
+        }
+    else 
+        {
+        // Create new tags and train them.
+        for ( k = 0; k < iAddList.Count(); k++ )
+            {
+            // 3 names may be created from one contact:
+            //  * Main name (firstname + lastname XOR companyname)
+            //  * Reversed order name (usually lastname + firstname, depends on the UI language)
+            //      -> only if NSSVAS_CONTACTHANDLER_FREEORDER is defined
+            //  * Nickname
+
+            TContactData contactData;
+
+            contactData.iID = iAddList[k].iContactId;
+            contactData.iSyncTime.UniversalTime();
+
+            // Nickname
+            contactData.iTitle = NULL;
+            TRAPD( err, contactData.iTitle = GetContactNicknameL( iAddList[k].iContactId ) );
+
+            if ( contactData.iTitle != NULL )
+                {
+                CleanupStack::PushL( contactData.iTitle );
+                contactData.iType = ETagTypeNickName;
+                User::LeaveIfError( iContactQueue.Append( contactData ) );
+                CleanupStack::Pop( contactData.iTitle );
+                }
+
+            // Main name ( Using GetContactTitleL() )
+            contactData.iTitle = NULL;
+            TBool bothFound = EFalse;
+            TVasTagType tagType( ETagTypeUnknown );
+            TRAP( err, contactData.iTitle = GetContactTitleL( iAddList[k].iContactId, ETrue, bothFound, tagType ) );
+
+            // If all the name fields are empty, nothing
+            // can be done for the tag. We're finished.
+            if ( contactData.iTitle != NULL )
+                {
+                RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesL For Contact id [%d] title is: [%S]", iAddList[k].iContactId, contactData.iTitle );
+                CleanupStack::PushL( contactData.iTitle );
+                contactData.iType = tagType;
+                User::LeaveIfError( iContactQueue.Append( contactData ) );
+                CleanupStack::Pop( contactData.iTitle );
+                }
+
+    #ifdef NSSVAS_CONTACTHANDLER_FREEORDER
+            // Default order added previously, now adding
+            // secondary order if both first and lastnames are found
+            if ( bothFound )
+                {
+                contactData.iTitle = NULL;
+                TRAPD( err, contactData.iTitle = GetContactTitleL( iAddList[k].iContactId, EFalse, bothFound, tagType ) );
+                if ( contactData.iTitle != NULL )
+                    {
+                    CleanupStack::PushL( contactData.iTitle );
+                    contactData.iType = tagType;
+                    User::LeaveIfError( iContactQueue.Append( contactData ) );
+                    CleanupStack::Pop( contactData.iTitle );
+                    }
+                }
+    #endif // NSSVAS_CONTACTHANDLER_FREEORDER
+
+            }
+
+        // No tag had any text.
+        if ( iContactQueue.Count() == 0 )
+            {
+            RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL. No tag had any text, return" );
+            DoAddNamesCompletedL();
+            }
+        else 
+            {
+            // At least some tag has some text
+            RUBY_ASSERT_DEBUG( iTagArray.Count() == 0, User::Panic( KFile, __LINE__ ) );
+
+            for ( k = 0; k < iContactQueue.Count(); k++ )
+                {
+                // (1) Create a new tag
+                if ( iNewTag != NULL ) 
+                    {
+                    // If previous call to this function leaves, iNewTag can 
+                    // be not deleted
+                    delete iNewTag;
+                    iNewTag = NULL;
+                    }
+                iNewTag = iTagManager->CreateTagL( iContext );
+
+                MNssRRD* rrd = iNewTag->RRD();
+                MNssSpeechItem* speechItem = iNewTag->SpeechItem();
+
+                // (2) Fill RRD. RRD contains client-chosen data about the tag.
+                //     In case of name dialing, RRD contains the contact ID,
+                //     which is used later to get the phone number.
+                CArrayFixFlat<TInt> *id = new (ELeave) CArrayFixFlat<TInt>( 1 );
+
+                CleanupStack::PushL( id );
+
+                id->AppendL( iContactQueue[k].iID ); // ID
+                // select a number field for voice tag
+                // TInt fieldIndex( 0 );
+
+        		WaitUntilNoSyncBackupIsRunning();
+                TRAPD( err, ReadContactL( iContactQueue[k].iID ); );
+                if ( err == KErrNone )
+                    {
+                    // 1. Default voice call number set by the user
+                    TInt found = KErrNotFound;
+                    TRAP( found, iPbkHandler->FindDefaultContactFieldL( EDefaultCommand ) );
+                    
+                    if ( found == KErrNotFound )
+                        {
+                        for( TInt i = 0; i < KTaggableFieldsCount; i++) 
+                            {
+                            TRAP( found, iPbkHandler->FindFieldL( KTaggableFields[i] ) );
+                            if( found == KErrNone && ( !iPbkHandler->IsFieldEmptyL() ) )
+                                {
+                                break;  // found field
+                                }
+                            else 
+                                {
+                                // When field->IsEmptyOrAllSpaces(), it is the same for us as no field present
+                                found = KErrNotFound;
+                                }
+                            }  // for i < KTaggableFieldsCount
+                        }
+
+                    if ( found == KErrNone ) // found and is not empty
+                        {
+                        id->AppendL( iPbkHandler->FieldIdL() ); // field ID to array
+                        id->AppendL( EDial );
+                        id->AppendL( iContactQueue[k].iType );
+                        id->AppendL( EDefaultCommand );
+                        rrd->SetIntArrayL ( id );
+                                                
+                        // (3) Set the training text.
+                        speechItem = iNewTag->SpeechItem();
+                        speechItem->SetTextL( *iContactQueue[k].iTitle );
+                 
+                        User::LeaveIfError( iTagArray.Append( iNewTag ) );
+                        // Leave protection for iNewTag completed
+                        // Ownership and deletion responsibility moved
+                        // to iTagArray
+                        iNewTag = NULL;
+                        }
+                    else
+                        {
+                        // field not found or is empty
+                        RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesL Did NOT identify a suitable voice dial field for contact id [%d]", iContactQueue[k].iID);
+                        
+                        
+                        delete iNewTag;
+                        iNewTag = NULL;
+                        }  // if field else
+#ifdef __SIND_EXTENSIONS
+                    
+                    // this part creates separate voice tags for extensions
+                    for ( TInt i(0); i < iExtensionList.Count(); i++ )
+                        {
+                        found = KErrNotFound;
+                        TVasExtensionAction action = iExtensionList[i].iAction;
+                        TPbkFieldId fieldId = iExtensionList[i].iId;
+                        
+                        // default message number
+                        // default video
+                        // default e-mail
+                        // default VOIP  
+                        if ( action == ENewMessage ||
+                             iExtensionList[i].iId == EPbkFieldIdPhoneNumberVideo ||
+                             iExtensionList[i].iId == EPbkFieldIdEmailAddress ||
+                             iExtensionList[i].iId == EPbkFieldIdVOIP )
+                            {
+                            // @todo Should use DefaultMessageField or something similar!!
+                            TRAP( found,
+                                iPbkHandler->FindDefaultContactFieldL( iExtensionList[i].iCommand ) );
+                            }
+                            
+                        /*if ( extensionField == NULL )
+                            {
+                            extensionField = item->FindField( iExtensionList[i].iId );
+                            }
+                            
+                        if ( extensionField == NULL && field != NULL 
+                             && ( action == ENewMessage ) )
+                            {
+                            // if default neither default field or mobile field was not found
+                            // message use normal SIND field
+                            extensionField = field;
+                            fieldId = field->FieldInfo().FieldId();
+                            }*/
+                            
+                        // If field was not found, try to find it based on the priority lists
+                        if ( found == KErrNotFound )
+                            {
+                            found = FindExtensionField( action, iExtensionList[i].iId );
+                            // If field was found, check the field ID
+                            if ( found == KErrNone )
+                                {
+                                fieldId = iPbkHandler->FieldIdL();
+                                }
+                            }
+                            
+                        if ( found == KErrNone )
+                            {
+                            // create new tag
+                            iNewTag = iTagManager->CreateTagL( iContext );
+
+                            rrd = iNewTag->RRD();
+                            speechItem = iNewTag->SpeechItem();
+                            
+                            // set RRD data
+                            CArrayFixFlat<TInt> *extIds = new (ELeave) CArrayFixFlat<TInt>( 1 );
+                            CleanupStack::PushL( extIds );
+
+                            extIds->AppendL( iContactQueue[k].iID ); 
+                            if ( iExtensionList[i].iCommand == EMessageCommand ||
+                                  iExtensionList[i].iCommand == EVideoCommand )
+                                {
+                                TInt id = iPbkHandler->FieldIdL();
+                                if ( id )
+                                    {
+                                    extIds->AppendL( id );
+                                    }
+                                else
+                                    {
+                                    extIds->AppendL( fieldId );
+                                    }
+                                }
+                            else
+                                {
+                                extIds->AppendL( fieldId );
+                                }
+                            extIds->AppendL( action );
+                            extIds->AppendL( iContactQueue[k].iType );
+                            extIds->AppendL( iExtensionList[i].iCommand );
+
+                            rrd->SetIntArrayL ( extIds );
+                                                
+                            // set the training text.
+                            TPtr namePtr( iContactQueue[k].iTitle->Des() );
+                            TPtr extensionPtr( iExtensionList[i].iText->Des() );
+                            HBufC* tempTitle = HBufC::NewLC( namePtr.Length() + extensionPtr.Length() + KTrainingIndexSize );
+                            TPtr titlePtr( tempTitle->Des() );                                                           
+                            
+                            //not work: titlePtr.AppendFormat( extensionPtr, namePtr ); 
+                            
+                            titlePtr = extensionPtr;
+                            AppendExtensionMarker( titlePtr );
+                            TInt index = titlePtr.Find( KExtensionFormatString );
+                            if ( index >= 0 )
+                                {
+                                // replace %U with name
+                                titlePtr.Delete( index, 2 ); // delete %U
+                                titlePtr.Insert( index, namePtr ); 
+                                }
+                            speechItem->SetTextL( titlePtr );
+                        
+                            User::LeaveIfError( iTagArray.Append( iNewTag ) );
+                            iNewTag = NULL; //ownership changed
+                            CleanupStack::PopAndDestroy( tempTitle );  
+                            CleanupStack::PopAndDestroy( extIds );  
+                            }
+                        }
+
+#endif // __SIND_EXTENSIONS                       
+                    // delete id
+                    CleanupStack::PopAndDestroy( id );
+                    }
+                else 
+                    {
+                    // Opening contact failed. E.g. if it was just deleted by user	
+                    RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesL Failed to open contact id [%d]. Leave [%d]", iContactQueue[k].iID, err);
+                    CleanupStack::PopAndDestroy( id );
+                    delete iNewTag;
+                    iNewTag = NULL;
+                    /** @todo Try reengineering duplicated cleanups into single piece 
+                    of the code */
+                    }
+                }  // for k .. iContactQueue
+
+            if ( iTagArray.Count() == 0 )
+                {
+                // If there are events on the list, DoAddNamesCompleted will fire them
+                DoAddNamesCompletedL();  
+                RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL. Returned from DoAddNamesCompleted. Setting iState = ECHIdle" );
+        		SetState ( ECHIdle );
+                }
+            else 
+                {
+                RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesL Before training tags. TagCount = [%d]",
+        	            iTagArray.Count() );
+
+                iTrainCallbackCounter = 0;
+                for ( k = 0; k < iTagArray.Count(); k++ )
+                    {
+                    MNssSpeechItem* speechItem = iTagArray[k]->SpeechItem();
+
+                    MNssSpeechItem::TNssSpeechItemResult trainRet 
+                    = speechItem->TrainTextL( this, iTrainingParams );
+
+                    if ( trainRet != MNssSpeechItem::EVasErrorNone )
+                        {
+                        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL : TrainTextL call failed." );
+                        // Not much we can do except for timed retry.
+                        // This stops the contact handler.
+                        DoHandleEventFailedL();
+                        break;  // no processing anymore. Quit function
+                        }
+                    }  // for k < iTagArray.Count()
+                }  // if iTagArrayCount != 0
+            }
+        }  // if ( iAddList.Count() != 0 )
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesTrainingFinished
+// Called after training of a current pack of tags has finished. 
+// Starts saving tags.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesTrainingFinished()
+    {
+    iSaveCallbackCounter = 0;
+    iRetry = 0; // retry, if save tag fails
+    iNormalChangesError = 0; // to record failures in save tag
+
+    DoAddNamesSaveTag();
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesSaveTag
+// This function has two branches:
+// (1) Initiate saving tags
+// (1) After all tags have been saved, go to the next phase.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesSaveTag()
+    {
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag" );
+    TInt retCode;
+
+    // If all tags have been saved, go to the next phase.
+    if ( iSaveCallbackCounter == iTagArray.Count() )
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag All tags have been saved, go to the next phase" );
+        // Tags saved - go to the next phase
+        if ( iNormalChangesError == KErrNone )
+            {
+            // This function is called once per 100 contacts -> It is ok to use TRAP
+            TRAPD( error, DoAddNamesCompletedL() );
+            if( error != KErrNone ) 
+                {
+                /** @todo Test this branch */
+                // Some fatal error occured
+                RUBY_ERROR1( "DoAddNamesSaveTag DoAddNamesCompletedL failed with [%d]. Disabling SIND",
+                            error );
+                DisableEventHandling();
+                }
+            }
+        else
+            {
+            RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag Saving tags failed. Try again a few times" );
+            // Saving tags failed. Try again a few times, and if it still
+            // doesn't work, go to fatal error handler.
+            if ( iRetry < KVASCHMaxRetry )
+                {
+                iRetry++;
+                RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesSaveTag Retried saving [%d] times. [%d] retries left",
+                            iRetry, KVASCHMaxRetry - iRetry);
+                iSaveCallbackCounter = 0;
+                iNormalChangesError = KErrNone;
+                
+                DoAddNamesSaveTag();
+                }
+            else
+                {
+                RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTag Retried saving [%d] times. Still saving fails. Handling error", iRetry );
+                NormalChangesHandleError();
+                }
+            }
+        }  // if ( iSaveCallbackCounter == iTagArray.Count() )
+    
+    // If this is the first call to this function,
+    // make "save tag" calls. After that, wait for the callbacks.
+    else if ( iSaveCallbackCounter == 0 )
+        {
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTag iSaveCallbackCounter == 0. Run SaveTag for [%d] contacts",
+                    iTagArray.Count());
+        for ( TInt k( 0 ); k < iTagArray.Count(); k++ )
+            {
+            retCode = iTagManager->SaveTag( this, iTagArray[k] );
+
+            if ( retCode != KErrNone )
+                {
+                RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag - SaveTag failed." );
+				TRAPD( err, DoHandleEventFailedL(); );
+				if( err != KErrNone ) 
+					{
+					RUBY_ERROR1( "CNssContactHandlerImplementation::DoAddNamesSaveTag handling error Left with [%d]", err );
+					}
+                return;
+                }  // if retCode
+            }  // for iTagArray
+        }  // else if iSaveCallbackCounter == 0
+    else 
+    	{
+    	// iSaveCallbackCounter != 0, but different from iTagArray.Count()
+    	// This means, that SaveTag callbacks are still coming. We will wait for them
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag. iSaveCallbackCounter != 0, but different from iTagArray.Count()" );
+        }
+    } 
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted
+// Saving single tag has completed. 
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted( TInt aError )
+    {
+    iSaveCallbackCounter++;
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted iSaveCallbackCounter became [%d]",
+                iSaveCallbackCounter);
+	// |= to let single KErrGeneral fail any number of KErrNone
+    iNormalChangesError |= aError;
+
+    // Check if all tags have been saved.
+    DoAddNamesSaveTag();
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesCompleted
+// When adding names has successfully finished, clean up.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesCompletedL()
+    {
+    RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesCompleted. iDeleteCallbackCounter [%d], Count() [%d]", iDeleteCallbackCounter, iDeleteTagListArray->Count() );
+	TInt initialDeleteTagListCount = iDeleteTagListArray->Count();    
+    
+    iSaveCallbackCounter = 0;
+    iTrainCallbackCounter = 0;
+    
+    // DoAddNamesCompleted if often called after voice tags have been added =>
+    // Nothing has to be done at all
+    TBool changedDatabase = EFalse;  
+
+#ifndef __SIND_EXTENSIONS
+// Set VAS_SETS_VOICETAG on if old behaviour is needed.
+// That is, VAS sets on the voice tag field to Phonebook engine and Phonebook UI checks the contacts DB for voice tags.
+// Off means that VAS does not set voice tag field but instead Phonebook asks from VAS.
+#ifdef VAS_SETS_VOICETAG
+    // add voice tag fields
+    // iTagArray contains only successfully trained tags.
+    // Failed items are removed.
+    for ( TInt i( 0 ); i < iTagArray.Count(); i++ )
+        {
+        MNssRRD* rrd = iTagArray[i]->RRD();
+        CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+        RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+        if ( intArray->Count() >= 2 )
+            {
+            TBool committed( EFalse ); // ETrue if contact is committed already
+            TInt contactId( intArray->At( 0 ) );
+            RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted. Working on contact id [%d]",
+                        contactId);
+            TInt fieldId( intArray->At( 1 ) );
+
+            WaitUntilNoSyncBackupIsRunning();
+            // Open contact item based on id
+            TRAPD( errFor,
+                OpenContactL( contactId );
+                
+                RUBY_DEBUG1( "DoAddNamesCompleted Opened contact [%d]", contactId );
+    			TInt found == KErrNotFound;
+    			TRAP( found, iPbkHandler->FindDefaultContactFieldL( EDefaultCommand ) );
+                if ( found == KErrNotFound )
+                    {
+                    TRAP( found, iPbkHandler->FindFieldL( fieldId ) );
+                    }
+    					// field - field, that SHOULD have voice tag attached
+                        	
+                if ( found == KErrNone )
+                    {
+                    TRAP( error, iPbkHandler->ChangeVoiceTagFieldL( ETrue ) );
+                    if ( error == KErrNone )
+                        {
+                        RUBY_DEBUG1("DoAddNamesCompleted  Successfully added icon to the contact [%d]", 
+                                    contactId);
+                        iPbkHandler->CloseContactL( ETrue );
+                        RUBY_DEBUG1("DoAddNamesCompleted  Successfully committed contact [%d]", 
+                                    contactId);
+                        iVoiceTagAddedList.Append( contactId );
+                        changedDatabase = ETrue;
+                        committed = ETrue;
+                        }
+                    }
+                else
+                    {
+                    // field not found anymore
+                    RUBY_DEBUG2("DoAddNamesCompleted Field [%d] not found in (already deleted from?) contact [%d]",
+                                fieldId, contactId );
+                    iDeleteTagListArray->AppendL( NULL ); // One DeleteTag will generate one callback
+                    iTagManager->DeleteTag( this, iTagArray[i] );
+                    }
+                if ( !committed )
+                    {
+                    RUBY_DEBUG1("DoAddNamesCompleted Did not change contact [%d]. Closing without committing", 
+                       	            contactId);
+                    iPbkHandler->CloseContactL( EFalse );
+                    }
+            );  // TRAPD errFor
+            if ( errFor != KErrNone ) 
+                {
+                RUBY_DEBUG2("CNssContactHandlerImplementation::DoAddNamesCompleted Failed to add icon for contact [%d]. Error [%d]",
+                            contactId, errFor);
+                    // voice tag was not in the contacts database, delete it from VAS
+                    // One DeleteTag will generate one callback
+                iDeleteTagListArray->AppendL( NULL );
+                iTagManager->DeleteTag( this, iTagArray[i] );
+                }
+            }  // if intArray->Count() >= 2
+        }  // for  iTagArray
+#endif // VAS_SETS_VOICETAG
+#endif //__SIND_EXTENSIONS        
+
+    iTagArray.ResetAndDestroy();
+
+    iAddList.Close();
+    
+#ifndef __SIND_EXTENSIONS
+#ifdef VAS_SETS_VOICETAG
+    // Remove voice tag icons for contacts, that failed to be trained or saved
+    for( TInt l = 0 ; l < iRemoveIconList.Count(); l++ ) 
+        {
+        WaitUntilNoSyncBackupIsRunning();
+        TRAPD( error, OpenContactL( iRemoveIconList[l] ) );
+        if ( error != KErrNone ) 
+            {
+            RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to open contact [%d]. Error [%d]", iRemoveIconList[l], error);
+            }
+        else 
+            {           
+            TRAP( error, iPbkHandler->ChangeVoiceTagFieldL( EFalse ) );
+            if ( error == KErrNotFound )
+                {
+                // Nothing to remove. Already no voice tag icon
+                TRAP( error, iPbkHandler->CloseContactL( EFalse ) );
+                if ( error != KErrNone ) 
+                    {
+                    RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to close contact [%d]. Error [%d]", iRemoveIconList[l], error);
+                    }
+                }
+            else if ( error != KErrNone )
+                {
+                RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted  Failed to remove voice tag icon for contact [%d]. Error [%d]", iRemoveIconList[l], error);
+                TRAP( error, iPbkHandler->CloseContactL( EFalse ) );
+                if ( error != KErrNone ) 
+                    {
+                    RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to close contact [%d]. Error [%d]", iRemoveIconList[l], error);
+                    }
+                }
+            else
+                {
+                iVoiceTagAddedList.Append( iRemoveIconList[l] );
+                TRAP( error, iPbkHandler->CloseContactL( ETrue ) );
+                if ( error != KErrNone ) 
+                    {
+                    RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to commit contact [%d]. Error [%d]", iRemoveIconList[l], error);
+                    }
+                }            
+            
+            }  // if opened contact successfully
+        
+        }
+#endif // VAS_SETS_VOICETAG
+#endif //__SIND_EXTENSIONS
+        
+    iRemoveIconList.Close();
+
+    for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+        {
+        delete iContactQueue[k].iTitle;
+        iContactQueue[k].iTitle = NULL;
+        }
+    iContactQueue.Close();
+    
+    if( changedDatabase ) 
+    	{
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted changed DBx" );
+    	WaitUntilNoSyncBackupIsRunning();
+	    TRAPD(err, iPbkHandler->CompressL() );
+	    if ( err )
+    		{
+    		RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted Contacts DB compression failed. Error [%d]", 
+    		            err);
+    		}
+    	}
+    else 
+    	{
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted did NOT change DB" );
+    	}
+    	
+    	// full resyncs are always tag additions
+    if( initialDeleteTagListCount == iDeleteTagListArray->Count() ) 
+    	{
+    	SetState( ECHIdle );
+    	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted Syncing with phonebook generated NO deletions" );
+    	// then full resync will not be initiated
+    	iConsecutiveErrorFullResync = 0;
+	    if ( iEventArray.Count() > 0 )
+        	{
+    		RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted There are still [%d] events in the queue. Call DoHandleEvents for them",
+    		            iEventArray.Count() );
+        	TRAPD( err, DoHandleEventsL() );
+        	if ( err != KErrNone )
+	            {
+	            RUBY_ERROR1( "CNssContactHandlerImplementation::DoAddNamesCompleted. Error [%d] in DoHandleEventsL", err );
+            	}
+        	}
+    	else 
+       		{
+       		// No self-generated events, and no pending events to handle
+       		// This is the only place in the code, where everything is completed 
+       		// and nothing is pending
+       		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted All the updates completed. No DB event is pending" );
+       		
+       		}
+    	} 
+    else 
+    	{
+    	RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted Generated [%d] deletions. Starting to wait for callbacks", 
+    		iDeleteTagListArray->Count() - initialDeleteTagListCount);
+    	}
+       	
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted completed" );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NormalChangesHandleError
+// Handle errors which happend when updating normal changes
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::NormalChangesHandleError()
+    {
+    // We can't know, what went wrong.
+    // Stop contact handler, so that the next time the phone is booted
+    // full synchronization happens.
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::NormalChangesHandleError " );
+    TRAPD( err, DoHandleEventFailedL(); );
+    if( err != KErrNone ) 
+        {
+        RUBY_ERROR1( "CNssContactHandlerImplementation::NormalChangesHandleError DoHandleEventFailedL Left with [%d]", err );
+        }
+    }
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleTrainComplete
+// Saves the tag after successful training.
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::HandleTrainComplete( TInt aErrorCode )
+    {
+    if( aErrorCode == KErrNone )
+        {
+        RUBY_DEBUG1( "Training OK for %d:th name.", iTrainCallbackCounter );
+
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainComplete. iState [%d]", iState);
+        RUBY_ASSERT_DEBUG( iState == ECHInitializing || 
+                        iState == ECHHandlingNormalChanges,
+                        User::Panic( KFile, __LINE__ ) );
+
+        iTrainCallbackCounter++;
+
+        if ( iTrainCallbackCounter == iTagArray.Count() )
+            {
+            if ( iState != ECHInitializing )
+                {
+                // assert at the beginning of the function guarantees, 
+                // that iState == ECHHandlingNormalChanges 
+                DoAddNamesTrainingFinished();
+                }
+            }        
+        }
+    
+    else // aErrorCode != KErrNone
+        {
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainComplete. Training failed for %d:th name.\n",
+                    iTrainCallbackCounter );
+
+        RUBY_ASSERT_DEBUG( iState == ECHInitializing || 
+                        iState == ECHHandlingNormalChanges,
+                        User::Panic( KFile, __LINE__ ) );
+
+    	// Order removal of voice tag icon if present
+    	MNssRRD* rrd = iTagArray[iTrainCallbackCounter]->RRD();
+        CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+        RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+        if ( intArray->Count() >= 2 )
+        	{
+        	TInt error = iRemoveIconList.Append( intArray->At(0) );
+        	if( error != KErrNone ) 
+        		{
+        		RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError Failed to insert [%d] into iRemoveIconList. Error [%d]",
+        					intArray->At(0), error);
+        		}
+            }
+      	else
+            {
+            RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainComplete rrd->IntArray()->Count [%d] for iTrainCallbackCounter [%d]", 
+            			intArray->Count(), iTrainCallbackCounter);   	
+            }
+                
+
+        delete iTagArray[ iTrainCallbackCounter ];
+        iTagArray.Remove( iTrainCallbackCounter );
+
+        if ( iTrainCallbackCounter == iTagArray.Count() )
+            {
+            if ( iState == ECHHandlingNormalChanges )
+                {
+                DoAddNamesTrainingFinished();
+                }
+            else 
+            	{
+            	RUBY_ERROR1( "CNssContactHandlerImplementation::HandleTrainComplete Unexpected state [%d]", iState);
+            	}
+            }        
+        }
+    }
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleTrainError
+// Handles single tag training failure
+//---------------------------------------------------------
+//
+/*void CNssContactHandlerImplementation::HandleTrainError( enum MNssTrainTextEventHandler::TNssTrainResult )
+    {
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainError Training failed for %d:th name.\n",
+                iTrainCallbackCounter );
+
+    RUBY_ASSERT_DEBUG( iState == ECHInitializing || 
+                    iState == ECHHandlingNormalChanges,
+                    User::Panic( KFile, __LINE__ ) );
+
+	// Order removal of voice tag icon if present
+	MNssRRD* rrd = iTagArray[iTrainCallbackCounter]->RRD();
+    CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+    RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+    if ( intArray->Count() >= 2 )
+    	{
+    	TInt error = iRemoveIconList.Append( intArray->At(0) );
+    	if( error != KErrNone ) 
+    		{
+    		RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError Failed to insert [%d] into iRemoveIconList. Error [%d]",
+    					intArray->At(0), error);
+    		}
+        }
+  	else
+        {
+        RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError rrd->IntArray()->Count [%d] for iTrainCallbackCounter [%d]", 
+        			intArray->Count(), iTrainCallbackCounter);   	
+        }
+            
+
+    delete iTagArray[ iTrainCallbackCounter ];
+    iTagArray.Remove( iTrainCallbackCounter );
+
+    if ( iTrainCallbackCounter == iTagArray.Count() )
+        {
+        if ( iState == ECHHandlingNormalChanges )
+            {
+            DoAddNamesTrainingFinished();
+            }
+        else 
+        	{
+        	RUBY_ERROR1( "CNssContactHandlerImplementation::HandleTrainError Unexpected state [%d]", iState);
+        	}
+        }
+    }*/
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncStart
+// Starts a full synchronization
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncStart()
+    {
+    // If we have synchronized earlier, we must delete the old context.
+    if ( iHaveWeEverSynced )
+        {
+        iContextManager->DeleteContext( this, iContext );
+        }
+    else
+        {
+        TRAPD( err, FullResyncCreateContextL() );
+        if ( err != KErrNone )
+            {
+            RUBY_ERROR1( "CNssContactHandlerImplementation::FullResyncStart. Error [%d] in FullResyncCreateContextL", err );
+            }
+        }
+    }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncCreateContextL
+// Full resync: Create a new context
+// (prev: Destroy old context, next:populate the context with tags)
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncCreateContextL()
+    {
+    RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::FullResyncCreateContextL" );
+    if ( iContext != 0 )
+        {
+        delete iContext;
+        iContext = 0;
+        }
+
+    iContext = iContextManager->CreateContextL();
+    iContext->SetNameL( KNssCHNameDialContext );
+    iContext->SetGlobal( ETrue );
+
+    TBuf8<KClientDataSize> clientDataBuf;
+    iClientData.iLanguage = User::Language();
+    iClientData.iContactSyncId = KErrNotFound;
+    	
+    TRAPD( error, iClientData.ExternalizeL( clientDataBuf ) );
+    if ( !error )
+        {
+        iContext->SetClientData( clientDataBuf );
+        iContextManager->SaveContext( this, iContext );
+        }
+    }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncCreateChanges
+// Create tags and send training requests to VAS.
+// VAS trains them and calls back when it's ready
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncCreateChangesL()
+    {
+	RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::FullResyncCreateChangesL" );
+
+    WaitUntilNoSyncBackupIsRunning();
+
+    CContactIdArray* ids = iPbkHandler->ContactIdArrayLC();
+	
+	RUBY_DEBUG0( "CNssContactHandlerImplementation::FullResyncCreateChanges. iContactQueue.Count() == 0" );
+    RUBY_ASSERT_DEBUG( iContactQueue.Count() == 0, User::Panic( KFile, __LINE__ ) );
+    // Create change events for training
+    for ( TInt k( 0 ); k < ids->Count(); k++ )
+        {
+        TPhonebookEvent event;
+        event.iContactId = (*ids)[k];
+        event.iType = EContactAdded;
+        iEventArray.Append( event );
+        }
+
+    CleanupStack::PopAndDestroy( ids ); // Cleanup stack: empty
+
+    // go to processing events
+    SetState ( ECHIdle );
+    TRAPD( err, DoHandleEventsL() );
+    if ( err != KErrNone )
+        {
+        RUBY_ERROR1( "CNssContactHandlerImplementation::FullResyncCreateChanges. Error [%d] in DoHandleEventsL", err );
+        }
+    }
+
+// ------------------------------------------------------------    
+// CNssContactHandlerImplementation::DeleteContextCompleted
+// DeleteContext callback. Deleting a context is part of
+// full synchronization.
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DeleteContextCompleted( TInt aErrorCode )
+    {
+    if( aErrorCode == KErrNone )
+        {
+        // Go to the next phase of full synchronization
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::DeleteContextCompleted. iState == ECHFullResync, iState [%d]", iState );
+        RUBY_ASSERT_DEBUG( iState == ECHFullResync, User::Panic( KFile, __LINE__ ) );
+        TRAPD( err, FullResyncCreateContextL() );
+        if ( err != KErrNone )
+            {
+            RUBY_ERROR1( "CNssContactHandlerImplementation::DeleteContextCompleted. Error [%d] in FullResyncCreateContextL", err );
+            }        
+        }
+    else
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteContextCompleted failed" );
+        RUBY_ASSERT_DEBUG( iState == ECHFullResync, User::Panic( KFile, __LINE__ ) );
+
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteContextCompleted - can't handle, stopping." );
+    	TRAPD( err, DoHandleEventFailedL(); );
+    	if( err != KErrNone ) 
+    		{
+    		RUBY_ERROR1( "CNssContactHandlerImplementation::DeleteContextCompleted Left with [%d]", err);
+    		}        
+        }
+    }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::CausedByVoiceTagAddition
+// Check if the event was caused by voice tag field adding
+// ------------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::CausedByVoiceTagAddition( const TPhonebookEvent& aEvent )
+	{
+	TBool isVoiceTagAddedEvent (EFalse);
+	if( EContactChanged == aEvent.iType )
+		{
+		for ( TInt i = 0; i < iVoiceTagAddedList.Count(); i++ )
+        	{
+        	if ( aEvent.iContactId == iVoiceTagAddedList[i] )
+        		{
+            	isVoiceTagAddedEvent = ETrue;
+            	iVoiceTagAddedList.Remove( i-- );  // "i--" because of removal
+            	// Continue iterations. We are not sure if voice tag for the given 
+            	// contact could be changed twice
+            	}
+        	}  // for
+		}  // if event type
+	return isVoiceTagAddedEvent;
+	}
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::PeriodicCallback
+// Static callback required by Symbian Timer services.
+// ------------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::PeriodicCallback( TAny* pX )
+	{
+	((CNssContactHandlerImplementation*)pX)->DoPeriodicCallback();
+	return KErrNone;  // useless for CPeriodic
+	}
+    
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::DoPeriodicCallback
+// Periodically check if there are some events to process
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoPeriodicCallback()
+	{
+	RUBY_DEBUG0( "CNssContactHandlerImplementation::DoPeriodicCallback" );
+	// Check is needed to eliminate unnesessary TRAP and 64 bit time operations
+	// Note, that we are not checking for PeriodicAction(), but for iPeriodicHandleEventTimer == NULL
+	// PeriodicAction() is a method for detection of start of the action,
+	// while iPeriodicHandleEventTimer == NULL says if periodic action is being executed
+	// right *now*
+	
+	if ( ( iState == ECHIdle ) && ( iEventArray.Count() > 0 ) && ( !PeriodicAction() ) ) 
+		{
+		RUBY_DEBUG1( "CNssContactHandlerImplementation::DoPeriodicCallback. iState is ECHIdle, there are [%d] events to handle and no periodic action in progress", iEventArray.Count());
+		TRAPD( err, DoHandleEventsL());
+        if ( err != KErrNone )
+            {
+            RUBY_ERROR1( "CNssContactHandlerImplementation::DoPeriodicCallback. Error [%d] in DoHandleEventsL", err );
+            }
+        }  // if there are some events
+	else
+		{
+		RUBY_DEBUG0( "CNssContactHandlerImplementation::DoPeriodicCallback No events to handle"  );
+		}
+	}
+	
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::PeriodicAction
+// Detects periodic action.
+// Checks if many events have been received "lately"
+// ---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::PeriodicAction()
+    {
+    // index of the first recorded call
+    TInt checkedIndex = iEventMomentIndex + 1;
+    checkedIndex = checkedIndex < KEventMomentsSize ? checkedIndex : 0;
+    TTime& checkedTime = iEventMoments[checkedIndex];
+    
+    // Check if the first recorded call happened "lately" = within last 
+    // KPeriodicActionInterval seconds
+    TTime currentTime;
+    currentTime.UniversalTime();
+    TTimeIntervalMicroSeconds diff = currentTime.MicroSecondsFrom( checkedTime );
+    TBool result = diff < iEndOfPeriodicActionInterval;
+    if ( result == EFalse ) 
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction. Periodic action completed." );
+        RUBY_DEBUG2( "CNssContactHandlerImplementation::PeriodicAction. Periodic action completed. iEventMomentIndex [%d], checkedIndex [%d]", iEventMomentIndex, checkedIndex );
+        RUBY_DEBUG1( "currentTime [%d]", I64INT(currentTime.Int64()));
+        RUBY_DEBUG2( "CNssContactHandlerImplementation::PeriodicAction. diff [%d], iEndOfPeriodicActionInterval [%d]", I64INT(diff.Int64()), I64INT(iEndOfPeriodicActionInterval.Int64() ) );
+        
+        for( TInt i = 0; i < KEventMomentsSize; i++ ) 
+            {
+            RUBY_DEBUG2( "checkedIndex[%d] = [%d]", i, I64INT(iEventMoments[i].Int64() ) );
+            }
+        
+        // Periodic action completed. Or hasn't even been started
+        // Restore timeout values
+        // iEndOfPeriodicActionInterval = KInitialPeriodicActionInterval;
+        if ( iPeriodicHandleEventTimer != NULL ) 
+            {
+            // stop the timer, it was started, when periodic action was started
+            RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction Stopping the periodic action timer" );
+            iPeriodicHandleEventTimer->Cancel();
+            delete iPeriodicHandleEventTimer;
+            iPeriodicHandleEventTimer = NULL;
+            }
+        }
+    else 
+        {
+        // Periodic action detected
+        // start the timer if it is not running yet
+        if( iPeriodicHandleEventTimer == NULL ) 
+            {
+            TRAPD( err, iPeriodicHandleEventTimer = CPeriodic::NewL( EPriorityNormal ) );
+            if( err != KErrNone ) 
+                {
+                // This is a bad situation, but not the fatal one.
+                // If we cannot use timer to detect end of periodic action,
+                // Let's disable periodic action detection at all
+                RUBY_DEBUG1( "CNssContactHandlerImplementation::PeriodicAction. PeriodicTimer creation failed. Error [%d]. Disabling periodic actions", err);
+                result = EFalse;
+                }
+            else 
+                {  // Timer was created successfully
+                RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction Starting the periodic action timer" );
+                iPeriodicHandleEventTimer->Start( KPeriodicTimerInterval, KPeriodicTimerInterval, TCallBack( PeriodicCallback, this ) );
+                }
+            }
+        }
+    return result;
+    }
+	
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::UpdatePeriodicActionVars
+// Update variables, used to detect if periodic action is happening
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::UpdatePeriodicActionVars()
+    {
+    if ( ++iEventMomentIndex >= KEventMomentsSize )
+        {
+        iEventMomentIndex = 0;
+        }
+    iEventMoments[iEventMomentIndex].UniversalTime();
+    
+    // Update values used to detect end of periodic action
+    TTimeIntervalMicroSeconds32 currInterval = MaximalInterval( iEventMoments, KEventMomentsSize, iEventMomentIndex );
+    // 8 times. At the end of action, this would typically be no longer, than 0.5secs
+    // Note, that this is the interval fo KEventMomentsSize events
+    if ( ( currInterval.Int() << 3 ) >= ( KInitialPeriodicActionInterval ) ) 
+        {
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::UpdatePeriodicActionVars. Calculated interval is too big. Reverting periodic interval to the max [%d] mcs", KInitialPeriodicActionInterval);
+        iEndOfPeriodicActionInterval = KInitialPeriodicActionInterval;
+        }
+    else 
+        {
+        currInterval = currInterval.Int() << 3;  
+        RUBY_DEBUG1( "CNssContactHandlerImplementation::UpdatePeriodicActionVars. Adjusting periodic interval to [%d] mcs", currInterval.Int() );
+        iEndOfPeriodicActionInterval = currInterval.Int();
+        }
+    }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::MaximalInterval
+// Return maximal interval from the array of time moments
+// ------------------------------------------------------------
+//
+TTimeIntervalMicroSeconds32 CNssContactHandlerImplementation::MaximalInterval( const TTime aMoments[], const TInt aMomentsLength, const TInt aLastMomentIndex )
+    {
+    RUBY_ASSERT_DEBUG( aMomentsLength > 1, User::Panic( KFile, __LINE__ ) );
+    TInt currIndex( aLastMomentIndex );
+    TInt prevIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+    TTimeIntervalMicroSeconds32 result( 0 );
+    TTimeIntervalMicroSeconds currInterval;
+    
+    for ( TInt i = 0; i < aMomentsLength - 1; i++ ) 
+        {
+        currInterval = aMoments[currIndex].MicroSecondsFrom( aMoments[prevIndex] );
+        const TInt64& currIntVal = currInterval.Int64();
+        TBool tooBigValue = currIntVal > KMaxTInt;
+        
+        // if high != 0, aMoments were filled in the incorrect way
+        // but we should not bug the user about it
+        if ( tooBigValue ) 
+            {
+            // this cannot be a negative value.. if moments are recorded in the correct order.
+            // Then non-zero high means, that interval is too big to fit into
+            // TTimeIntervalMicroSeconds32
+            RUBY_DEBUG0( "CNssContactHandlerImplementation::MaximalInterval. Maximal interval is out of bounds" );
+            result = TTimeIntervalMicroSeconds32( KMaxTInt );
+            break;
+            }
+        else 
+            {
+            // normal size value
+            // casting is safe - we checked, that it fits into TInt
+            TTimeIntervalMicroSeconds32 currInterval32 ( (TInt)currIntVal );
+            
+            if ( currInterval32 > result ) 
+                {
+                result = currInterval32;
+                }
+            }
+        currIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+        prevIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+        }  // for
+    return result;
+    }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::SetState
+// Sets the contact handler state. 
+// In debug, panics (via asserts) if state change is not legal
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SetState( TContactHandlerState aState )
+	{
+    RUBY_DEBUG2( "CNssContactHandlerImplementation::SetState from [%d] to [%d]", iState, aState);
+    switch( aState ) 
+        {
+        case ECHFullResync:
+            // no pending events
+            RUBY_ASSERT_DEBUG( ( iDeleteTagListArray == NULL ) || ( iDeleteCallbackCounter == iDeleteTagListArray->Count() ), User::Panic( KFile, __LINE__ ) );
+            RUBY_ASSERT_DEBUG( iSaveCallbackCounter == iTagArray.Count(), User::Panic( KFile, __LINE__ ) );
+            
+            // full resyncronization needed again if this full resyncronization fails
+            RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+            break;
+        case ECHHandlingNormalChanges:
+            RUBY_ASSERT_DEBUG( iState == ECHIdle, User::Panic( KFile, __LINE__ ) );
+            RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+            break;
+        case ECHIdle:
+            if ( iDeleteTagListArray != NULL ) 
+                {
+                RUBY_ASSERT_DEBUG( (iSaveCallbackCounter == iTagArray.Count() ) || ( iDeleteCallbackCounter == iDeleteTagListArray->Count() ), User::Panic( KFile, __LINE__ ) );
+                }
+            else 
+                {
+                RUBY_ASSERT_DEBUG( ( iSaveCallbackCounter == iTagArray.Count() ) , User::Panic( KFile, __LINE__ ) );
+                }
+            if( iEventArray.Count() == 0 ) 
+                {
+                    // Idle state and no pending events mean, that contact handler
+                    // completed its operations for now
+                    TInt errProp = iBusyProperty.Set( EContactsNoTraining );
+                    if ( errProp != KErrNone )
+                        {
+                        RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]", 
+                                    errProp );
+                        // @todo Not a leaving function -> leave commented out
+                        //User::Leave( errProp )  ;
+                        }
+                }
+                
+            // all changes done, no need for resyncronization
+            RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( EFalse ) );
+            
+            break;
+        default:
+            RUBY_ERROR0( "CNssContactHandlerImplementation::SetState Unexpected or unknown state" );
+            RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+            
+            // failure, full resyncronization needed
+            RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+            break;
+        }
+    // actually switch state
+    iState = aState;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DataSyncStateChanged
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DataSyncStateChanged( TBool aRunning )
+    {
+    if( !aRunning && ( iSyncBackupWaiter->IsStarted() ) )
+        {
+        RUBY_DEBUG0( "DataSyncStateChanged Sync became inactive and CH was waiting for it" );
+        iSyncBackupWaiter->AsyncStop();
+        }
+    }
+    
+// ---------------------------------------------------------
+//  Is called when backup/restore status is changed and when backup/restore
+//  state observation was just started
+//  @param aRunning ETrue if some backup/restore action is in progress
+//                  EFalse otherwise
+//  @param aRestoreType ETrue if the event is restore related
+//                      EFalse if the event is backup related
+//  @see CNssChBackupObserver
+// ---------------------------------------------------------
+void CNssContactHandlerImplementation::BackupRestoreStateChanged( TBool aRestoreType, 
+                                                                  TBool aRunning )
+	{
+    RUBY_DEBUG2( "BackupRestoreStateChanged aRestoreType [%d], aRunning [%d]", 
+                  aRestoreType, aRunning );
+    
+    if( aRestoreType && aRunning )
+        {
+        RUBY_DEBUG0( "Setting the Full Resync CR flag" );
+        RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+        }
+    
+    if( !aRunning && ( iSyncBackupWaiter->IsStarted() ) )
+        {
+        RUBY_DEBUG0( "BackupRestoreStateChanged b/r became inactive and CH was waiting for it" );
+        iSyncBackupWaiter->AsyncStop();
+        }	
+	}
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleNotifyInt
+//
+// ---------------------------------------------------------
+// 	
+void CNssContactHandlerImplementation::HandleNotifyInt( TUint32 aId, TInt aNewValue )
+    {
+    RUBY_DEBUG0( "" );
+    
+    if ( aId == KSRSFFullResyncNeeded )
+        {
+        if ( aNewValue == KImmediateResync )
+            {
+            RUBY_DEBUG0( "Immediate full resync requested" );
+            
+            // Write the previous value back to the cenrep
+            TRAP_IGNORE( SetFullResyncCrFlagL( iResyncAtBoot ) );
+            
+            // Create an event that will launch full resync
+            TPhonebookEvent event;
+            event.iType = ENullEvent;
+            event.iContactId = 0;
+        
+            iEventArray.Append( event );
+            
+            // Start resync if nothing is ongoing
+            if ( iState == ECHIdle )
+                {
+                TRAP_IGNORE( DoHandleEventsL() );
+                }
+            }
+        }
+    }
+    
+// ---------------------------------------------------------
+// Sets the central repository full resync flag
+// ---------------------------------------------------------
+void CNssContactHandlerImplementation::SetFullResyncCrFlagL( TBool aResyncNeeded )
+    {
+    RUBY_DEBUG_BLOCKL("");
+    User::LeaveIfError( iRepository->Set( KSRSFFullResyncNeeded, aResyncNeeded ) );
+    iResyncAtBoot = aResyncNeeded;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::WaitUntilNoSyncBackupIsRunning
+//
+// ---------------------------------------------------------
+//    
+void CNssContactHandlerImplementation::WaitUntilNoSyncBackupIsRunning()
+    {
+    RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning start" );
+    RUBY_ASSERT_DEBUG( !iSyncBackupWaiter->IsStarted(), User::Panic( KFile, __LINE__ ) );
+    
+    // Disconnection from phonebook should happen only once and only if b/r is running
+    TBool disconnectedFromPb = EFalse;
+    
+    // Check the values until both data sync and backup/restore are not active
+    while( iDataSyncWatcher->InProgress() || iBackupObserver->InProgress() )
+    	{
+    	RUBY_DEBUG2( "WaitUntilNoSyncBackupIsRunning Have to wait. data sync [%d], backup/restore [%d]",
+    				 iDataSyncWatcher->InProgress(), iBackupObserver->InProgress() );
+    	if( !iSyncBackupWaiter->IsStarted() ) 
+            {
+            if( (!disconnectedFromPb) && iBackupObserver->InProgress() )
+            	{
+            	RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning Disconnecting from phonebook" );
+            	DisconnectFromPhonebook();
+            	disconnectedFromPb = ETrue;
+            	}
+            iSyncBackupWaiter->Start();
+            }
+        else 
+            {
+            RUBY_ERROR0( "WaitUntilNoSyncBackupIsRunning Attempt to start already active iSyncBackupWaiter. Huge error!" );
+            }
+    	}
+    	
+    if( disconnectedFromPb )
+    	{
+    	RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning Reconnecting to phonebook" );
+    	TRAP_IGNORE( ConnectToPhonebookL() );
+    	}
+            
+    RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning end" );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactL
+//
+// ---------------------------------------------------------
+//        
+void CNssContactHandlerImplementation::GetContactL( TContactItemId aContactId, TBool aReadContact ) 
+    {
+    RUBY_DEBUG1( "CNssContactHandlerImplementation::GetContactL. contact id [%d]", aContactId );
+    TInt err = KErrNone;
+    // Number of attempts tries
+    TInt attemptsTried( 0 );
+    do 
+        {
+        if( err == KErrInUse ) // i.e. not the first cycle iteration
+            {
+            RUBY_DEBUG2( "GetContactL Contact [%d] is locked. Waiting one more second. Retrial [%d]", 
+                         aContactId, attemptsTried);
+            // start periodic timer
+            if( attemptsTried == 1)   // i.e exactly the second cycle iteration
+                {
+                RUBY_DEBUG0( "GetContactL Creating periodic timer" );
+                iContactWaitTimer = CPeriodic::NewL( EPriorityNormal );
+                iContactWaitTimer->Start( KContactWaitInterval, KContactWaitInterval, TCallBack( ContactWaitCallback, this ) );
+                }
+            RUBY_DEBUG0( "GetContactL Starting the inner cycle waiting" );
+            iContactWaiter->Start( );
+            RUBY_DEBUG0( "GetContactL Waiting completed, trying to open contact again" );
+            }
+            
+        TRAP( err, iPbkHandler->FindContactL( aContactId, aReadContact ) );
+
+        attemptsTried++;
+        // -1 means forever
+        if( ( KMaxContactLockRetrials != -1 ) && ( attemptsTried >= KMaxContactLockRetrials ) ) 
+            {
+            RUBY_DEBUG1( "GetContactL Tried for [%d] times, still no success", attemptsTried );
+            break;
+            }
+        }
+    while( err == KErrInUse );
+    if( iContactWaitTimer != NULL ) 
+        {
+        iContactWaitTimer->Cancel();
+        delete iContactWaitTimer;
+        iContactWaitTimer = NULL;
+        }
+    // Propagate all the leaves, BUT KErrInUse
+    if( ( err != KErrInUse ) && ( err != KErrNone ) )
+        {
+        User::LeaveIfError( err );
+        }
+    if( err == KErrInUse ) 
+        {
+        RUBY_DEBUG1( "GetContactL Failed to lock contact [%d]. Disabling CH, will resync on reboot", aContactId );
+        DisableEventHandling();
+        User::Leave( KErrInUse );
+        }
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContactL completed" );
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ReadContactL
+//
+// ---------------------------------------------------------
+//     
+void CNssContactHandlerImplementation::ReadContactL( TContactItemId aContactId )
+    {
+    GetContactL( aContactId, ETrue );
+    }
+    
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::OpenContactL
+//
+// ---------------------------------------------------------
+//     
+void CNssContactHandlerImplementation::OpenContactL( TContactItemId aContactId )
+    {
+    GetContactL( aContactId, EFalse );
+    }
+    
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ContactWaitCallback
+//
+// ---------------------------------------------------------
+//     
+TInt CNssContactHandlerImplementation::ContactWaitCallback( TAny* pX )
+    {
+	((CNssContactHandlerImplementation*)pX)->DoContactWaitCallback();
+	return KErrNone;  // useless for CPeriodic    
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoContactWaitCallback
+//
+// ---------------------------------------------------------
+//             
+void CNssContactHandlerImplementation::DoContactWaitCallback()
+    {
+    iContactWaiter->AsyncStop();
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NoDiskSpace
+// Checks if there is not enough disk space to finish the
+// operation. Returns ETrue if there is no space.
+// ---------------------------------------------------------
+//    
+TBool CNssContactHandlerImplementation::NoDiskSpace()
+    {
+    RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace" );
+    TBool diskSpace( EFalse );
+    TRAP_IGNORE( diskSpace = SysUtil::FFSSpaceBelowCriticalLevelL( &iFSession, KIncreaseOfDatabaseSizes ) );
+    
+    if ( diskSpace )
+        {
+        RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace NOT enough disk space available" );
+        return ETrue;
+        }
+    else
+        {
+        RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace There is enough disk space" );
+        return EFalse;
+        }
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::TrimName
+// Substitutes separator characters in descriptor with
+// white spaces. Removes extra withspaces. 
+// ---------------------------------------------------------
+//    
+void CNssContactHandlerImplementation::TrimName( HBufC* aBuf )
+    {
+    TPtr ptr = aBuf->Des();
+   
+    for ( TInt findIndex = 0; findIndex < aBuf->Des().Length(); findIndex++ )
+        {
+        if ( ptr[findIndex] == iSeparator )
+            {
+            ptr[findIndex] = ' ';
+            }
+        }
+       
+    ptr.TrimAll();
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::AppendName
+// Appends name to given buffer
+// ---------------------------------------------------------
+//    
+void CNssContactHandlerImplementation::AppendName( HBufC* aBuf, HBufC* aName )
+    {
+    if ( aName->Length() != 0 )
+        {     
+        aBuf->Des().Append( iSeparator );
+        aBuf->Des().Append( KNameTrainingIndex );
+        aBuf->Des().Append( aName->Des() );
+        }
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::AppendExtensionMarker
+// Appends marker for the extensions
+// Parameters should be in format "%U text message"
+// In which case output is "%U_2text message"
+// ---------------------------------------------------------
+//    
+void CNssContactHandlerImplementation::AppendExtensionMarker( TDes& aDes )
+    {
+    if ( aDes.Length() > 0 )
+        {
+        TInt insertPosition( 0 );
+        if ( aDes.Find( KExtensionFormatString ) == 0 )
+            {
+            // Find the first space and insert marker to that position
+            TInt firstSpace( 0 );
+            firstSpace = aDes.Find( _L(" ") );
+            if ( firstSpace == 2 )         // Next character after KExtensionFormatString
+                {
+                aDes.Delete( firstSpace, 1 );
+                }
+            insertPosition = 2;
+            }
+        // Insert marker
+        aDes.Insert( insertPosition, KExtensionTrainingIndex );
+        aDes.Insert( insertPosition, KNameSeparator );
+        }
+    }
+
+/**
+* Establishes the connection to the contact engine and 
+* starts listening to it
+*/ 
+void CNssContactHandlerImplementation::ConnectToPhonebookL()
+	{
+	RUBY_ASSERT_DEBUG( (!iPbkHandler), User::Leave( KErrNotReady ) );
+	iPbkHandler = CVasVPbkHandler::NewL();
+	iPbkHandler->InitializeL();
+	iPbkHandler->CreateContactObserverL( this );
+	}
+
+/** 
+* Clears the connection to the contact engine
+*/
+void CNssContactHandlerImplementation::DisconnectFromPhonebook()
+	{
+	delete iPbkHandler;
+	iPbkHandler = NULL;
+	}
+    
+#ifdef __SIND_EXTENSIONS	
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ReadExtensionsL
+// Reads the extension resource file
+// ---------------------------------------------------------
+//           
+void CNssContactHandlerImplementation::ReadExtensionsL()
+    {
+    // using StringLoader should be used, but there is no 
+    // access to CCoeEnv
+    
+
+    // letters for drives in search order
+    const TBuf<1> KResourceDrivers = _L("z"); 
+    _LIT( KResourceFileName, "nssvascontacthandler.rsc" );
+    
+    // number of extension commands in the resource file
+    const TInt KExtensionCommandCount = 8;
+   
+    // load resources
+    RResourceFile resourceFile;
+    RFs fs;
+    CleanupClosePushL( fs );
+    TBool found( EFalse );
+    User::LeaveIfError( fs.Connect() );
+   
+        
+    TFileName name;
+    TInt i( 0 );
+    // use the first drive
+    name.Append( KResourceDrivers[i] );
+    name.Append(':');
+    name.Append( KDC_RESOURCE_FILES_DIR );
+    name.Append( KResourceFileName );
+
+    while ( !found && i < KResourceDrivers.Length() )
+        {
+        name[0] = KResourceDrivers[i++];
+
+        BaflUtils::NearestLanguageFile( fs, name );
+       
+        if ( BaflUtils::FileExists(fs, name) )
+            {
+            // open resource
+            resourceFile.OpenL( fs, name );
+            CleanupClosePushL( resourceFile );
+            resourceFile.ConfirmSignatureL();
+            found = ETrue;
+            }
+        }
+
+    if ( !found )
+        {
+        User::Leave( KErrNotFound );
+        }
+    
+    for ( TInt extensionIndex( 0 ); extensionIndex < KExtensionCommandCount; extensionIndex++ )
+        {
+        TExtension extension;
+        TResourceReader reader;
+        TBool extentionSupported( ETrue );
+        
+        switch ( extensionIndex )
+            {
+            
+            case 0:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_MOBILE ) );
+            extension.iId = EPbkFieldIdPhoneNumberMobile;     
+            extension.iAction = EDial;
+            extension.iCommand = EMobileCommand;
+            break;
+            
+            case 1:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_MESSAGE ) );
+            extension.iId = EPbkFieldIdPhoneNumberMobile;     
+            extension.iAction = ENewMessage;     
+            extension.iCommand = EMessageCommand;
+            break;
+            
+            case 2:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_GENERAL ) );
+            extension.iId = EPbkFieldIdPhoneNumberGeneral;     
+            extension.iAction = EDial;
+            extension.iCommand = EGeneralCommand;          
+            break;
+            
+            case 3:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_WORK ) );
+            extension.iId = EPbkFieldIdPhoneNumberWork;     
+            extension.iAction = EDial;
+            extension.iCommand = EWorkCommand; 
+            break;
+            
+            case 4:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_HOME ) );
+            extension.iId = EPbkFieldIdPhoneNumberHome;     
+            extension.iAction = EDial;
+            extension.iCommand = EHomeCommand;          
+            break;
+            
+            case 5:
+            if ( iVideoCallFeatureSupported )
+                {  
+                reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_VIDEO ) );
+                extension.iId = EPbkFieldIdPhoneNumberVideo;     
+                extension.iAction = EDial;
+                extension.iCommand = EVideoCommand;                
+                }
+            else
+                { 
+                extentionSupported = EFalse;
+                }
+            break; 
+                                                                       
+            case 6:
+            reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_EMAIL ) );
+            extension.iId =EPbkFieldIdEmailAddress ;     
+            extension.iAction = ENewEmail;  
+            extension.iCommand = EEmailCommand;
+            break;
+            
+            case 7:
+            if ( iVoIPFeatureSupported )
+                {                
+                reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_VOIP ) );
+                extension.iId = EPbkFieldIdVOIP;     
+                extension.iAction = EDial;
+                extension.iCommand = EVoipCommand; 
+                }
+            else
+                {
+                extentionSupported = EFalse;
+                }
+            break; 
+            
+            default:
+            User::Leave( KErrGeneral );  // !!! @todo: PANIC                               
+            }
+        
+        if ( extentionSupported )
+            {           
+            extension.iText = reader.ReadHBufCL();
+            CleanupStack::PushL( extension.iText );    
+            User::LeaveIfError( iExtensionList.Append( extension ) );
+            CleanupStack::Pop( extension.iText );
+            CleanupStack::PopAndDestroy(); // AllocReadLC
+            }
+        }
+
+    CleanupStack::PopAndDestroy( &resourceFile );    
+    CleanupStack::PopAndDestroy( &fs );
+
+    }
+	
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::FindExtensionField
+// Finds the extension field based on priority lists
+// ---------------------------------------------------------
+//        	
+TInt CNssContactHandlerImplementation::FindExtensionField( TVasExtensionAction aAction, 
+                                                           TPbkFieldId aDefaultFieldId )
+    {  
+    TInt found = KErrNotFound;
+    
+    switch ( aDefaultFieldId )
+        {
+        case EPbkFieldIdPhoneNumberVideo:
+            {
+            // Use defaults for video extension
+            found = ExtensionFieldFindLoop( KVideoExtensionFields, KVideoExtensionFieldsCount );
+            break;
+            }
+        
+        case EPbkFieldIdPhoneNumberMobile:
+            {
+            if ( aAction == ENewMessage )
+                {
+                // Use defaults for message extension
+                found = ExtensionFieldFindLoop( KMessageExtensionFields, KMessageExtensionFieldsCount );  
+                }
+            else
+                {
+                TRAP( found, iPbkHandler->FindFieldL( aDefaultFieldId ) );
+                }
+                
+            break;
+            }
+            
+        case EPbkFieldIdVOIP:
+            {
+            // Use defaults for VOIP extension
+            found = ExtensionFieldFindLoop( KVOIPExtensionFields, KVOIPExtensionFieldsCount );  
+                
+            break;
+            }
+        
+        default:
+            {
+            TRAP( found, iPbkHandler->FindFieldL( aDefaultFieldId ) );
+            break;
+            }
+        }
+    return found;
+    }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ExtensionFieldFindLoop
+// Finds the extension from the prioroty list arrays
+// ---------------------------------------------------------
+//        	
+TInt CNssContactHandlerImplementation::ExtensionFieldFindLoop( const TPbkFieldId* const aArray, 
+                                                               TInt aCount )
+    {
+    TInt found = KErrNotFound;
+       
+    for ( TInt iCounter = 0; iCounter < aCount; iCounter++ )
+        {
+        TRAP( found, iPbkHandler->FindFieldL( aArray[iCounter] ) );
+        if ( found == KErrNone )
+            {
+            break;
+            }
+        }
+    return found;
+    }
+
+#endif // __SIND_EXTENSIONS
+    	
+//  End of File