os/persistentdata/loggingservices/eventlogger/LogServ/src/LOGADD.CPP
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/loggingservices/eventlogger/LogServ/src/LOGADD.CPP	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,503 @@
     1.4 +// Copyright (c) 2004-2010 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +#include <s32file.h>
    1.20 +#include <logcntmodel.h>
    1.21 +#include <logengevents.h>
    1.22 +#include "LOGADD.H"
    1.23 +#include "logservpanic.h"
    1.24 +#include "LOGDUP.H"
    1.25 +#include "LOGQUERY.H"
    1.26 +#include "LogServRecentList.h"
    1.27 +#include "LogServDatabaseTransactionInterface.h"
    1.28 +#include "LogServResourceInterpreter.h"
    1.29 +#include "LogServCacheConfig.h"
    1.30 +#include "LogServDatabaseChangeInterface.h"
    1.31 +#include <centralrepository.h>
    1.32 +#include "LogServCacheStrings.h"
    1.33 +#include "LogServCacheTypes.h"
    1.34 +#include "LOGREPDEFS.H"
    1.35 +
    1.36 +const TInt KMinimumNumberOfDigitsToMatch = 3;
    1.37 +
    1.38 +////////////////////////////////////////////////////////////////////////////////////////////
    1.39 +// Local functions
    1.40 +
    1.41 +#ifdef SYSLIBS_TEST
    1.42 +
    1.43 +#pragma BullseyeCoverage off
    1.44 +
    1.45 +static void LogStore32IntL(RFs& aFs, const TDesC& aFilePath, TInt aVal)
    1.46 +	{
    1.47 +	RFile file;
    1.48 +	User::LeaveIfError(file.Replace(aFs, aFilePath, EFileWrite));
    1.49 +	TPtrC8 p((const TUint8*)&aVal, sizeof(aVal));
    1.50 +	TInt err = file.Write(p);
    1.51 +	if(err == KErrNone)
    1.52 +		{
    1.53 +		err = file.Flush();
    1.54 +		}
    1.55 +	file.Close();
    1.56 +	User::LeaveIfError(err);
    1.57 +	}
    1.58 +
    1.59 +static void LogStoreContactMatchCountAndNameFormatL(TInt aContactMatchCount, TLogContactNameFormat aContactNameFormat)
    1.60 +	{
    1.61 +	RFs fs;
    1.62 +	CleanupClosePushL(fs);
    1.63 +	User::LeaveIfError(fs.Connect());
    1.64 +
    1.65 +	_LIT(KTestDir, "c:\\test\\");
    1.66 +	TInt err = fs.MkDir(KTestDir);
    1.67 +	if(err != KErrNone && err != KErrAlreadyExists)
    1.68 +		{
    1.69 +		User::Leave(err);
    1.70 +		}
    1.71 +	
    1.72 +	_LIT(KLogengTestFileNameCount, "c:\\test\\test_logengconfig_count.ini");
    1.73 +	LogStore32IntL(fs, KLogengTestFileNameCount, aContactMatchCount);
    1.74 +	
    1.75 +	_LIT(KLogengTestFileNameFormat, "c:\\test\\test_logengconfig_format.ini");
    1.76 +	LogStore32IntL(fs, KLogengTestFileNameFormat, (TInt)aContactNameFormat);
    1.77 +	
    1.78 +	CleanupStack::PopAndDestroy(&fs);
    1.79 +	}
    1.80 +
    1.81 +#pragma BullseyeCoverage on
    1.82 +
    1.83 +#endif //SYSLIBS_TEST
    1.84 +
    1.85 +//This function reads logeng repository file and returns the integer value of the given key. 
    1.86 +static TInt LogGetRepositoryValueL(CRepository& aRepository, TInt aKey)
    1.87 +	{		
    1.88 +	TInt val = -1;		
    1.89 +	User::LeaveIfError(aRepository.Get(aKey, val));
    1.90 +	return val;
    1.91 +	}
    1.92 +
    1.93 +static void LogGetContactmatchCountAndNameFormatL(TInt& aContactMatchCount, TLogContactNameFormat& aContactNameFormat)
    1.94 +	{
    1.95 +	CRepository* repository = NULL;
    1.96 +	TRAPD(err, repository = CRepository::NewL(KUidLogengRepository));		
    1.97 +	if(err == KErrNone)
    1.98 +		{
    1.99 +		CleanupStack::PushL(repository);
   1.100 +		aContactMatchCount = LogGetRepositoryValueL(*repository, KContactMatchCountRepKey);
   1.101 +		aContactNameFormat = static_cast <TLogContactNameFormat> (LogGetRepositoryValueL(*repository, KContactNameFormatRepKey));
   1.102 +		CleanupStack::PopAndDestroy(repository);
   1.103 +		}
   1.104 +	else if(err == KErrCorrupt)
   1.105 +		{
   1.106 +		__ASSERT_DEBUG(!repository, User::Invariant());
   1.107 +		User::Leave(err);
   1.108 +		}
   1.109 +	else
   1.110 +		{
   1.111 +		__ASSERT_DEBUG(!repository, User::Invariant());
   1.112 +		aContactMatchCount = KLogContactMatchCount;
   1.113 +		aContactNameFormat = KLogContactNameFormat;
   1.114 +		}
   1.115 +	#ifdef SYSLIBS_TEST
   1.116 +	LogStoreContactMatchCountAndNameFormatL(aContactMatchCount, aContactNameFormat);
   1.117 +	#endif	
   1.118 +	}
   1.119 +
   1.120 +////////////////////////////////////////////////////////////////////////////////////////////
   1.121 +// CLogAddEvent class
   1.122 +
   1.123 +CLogAddEvent::CLogAddEvent(MLogServDatabaseTransactionInterface& aDatabase, TInt aPriority) :
   1.124 +	CLogActive(aPriority), 
   1.125 +	iDatabase(aDatabase)
   1.126 +	{
   1.127 +	}
   1.128 +
   1.129 +CLogAddEvent::~CLogAddEvent()
   1.130 +	{
   1.131 +	Cancel();
   1.132 +	
   1.133 +	CloseContactsPlugin();
   1.134 +
   1.135 +	delete iDuplicate;
   1.136 +	delete iDuplicateFilter;
   1.137 +	}
   1.138 +
   1.139 +CLogAddEvent* CLogAddEvent::NewL(MLogServDatabaseTransactionInterface& aDatabase, TInt aPriority)
   1.140 +	{
   1.141 +	CLogAddEvent* self = new(ELeave) CLogAddEvent(aDatabase, aPriority);
   1.142 +	CleanupStack::PushL(self);
   1.143 +	self->ConstructL();
   1.144 +	CleanupStack::Pop(self);
   1.145 +	return self;
   1.146 +	}
   1.147 +
   1.148 +void CLogAddEvent::ConstructL()
   1.149 +	{
   1.150 +	iDuplicate = CLogDuplicate::NewL(iDatabase, Priority());
   1.151 +	iDuplicateFilter = CLogFilter::NewL();
   1.152 +	::LogGetContactmatchCountAndNameFormatL(iContactMatchCount, iContactNameFormat);
   1.153 +	}
   1.154 +
   1.155 +//This method will open contacts database (if not opened yet), only if the value of 
   1.156 +//iContactMatchCount is not 0.
   1.157 +//Se how iContactMatchCount data member is initialised.
   1.158 +TBool CLogAddEvent::PerformContactMatchL()
   1.159 +	{
   1.160 +	if (iContactMatchCount <= 0 || iEvent->Contact() != KLogNullContactId)
   1.161 +		return EFalse;
   1.162 +	
   1.163 +	if (iContactPlugin)
   1.164 +		return ETrue;
   1.165 +	
   1.166 +	// Attempt to load plugin
   1.167 +	TRAPD( err, iContactPlugin=CLogCntModel::NewL());
   1.168 +
   1.169 +	// If plugin doesn't exist this is equivalent to matching being disabled so we don't leave
   1.170 +	// for KErrNotFound
   1.171 +	if(err==KEComErrNoInterfaceIdentified)
   1.172 +		{
   1.173 +		// Disable contacts matching so that we don't keep attempting to match
   1.174 +		iContactMatchCount = 0;
   1.175 +		// Plugin doesn't exist
   1.176 +		return EFalse;
   1.177 +		}
   1.178 +
   1.179 +	User::LeaveIfError(err);
   1.180 +	
   1.181 +	// Open the DB
   1.182 +	OpenContactsL();
   1.183 +	
   1.184 +	return ETrue;
   1.185 +	}
   1.186 +
   1.187 +void CLogAddEvent::StartL(CLogEvent& aEvent, const CLogServRecentList* aRecentList, TRequestStatus& aStatus, const RMessage2& aMessage)
   1.188 +	{
   1.189 +	__ASSERT_ALWAYS(!IsActive(), Panic(ELogAlreadyActive1));
   1.190 +
   1.191 +	LOGTEXT("CLogAddEvent::StartL()");
   1.192 +
   1.193 +	// Store event details which were obtained from the client side
   1.194 +	iEvent = &aEvent;
   1.195 +	iState = ELogAddEvent;
   1.196 +	
   1.197 +	if (!iDatabase.DTIIsAllowed(EWriteOp, aMessage, iEvent->EventType()))
   1.198 +		{
   1.199 +		User::Leave(KErrPermissionDenied);
   1.200 +		}
   1.201 +
   1.202 +	if (PerformContactMatchL())
   1.203 +		iState = ELogSetContactAndRemoteParty; // Go look for a matching contact
   1.204 +
   1.205 +	iRecentList = aRecentList;
   1.206 +	iEventAdded = EFalse;
   1.207 +
   1.208 +	// Setup the event's time (UTC)
   1.209 +	TTime time;
   1.210 +	time.UniversalTime();
   1.211 +	iEvent->SetTime(time);
   1.212 +
   1.213 +	// Save the observer's request status and set it to KRequestPending
   1.214 +	Queue(aStatus);
   1.215 +
   1.216 +	// Start this objects RunL chain
   1.217 +	TRequestStatus* status = &iStatus;
   1.218 +	User::RequestComplete(status, KErrNone);
   1.219 +	SetActive();
   1.220 +	}
   1.221 +	
   1.222 +void CLogAddEvent::SetEventContact()
   1.223 +    {
   1.224 +    // Start by converting the phone number text into a number
   1.225 +    // check we've got a long enough number to be worth checking
   1.226 +    if(iEvent->Number().Length() >= KMinimumNumberOfDigitsToMatch)
   1.227 +        {
   1.228 +        // now search for a contact by looking up the phone number
   1.229 +        TLogContactItemId contactId = KLogNullContactId;
   1.230 +        TRAPD(err, contactId = iContactPlugin->MatchPhoneNumberL(iEvent->Number(), iContactMatchCount));
   1.231 +        
   1.232 +        if(err == KErrNone)
   1.233 +            {
   1.234 +            // we have at least one match
   1.235 +            if(contactId != KLogNullContactId)
   1.236 +                {
   1.237 +                // we have a match so set the contact id
   1.238 +                iEvent->SetContact(contactId);
   1.239 +                }
   1.240 +            }
   1.241 +        }
   1.242 +    iEvent->SetFlags(KLogEventContactSearched);
   1.243 +    }
   1.244 +
   1.245 +void CLogAddEvent::SetRemoteParty()
   1.246 +    {
   1.247 +    // Get the contact id
   1.248 +    TLogContactItemId  contactId = iEvent->Contact();
   1.249 +    if((contactId != KLogNullContactId) && (iEvent->RemoteParty().Length() == 0))
   1.250 +        {
   1.251 +        // Look it up and get the remote party info
   1.252 +        // Setup buffer to contain concatenated result
   1.253 +        TBuf<128> buf;
   1.254 +        // Go get the info
   1.255 +        TRAPD(err, iContactPlugin->ReadContactNameL(contactId, buf, iContactNameFormat));
   1.256 +
   1.257 +        if(err == KErrNotFound)
   1.258 +            {
   1.259 +            // Couldn't find the contact with that id so set it to NULL
   1.260 +            iEvent->SetContact(KLogNullContactId);
   1.261 +            }
   1.262 +        else
   1.263 +            {
   1.264 +            // Found it so fill in remote party
   1.265 +            iEvent->SetRemoteParty(buf);
   1.266 +            }
   1.267 +        }
   1.268 +    }
   1.269 +
   1.270 +void CLogAddEvent::GetConfigL()
   1.271 +    {
   1.272 +    iConfig = iDatabase.DTICacheConfig().Config();
   1.273 +    if(iConfig.iMaxLogSize == 0)
   1.274 +      {
   1.275 +      LOGTEXT("CLogAddEvent::DoRunL() - logging disabled");
   1.276 +      User::Leave(KErrNotSupported);
   1.277 +      }
   1.278 +    }
   1.279 +
   1.280 +TLogTypeId CLogAddEvent::SetDescriptionL()
   1.281 +    {
   1.282 +    const TLogServCacheTypeEntry& entry = iDatabase.DTICacheTypes().FindByUid(iEvent->EventType());
   1.283 +    if(entry.iEventTypeId == KLogNullTypeId)
   1.284 +        {
   1.285 +        LOGTEXT("CLogAddEvent::DoRunL() - type not found");
   1.286 +        User::Leave(KErrNotFound);
   1.287 +        }
   1.288 +    if(!entry.iEventType->LoggingEnabled())
   1.289 +        {
   1.290 +        LOGTEXT("CLogAddEvent::DoRunL() - type not enabled");
   1.291 +        User::Leave(KErrNotSupported);
   1.292 +        }
   1.293 +    iEvent->SetDescription(entry.iEventType->Description());
   1.294 +    return entry.iEventTypeId;
   1.295 +    }
   1.296 +    
   1.297 +TBool CLogAddEvent::DetectDuplicateEventsL()
   1.298 +    {
   1.299 +    TBool rc = EFalse;
   1.300 +    if(iRecentList)
   1.301 +        {
   1.302 +        iRecentList->GetFilter(*iEvent, *iDuplicateFilter);
   1.303 +        rc = iDuplicate->StartL(iEvent->Id(), iRecentList->Id(), *iDuplicateFilter, iStatus);
   1.304 +        }
   1.305 +    return rc;
   1.306 +    }
   1.307 +
   1.308 +void CLogAddEvent::DoRunL()
   1.309 +	{
   1.310 +	LOGTEXT3("CLogAddEvent::DoRunL(%d), state = %d", iStatus.Int(), iState);
   1.311 +
   1.312 +	switch (iState)
   1.313 +		{
   1.314 +		case ELogSetContactAndRemoteParty:
   1.315 +			{
   1.316 +			SetEventContact();
   1.317 +			SetRemoteParty();
   1.318 +			TRequestStatus* status = &iStatus;
   1.319 +			User::RequestComplete(status, KErrNone);
   1.320 +			iState = ELogAddEvent;
   1.321 +			SetActive();
   1.322 +			break;
   1.323 +			}
   1.324 +		case ELogAddEvent:
   1.325 +			{
   1.326 +			GetConfigL();
   1.327 +			TLogTypeId typeId = SetDescriptionL();
   1.328 +			RLogEventDbTable tbl;
   1.329 +			tbl.OpenLC(iDatabase.DTIDatabase());
   1.330 +			::LogPurgeMainL(iDatabase, tbl, iConfig.iMaxLogSize, 1);
   1.331 +			DoAddEventL(tbl, typeId, iDatabase.DTICacheStrings().GetIdL(iEvent->Direction()), iDatabase.DTICacheStrings().GetIdL(iEvent->Status()));
   1.332 +            CleanupStack::PopAndDestroy();//tbl
   1.333 +			iEventAdded = ETrue;
   1.334 +			if(DetectDuplicateEventsL())
   1.335 +			    {
   1.336 +                iState = ELogPurgeRecent;
   1.337 +                SetActive();
   1.338 +                break;
   1.339 +			    }
   1.340 +			iState = ELogPurgeRecent;
   1.341 +			TRequestStatus* status = &iStatus;
   1.342 +			User::RequestComplete(status, KErrNone);
   1.343 +			SetActive();
   1.344 +			break;
   1.345 +			}
   1.346 +		case ELogPurgeRecent:
   1.347 +			{
   1.348 +			// Delete old recent events
   1.349 +			if (iRecentList)
   1.350 +				{
   1.351 +				RArray<TLogId> logIds;
   1.352 +				::LogGetRecentEventsLC(iDatabase, iRecentList->Id(), iConfig.iMaxRecentLogSize, logIds); 
   1.353 +				::LogPurgeRecentEventsL(iDatabase, logIds);
   1.354 +				CleanupStack::PopAndDestroy(&logIds);
   1.355 +				}
   1.356 +			}
   1.357 +			break;
   1.358 +		default:
   1.359 +			__ASSERT_DEBUG(ETrue, Panic(ELogBadState1));
   1.360 +            break;
   1.361 +		}
   1.362 +
   1.363 +	LOGTEXT("CLogAddEvent::DoRunL() - end");
   1.364 +	}
   1.365 +
   1.366 +void CLogAddEvent::DoCancel()
   1.367 +	{
   1.368 +	iDuplicate->Cancel();
   1.369 +	CLogActive::DoCancel();
   1.370 +	}
   1.371 +
   1.372 +void CLogAddEvent::DoAddEventL(RLogEventDbTable& aTbl, TLogTypeId aTypeId, TLogStringId aDirectionId, TLogStringId aStatusId)
   1.373 +	{
   1.374 +	LOGTEXT("CLogAddEvent::DoAddEventL()");
   1.375 +
   1.376 +	// Insert a new record
   1.377 +	aTbl.InsertL();
   1.378 +
   1.379 +	aTbl.SetColL(RLogEventDbTable::iTypeColNo, (TUint32)aTypeId);
   1.380 +
   1.381 +	if (iEvent->RemoteParty().Length() > 0)
   1.382 +		aTbl.SetColL(RLogEventDbTable::iRemotePartyColNo, iEvent->RemoteParty());
   1.383 +
   1.384 +	if (iEvent->Direction().Length() > 0)
   1.385 +		aTbl.SetColL(RLogEventDbTable::iDirectionColNo, (TUint32)aDirectionId);
   1.386 +
   1.387 +	aTbl.SetColL(RLogEventDbTable::iTimeColNo, iEvent->Time());
   1.388 +	aTbl.SetColL(RLogEventDbTable::iDurationTypeColNo, (TInt32)iEvent->DurationType());
   1.389 +
   1.390 +	if (iEvent->DurationType() != KLogNullDurationType)
   1.391 +		aTbl.SetColL(RLogEventDbTable::iDurationColNo, iEvent->Duration());
   1.392 +
   1.393 +	if (iEvent->Status().Length() > 0)
   1.394 +		aTbl.SetColL(RLogEventDbTable::iStatusColNo, (TUint32)aStatusId);
   1.395 +
   1.396 +	if (iEvent->Subject().Length() > 0)
   1.397 +		aTbl.SetColL(RLogEventDbTable::iSubjectColNo, iEvent->Subject());
   1.398 +
   1.399 +	if (iEvent->Number().Length() > 0)
   1.400 +		aTbl.SetColL(RLogEventDbTable::iNumberColNo, iEvent->Number());
   1.401 +
   1.402 +	if (iEvent->Contact() != KLogNullContactId)
   1.403 +		aTbl.SetColL(RLogEventDbTable::iContactColNo, iEvent->Contact());
   1.404 +
   1.405 +	if (iEvent->Link() != KLogNullLink)
   1.406 +		aTbl.SetColL(RLogEventDbTable::iLinkColNo, iEvent->Link());
   1.407 +
   1.408 +	if (iEvent->Data().Length() > 0)
   1.409 +		aTbl.SetColL(RLogEventDbTable::iDataColNo, iEvent->Data());
   1.410 +
   1.411 +	// Set the flags
   1.412 +	TInt bit = KLogFlagsCount;
   1.413 +	while(bit--)
   1.414 +		{
   1.415 +		aTbl.SetColL(RLogEventDbTable::iFlagColNo[bit], (TUint32)((iEvent->Flags() & 0x1 << bit) ? 1 : 0));
   1.416 +		}
   1.417 +
   1.418 +	if (iRecentList)
   1.419 +		{
   1.420 +		__ASSERT_DEBUG(iRecentList->Id() != KLogNullRecentList, Panic(ELogNullRecentList));
   1.421 +		aTbl.SetColL(RLogEventDbTable::iRecentColNo, (TInt32)iRecentList->Id());
   1.422 +		}
   1.423 +
   1.424 +#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM	
   1.425 +	if(iEvent->SimId() != KLogNullSimId)
   1.426 +		{
   1.427 +		aTbl.SetColL(RLogEventDbTable::iSimIdColNo, iEvent->SimId());
   1.428 +		}
   1.429 +	else
   1.430 +		{
   1.431 +		aTbl.SetColNullL(RLogEventDbTable::iSimIdColNo);
   1.432 +		}
   1.433 +#endif
   1.434 +	
   1.435 +	// Assign event id and end the rowset operation
   1.436 +	const TLogId newId = aTbl.ColInt32(RLogEventDbTable::iIdColNo);
   1.437 +	iEvent->SetId(newId);
   1.438 +	aTbl.PutL();
   1.439 +
   1.440 +	// Tell change interface about the addition
   1.441 +	iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventAdded, newId);
   1.442 +
   1.443 +	LOGTEXT("CLogAddEvent::DoAddEventL() - end");
   1.444 +	}
   1.445 +
   1.446 +void CLogAddEvent::DoComplete(TInt& aStatus)
   1.447 +	{
   1.448 +	LOGTEXT2("CLogAddEvent::DoComplete(%d)", aStatus);
   1.449 +
   1.450 +	if	(iDatabase.DTIInTransaction())
   1.451 +		{
   1.452 +		LOGTEXT2("CLogAddEvent::DoComplete() - in transaction: %d", aStatus);
   1.453 +		if (aStatus == KErrNone)
   1.454 +			aStatus = iDatabase.DTICommitAndEnd();
   1.455 +
   1.456 +		LOGTEXT2("CLogAddEvent::DoComplete() - checking for need to rollback: %d", aStatus);
   1.457 +		if (aStatus < KErrNone)
   1.458 +			iDatabase.DTIRollBack();
   1.459 +		}
   1.460 +	else
   1.461 +		{
   1.462 +		if	(iEventAdded)
   1.463 +			aStatus = KErrNone;
   1.464 +		}
   1.465 +
   1.466 +	LOGTEXT2("CLogAddEvent::DoComplete() - final status value is: %d", aStatus);
   1.467 +	}
   1.468 +
   1.469 +//Opens the default contacts database.
   1.470 +void CLogAddEvent::OpenContactsL()
   1.471 +	{
   1.472 +	//Sometimes, after a sequence of OpenContactsL()/CloseContacts() calls the Contacts server crashes
   1.473 +	//and OpenContactsL() leaves with error -15. In order to avoid that the following delay has been added.
   1.474 +	//(something related to Contacts server state machine)
   1.475 +    User::After(1000);
   1.476 +	// Attempt to open DB
   1.477 +	TRAPD(err,iContactPlugin->OpenContactsL());
   1.478 +	if(KErrNone!=err)
   1.479 +		{
   1.480 +		if(err == KErrServerTerminated)
   1.481 +		    {
   1.482 +		    RDebug::Print(_L("+++LogEng, LogAdd.cpp, Contacts server crashed!\r\n"));
   1.483 +		    }
   1.484 +		// If DB doesn't open delete plugin
   1.485 +		delete iContactPlugin;
   1.486 +		iContactPlugin = NULL;
   1.487 +		User::Leave(err);
   1.488 +		}
   1.489 +	}
   1.490 +
   1.491 +//Closes the default contacts database and deletes the plugin.
   1.492 +void CLogAddEvent::CloseContactsPlugin()
   1.493 +	{
   1.494 +	if(iContactPlugin)
   1.495 +		{
   1.496 +		iContactPlugin->CloseContacts();
   1.497 +		delete iContactPlugin;
   1.498 +		iContactPlugin = NULL;
   1.499 +		//REComSession::FinalClose() call moved here from logcntmodel.cpp.
   1.500 +		//The in-source documentation of REComSession::FinalClose() notes that: 
   1.501 +		//"It must never be called from within a plug-in implementations class 
   1.502 +		//destructor, especially following a DestroyImplementation() ". 
   1.503 +		//That was the case before the function call was moved here.
   1.504 +	    REComSession::FinalClose();
   1.505 +		}
   1.506 +	}