// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//
#include <s32file.h>
#include <logcntmodel.h>
#include <logengevents.h>
#include "LOGADD.H"
#include "logservpanic.h"
#include "LOGDUP.H"
#include "LOGQUERY.H"
#include "LogServRecentList.h"
#include "LogServDatabaseTransactionInterface.h"
#include "LogServResourceInterpreter.h"
#include "LogServCacheConfig.h"
#include "LogServDatabaseChangeInterface.h"
#include <logserv.rsg>
#include <centralrepository.h>
#include "LogServCacheStrings.h"
#include "LogServCacheTypes.h"
#include "LOGREPDEFS.H"
const TInt KMinimumNumberOfDigitsToMatch = 3;
////////////////////////////////////////////////////////////////////////////////////////////
// Local functions
#ifdef SYSLIBS_TEST
#pragma BullseyeCoverage off
static void LogStore32IntL(RFs& aFs, const TDesC& aFilePath, TInt aVal)
{
RFile file;
User::LeaveIfError(file.Replace(aFs, aFilePath, EFileWrite));
TPtrC8 p((const TUint8*)&aVal, sizeof(aVal));
TInt err = file.Write(p);
if(err == KErrNone)
{
err = file.Flush();
}
file.Close();
User::LeaveIfError(err);
}
static void LogStoreContactMatchCountAndNameFormatL(TInt aContactMatchCount, TLogContactNameFormat aContactNameFormat)
{
RFs fs;
CleanupClosePushL(fs);
User::LeaveIfError(fs.Connect());
_LIT(KTestDir, "c:\\test\\");
TInt err = fs.MkDir(KTestDir);
if(err != KErrNone && err != KErrAlreadyExists)
{
User::Leave(err);
}
_LIT(KLogengTestFileNameCount, "c:\\test\\test_logengconfig_count.ini");
LogStore32IntL(fs, KLogengTestFileNameCount, aContactMatchCount);
_LIT(KLogengTestFileNameFormat, "c:\\test\\test_logengconfig_format.ini");
LogStore32IntL(fs, KLogengTestFileNameFormat, (TInt)aContactNameFormat);
CleanupStack::PopAndDestroy(&fs);
}
#pragma BullseyeCoverage on
#endif //SYSLIBS_TEST
//This function reads logeng repository file and returns the integer value of the given key.
static TInt LogGetRepositoryValueL(CRepository& aRepository, TInt aKey)
{
TInt val = -1;
User::LeaveIfError(aRepository.Get(aKey, val));
return val;
}
//This function reads logeng resource file and returns the integer value of the given resource id.
static TInt LogGetResourceValueL(CLogServResourceInterpreter& aResourceInterpreter, TInt aResourceId)
{
TResourceReader reader;
aResourceInterpreter.CreateResourceReaderLC(reader, aResourceId, CLogServResourceInterpreter::ELogServer);
TInt val = reader.ReadInt16();
CleanupStack::PopAndDestroy();
return val;
}
static void LogGetContactmatchCountAndNameFormatL(CLogServResourceInterpreter& aResourceInterpreter, TInt& aContactMatchCount, TLogContactNameFormat& aContactNameFormat)
{
CRepository* repository = NULL;
TRAPD(err, repository = CRepository::NewL(KUidLogengRepository));
if(err == KErrCorrupt)
{
__ASSERT_DEBUG(!repository, User::Invariant());
User::Leave(err);
}
else if(err == KErrNone)
{
CleanupStack::PushL(repository);
aContactMatchCount = LogGetRepositoryValueL(*repository, KContactMatchCountRepKey);
aContactNameFormat = static_cast <TLogContactNameFormat> (LogGetRepositoryValueL(*repository, KContactNameFormatRepKey));
CleanupStack::PopAndDestroy(repository);
}
else
{
__ASSERT_DEBUG(!repository, User::Invariant());
aContactMatchCount = LogGetResourceValueL(aResourceInterpreter, R_LOG_CONTACT_MATCH_COUNT);
aContactNameFormat = static_cast <TLogContactNameFormat> (LogGetResourceValueL(aResourceInterpreter, R_LOG_CONTACT_NAME_FORMAT));
}
#ifdef SYSLIBS_TEST
LogStoreContactMatchCountAndNameFormatL(aContactMatchCount, aContactNameFormat);
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////
// CLogAddEvent class
CLogAddEvent::CLogAddEvent(MLogServDatabaseTransactionInterface& aDatabase, TInt aPriority) :
CLogActive(aPriority),
iDatabase(aDatabase)
{
}
CLogAddEvent::~CLogAddEvent()
{
Cancel();
CloseContactsPlugin();
delete iDuplicate;
delete iDuplicateFilter;
}
CLogAddEvent* CLogAddEvent::NewL(MLogServDatabaseTransactionInterface& aDatabase, TInt aPriority)
{
CLogAddEvent* self = new(ELeave) CLogAddEvent(aDatabase, aPriority);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
void CLogAddEvent::ConstructL()
{
iDuplicate = CLogDuplicate::NewL(iDatabase, Priority());
iDuplicateFilter = CLogFilter::NewL();
::LogGetContactmatchCountAndNameFormatL(iDatabase.DTIResourceInterface(), iContactMatchCount, iContactNameFormat);
}
//This method will open contacts database (if not opened yet), only if the value of
//r_log_contact_match_count resource in logserv.rsg resource file is not 0.
//Se how iContactMatchCount data member is initialised.
TBool CLogAddEvent::PerformContactMatchL()
{
if (iContactMatchCount <= 0 || iEvent->Contact() != KLogNullContactId)
return EFalse;
if (iContactPlugin)
return ETrue;
// Attempt to load plugin
TRAPD( err, iContactPlugin=CLogCntModel::NewL());
// If plugin doesn't exist this is equivalent to matching being disabled so we don't leave
// for KErrNotFound
if(err==KEComErrNoInterfaceIdentified)
{
// Disable contacts matching so that we don't keep attempting to match
iContactMatchCount = 0;
// Plugin doesn't exist
return EFalse;
}
User::LeaveIfError(err);
// Open the DB
OpenContactsL();
return ETrue;
}
void CLogAddEvent::StartL(CLogEvent& aEvent, const CLogServRecentList* aRecentList, TRequestStatus& aStatus, const RMessage2& aMessage)
{
__ASSERT_ALWAYS(!IsActive(), Panic(ELogAlreadyActive1));
LOGTEXT("CLogAddEvent::StartL()");
// Store event details which were obtained from the client side
iEvent = &aEvent;
iState = ELogAddEvent;
if (!iDatabase.DTIIsAllowed(EWriteOp, aMessage, iEvent->EventType()))
{
User::Leave(KErrPermissionDenied);
}
if (PerformContactMatchL())
iState = ELogSetContactAndRemoteParty; // Go look for a matching contact
iRecentList = aRecentList;
iEventAdded = EFalse;
// Setup the event's time (UTC)
TTime time;
time.UniversalTime();
iEvent->SetTime(time);
// Save the observer's request status and set it to KRequestPending
Queue(aStatus);
// Start this objects RunL chain
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
void CLogAddEvent::SetEventContact()
{
// Start by converting the phone number text into a number
// check we've got a long enough number to be worth checking
if(iEvent->Number().Length() >= KMinimumNumberOfDigitsToMatch)
{
// now search for a contact by looking up the phone number
TLogContactItemId contactId = KLogNullContactId;
TRAPD(err, contactId = iContactPlugin->MatchPhoneNumberL(iEvent->Number(), iContactMatchCount));
if(err == KErrNone)
{
// we have at least one match
if(contactId != KLogNullContactId)
{
// we have a match so set the contact id
iEvent->SetContact(contactId);
}
}
}
iEvent->SetFlags(KLogEventContactSearched);
}
void CLogAddEvent::SetRemoteParty()
{
// Get the contact id
TLogContactItemId contactId = iEvent->Contact();
if((contactId != KLogNullContactId) && (iEvent->RemoteParty().Length() == 0))
{
// Look it up and get the remote party info
// Setup buffer to contain concatenated result
TBuf<128> buf;
// Go get the info
TRAPD(err, iContactPlugin->ReadContactNameL(contactId, buf, iContactNameFormat));
if(err == KErrNotFound)
{
// Couldn't find the contact with that id so set it to NULL
iEvent->SetContact(KLogNullContactId);
}
else
{
// Found it so fill in remote party
iEvent->SetRemoteParty(buf);
}
}
}
void CLogAddEvent::GetConfigL()
{
iConfig = iDatabase.DTICacheConfig().Config();
if(iConfig.iMaxLogSize == 0)
{
LOGTEXT("CLogAddEvent::DoRunL() - logging disabled");
User::Leave(KErrNotSupported);
}
}
TLogTypeId CLogAddEvent::SetDescriptionL()
{
const TLogServCacheTypeEntry& entry = iDatabase.DTICacheTypes().FindByUid(iEvent->EventType());
if(entry.iEventTypeId == KLogNullTypeId)
{
LOGTEXT("CLogAddEvent::DoRunL() - type not found");
User::Leave(KErrNotFound);
}
if(!entry.iEventType->LoggingEnabled())
{
LOGTEXT("CLogAddEvent::DoRunL() - type not enabled");
User::Leave(KErrNotSupported);
}
iEvent->SetDescription(entry.iEventType->Description());
return entry.iEventTypeId;
}
TBool CLogAddEvent::DetectDuplicateEventsL()
{
TBool rc = EFalse;
if(iRecentList)
{
iRecentList->GetFilter(*iEvent, *iDuplicateFilter);
rc = iDuplicate->StartL(iEvent->Id(), iRecentList->Id(), *iDuplicateFilter, iStatus);
}
return rc;
}
void CLogAddEvent::DoRunL()
{
LOGTEXT3("CLogAddEvent::DoRunL(%d), state = %d", iStatus.Int(), iState);
switch (iState)
{
case ELogSetContactAndRemoteParty:
{
SetEventContact();
SetRemoteParty();
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
iState = ELogAddEvent;
SetActive();
break;
}
case ELogAddEvent:
{
GetConfigL();
TLogTypeId typeId = SetDescriptionL();
RLogEventDbTable tbl;
tbl.OpenLC(iDatabase.DTIDatabase());
::LogPurgeMainL(iDatabase, tbl, iConfig.iMaxLogSize, 1);
DoAddEventL(tbl, typeId, iDatabase.DTICacheStrings().GetIdL(iEvent->Direction()), iDatabase.DTICacheStrings().GetIdL(iEvent->Status()));
CleanupStack::PopAndDestroy();//tbl
iEventAdded = ETrue;
if(DetectDuplicateEventsL())
{
iState = ELogPurgeRecent;
SetActive();
break;
}
iState = ELogPurgeRecent;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
break;
}
case ELogPurgeRecent:
{
// Delete old recent events
if (iRecentList)
{
RArray<TLogId> logIds;
::LogGetRecentEventsLC(iDatabase, iRecentList->Id(), iConfig.iMaxRecentLogSize, logIds);
::LogPurgeRecentEventsL(iDatabase, logIds);
CleanupStack::PopAndDestroy(&logIds);
}
}
break;
default:
__ASSERT_DEBUG(ETrue, Panic(ELogBadState1));
break;
}
LOGTEXT("CLogAddEvent::DoRunL() - end");
}
void CLogAddEvent::DoCancel()
{
iDuplicate->Cancel();
CLogActive::DoCancel();
}
void CLogAddEvent::DoAddEventL(RLogEventDbTable& aTbl, TLogTypeId aTypeId, TLogStringId aDirectionId, TLogStringId aStatusId)
{
LOGTEXT("CLogAddEvent::DoAddEventL()");
// Insert a new record
aTbl.InsertL();
aTbl.SetColL(RLogEventDbTable::iTypeColNo, (TUint32)aTypeId);
if (iEvent->RemoteParty().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iRemotePartyColNo, iEvent->RemoteParty());
if (iEvent->Direction().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iDirectionColNo, (TUint32)aDirectionId);
aTbl.SetColL(RLogEventDbTable::iTimeColNo, iEvent->Time());
aTbl.SetColL(RLogEventDbTable::iDurationTypeColNo, (TInt32)iEvent->DurationType());
if (iEvent->DurationType() != KLogNullDurationType)
aTbl.SetColL(RLogEventDbTable::iDurationColNo, iEvent->Duration());
if (iEvent->Status().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iStatusColNo, (TUint32)aStatusId);
if (iEvent->Subject().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iSubjectColNo, iEvent->Subject());
if (iEvent->Number().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iNumberColNo, iEvent->Number());
if (iEvent->Contact() != KLogNullContactId)
aTbl.SetColL(RLogEventDbTable::iContactColNo, iEvent->Contact());
if (iEvent->Link() != KLogNullLink)
aTbl.SetColL(RLogEventDbTable::iLinkColNo, iEvent->Link());
if (iEvent->Data().Length() > 0)
aTbl.SetColL(RLogEventDbTable::iDataColNo, iEvent->Data());
// Set the flags
TInt bit = KLogFlagsCount;
while(bit--)
{
aTbl.SetColL(RLogEventDbTable::iFlagColNo[bit], (TUint32)((iEvent->Flags() & 0x1 << bit) ? 1 : 0));
}
if (iRecentList)
{
__ASSERT_DEBUG(iRecentList->Id() != KLogNullRecentList, Panic(ELogNullRecentList));
aTbl.SetColL(RLogEventDbTable::iRecentColNo, (TInt32)iRecentList->Id());
}
#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM
if(iEvent->SimId() != KLogNullSimId)
{
aTbl.SetColL(RLogEventDbTable::iSimIdColNo, iEvent->SimId());
}
else
{
aTbl.SetColNullL(RLogEventDbTable::iSimIdColNo);
}
#endif
// Assign event id and end the rowset operation
const TLogId newId = aTbl.ColInt32(RLogEventDbTable::iIdColNo);
iEvent->SetId(newId);
aTbl.PutL();
// Tell change interface about the addition
iDatabase.DTIChangeInterface().DCISubmitChangedEventContextL(ELogChangeTypeEventAdded, newId);
LOGTEXT("CLogAddEvent::DoAddEventL() - end");
}
void CLogAddEvent::DoComplete(TInt& aStatus)
{
LOGTEXT2("CLogAddEvent::DoComplete(%d)", aStatus);
if (iDatabase.DTIInTransaction())
{
LOGTEXT2("CLogAddEvent::DoComplete() - in transaction: %d", aStatus);
if (aStatus == KErrNone)
aStatus = iDatabase.DTICommitAndEnd();
LOGTEXT2("CLogAddEvent::DoComplete() - checking for need to rollback: %d", aStatus);
if (aStatus < KErrNone)
iDatabase.DTIRollBack();
}
else
{
if (iEventAdded)
aStatus = KErrNone;
}
LOGTEXT2("CLogAddEvent::DoComplete() - final status value is: %d", aStatus);
}
//Opens the default contacts database.
void CLogAddEvent::OpenContactsL()
{
//Sometimes, after a sequence of OpenContactsL()/CloseContacts() calls the Contacts server crashes
//and OpenContactsL() leaves with error -15. In order to avoid that the following delay has been added.
//(something related to Contacts server state machine)
User::After(100);
// Attempt to open DB
TRAPD(err,iContactPlugin->OpenContactsL());
if(KErrNone!=err)
{
if(err == KErrServerTerminated)
{
RDebug::Print(_L("+++LogEng, LogAdd.cpp, Contacts server crashed!\r\n"));
}
// If DB doesn't open delete plugin
delete iContactPlugin;
iContactPlugin = NULL;
User::Leave(err);
}
}
//Closes the default contacts database and deletes the plugin.
void CLogAddEvent::CloseContactsPlugin()
{
if(iContactPlugin)
{
iContactPlugin->CloseContacts();
delete iContactPlugin;
iContactPlugin = NULL;
//REComSession::FinalClose() call moved here from logcntmodel.cpp.
//The in-source documentation of REComSession::FinalClose() notes that:
//"It must never be called from within a plug-in implementations class
//destructor, especially following a DestroyImplementation() ".
//That was the case before the function call was moved here.
REComSession::FinalClose();
}
}