diff -r b183ec05bd8c -r 19bba8228ff0 devicediagnosticsfw/diagresultsdb/server/src/diagresultsdbsession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devicediagnosticsfw/diagresultsdb/server/src/diagresultsdbsession.cpp Wed Sep 01 12:27:42 2010 +0100 @@ -0,0 +1,1042 @@ +/* +* Copyright (c) 2007 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: Class definition of CDiagResultsDbSession +* +*/ + + +#include "diagresultsdatabasecommon.h" +#include "diagresultsdbsession.h" +#include "diagresultsdbserver.h" +#include "diagresultsdbtestrecordsubsession.h" +#include "diagresultsdatabasetestrecordinfo.h" +#include "diagresultsdbtestrecordhandle.h" +#include "diagresultsdbtestrecord.h" +#include "diagresultsdatabaseitem.h" +#include "diagresultsdbrecordinfoarraypacked.h" +#include "diagresultsdbcrdc.h" +#include "diagresultsdbrecordengineparam.h" + +//System includes +#include + +const TInt KArrayGranuality = 50; +const TInt KResultsDatabaseBufferLength=0x700; + +// --------------------------------------------------------------------------- +// constructor - must pass client to CSession +// --------------------------------------------------------------------------- +// +CDiagResultsDbSession::CDiagResultsDbSession(CDiagResultsDbServer * aServer): + iServer(aServer), iSubsessionContainer( NULL ), iSubsessionIndex(NULL), + iLastResultCommand( 0 ), + iStore(NULL), iBufferedLastResults(NULL), iBufferedSingleResult(NULL), + iHasWrittenData(EFalse) + { + aServer->IncreaseSessionCount(); + } + +// --------------------------------------------------------------------------- +// NewL. +// --------------------------------------------------------------------------- +// +CDiagResultsDbSession* CDiagResultsDbSession::NewL(CDiagResultsDbServer * aServer) + { + CDiagResultsDbSession* pSession= new (ELeave) CDiagResultsDbSession( aServer ); + CleanupStack::PushL( pSession ); + pSession->ConstructL(); + CleanupStack::Pop(); + return pSession; + } + +// --------------------------------------------------------------------------- +// ConstructL. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::ConstructL() + { + iSubsessionContainer = iServer->NewContainerL(); + iSubsessionIndex = CObjectIx::NewL(); + + // buffer for IPC. Objects are transmitted in this buffer. + // Size of the buffer has to be selected carefully. + // It should be 'large enough' but not too large. + // If you see too many overflows, increase the size. + iBuffer = CBufFlat::NewL(KArrayGranuality); + iBuffer->ResizeL(KResultsDatabaseBufferLength); + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CDiagResultsDbSession::~CDiagResultsDbSession() + { + if ( iBuffer ) + { + iBuffer->Reset(); + delete iBuffer; + iBuffer = NULL; + } + + // NOTE! + // The deletion order is important here. You must + // delete object indexes first, because this zeros the + // number of references to any CObject type objects. Trying + // to delete object container first, you get panic + // E32USER-CBase 33: an attempt is made to delete the CObject + // when the reference count is not zero. + delete iSubsessionIndex; + + if ( iSubsessionContainer ) + { + iServer->RemoveContainer( iSubsessionContainer ); + } + + delete iStore; + + if ( iServer ) + { + iServer->DecreaseSessionCount(); + } + + if ( iBufferedLastResults ) + { + iBufferedLastResults->ResetAndDestroy(); + iBufferedLastResults->Close(); + delete iBufferedLastResults; + iBufferedLastResults = 0; + } + + if ( iBufferedSingleResult ) + { + delete iBufferedSingleResult; + iBufferedSingleResult = NULL; + } + } + +// --------------------------------------------------------------------------- +// Service client requests. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::ServiceL(const RMessage2& aMessage) + { + LOGME("CDiagResultsDbSession::ServiceL"); + TBool async = EFalse; + TRAPD( err, async = DispatchMessageL( aMessage ) ); + LOGME1("CDiagResultsDbSession::ServiceL %d",err); + if ( !async ) + { + aMessage.Complete( err ); + } + } + +// --------------------------------------------------------------------------- +// service a client request; test the opcode and then do appropriate servicing +// --------------------------------------------------------------------------- +// +TBool CDiagResultsDbSession::DispatchMessageL(const RMessage2 &aMessage) + { + LOGME("CDiagResultsDbSession::DispatchMessageL"); + iMsg = aMessage; + TInt function = aMessage.Function(); + LOGME1("CDiagResultsDbSession::DispatchMessageL - %d",function); + TInt handle(0); + CObject* subsession = NULL; + switch( function ) + { + case DiagResultsDbCommon::EConnect: + { + LOGME("CDiagResultsDbSession::EConnect"); + TPckgBuf uidpckg; + aMessage.Read(0, uidpckg); + iDbUid = uidpckg(); + + iStore = CDiagResultsDbStore::NewL( iDbUid ); + return EFalse; + } + + case DiagResultsDbCommon::EClose: + { + LOGME("CDiagResultsDbSession::EClose"); + aMessage.Complete (KErrNone); + + return ETrue; + } + + case DiagResultsDbCommon::EGetRecordCount: + { + LOGME("CDiagResultsDbSession::EGetRecordCount"); + RArray uids; + CleanupClosePushL ( uids ); + iStore->RecordUidsL( uids ); + + TPckgBuf pckg( uids.Count() ); + aMessage.Write(0, pckg); + + CleanupStack::PopAndDestroy( &uids ); + return EFalse; + } + + case DiagResultsDbCommon::EConnectSubsession: + LOGME("CDiagResultsDbSession::EConnectSubsession"); + ConnectSubsessionL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::ESubsessionCreateNewRecord: + LOGME("CDiagResultsDbSession::ESubsessionCreateNewRecord"); + CreateNewRecordL( aMessage ); + HasWritten(); + return EFalse; + + case DiagResultsDbCommon::ECloseSubsession: + LOGME("CDiagResultsDbSession::ECloseSubsession"); + CloseSubsessionL(); + return EFalse; + + case DiagResultsDbCommon::EGetLastRecord: + LOGME("CDiagResultsDbSession::EGetLastRecord"); + GetLastRecordL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::EGetLastNotCompletedRecord: + LOGME("CDiagResultsDbSession::EGetLastNotCompletedRecord"); + GetLastNotCompletedRecordL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::EGetRecordList: //record uids + LOGME("CDiagResultsDbSession::EGetRecordList"); + GetRecordListL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::EGetRecordInfoList: + LOGME("CDiagResultsDbSession::EGetRecordInfoList"); + GetRecordInfoListL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::EInitiateGetLastResults: //Async + { + LOGME("CDiagResultsDbSession::EInitiateGetLastResults"); + ReadBufferL( aMessage, 0, iBuffer ); + + iLastResultsMsg = iMsg; + + iLastResultCommand = DiagResultsDbCommon::EInitiateGetLastResults; + + if ( iBufferedLastResults ) + { + iBufferedLastResults->ResetAndDestroy(); + iBufferedLastResults->Close(); + delete iBufferedLastResults; + iBufferedLastResults = 0; + } + + iStore->ExistingRecordsAsyncL( *this ); + return ETrue; + } + + case DiagResultsDbCommon::EInitiateGetSingleLastResult: //Async + { + LOGME("CDiagResultsDbSession::EInitiateGetSingleLastResult"); + iLastSingleResultsMsg = iMsg; + + iLastResultCommand = DiagResultsDbCommon::EInitiateGetSingleLastResult; + + if ( iBufferedSingleResult ) + { + delete iBufferedSingleResult; + iBufferedSingleResult = NULL; + } + + iStore->ExistingRecordsAsyncL( *this ); + return ETrue; + } + + + case DiagResultsDbCommon::EGetLastResults: + LOGME("CDiagResultsDbSession::EGetLastResults"); + GetLastResultsL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::EGetSingleLastResult: + LOGME("CDiagResultsDbSession::EGetSingleLastResult"); + GetSingleLastResultL( aMessage ); + return EFalse; + + case DiagResultsDbCommon::ECancelInitiateGetLastResults: + { + LOGME("CDiagResultsDbSession::ECancelInitiateGetLastResults"); + CancelLastResultsL( aMessage ); + return EFalse; + } + + // Sub-session requests. See CDiagResultsDbTestRecordSubsession. + case DiagResultsDbCommon::ESubsessionGetTestRecordId: + case DiagResultsDbCommon::ESubsessionTestCompleted: + case DiagResultsDbCommon::ESubsessionIsTestCompleted: + case DiagResultsDbCommon::ESubsessionGetRecordInfo: + case DiagResultsDbCommon::ESubsessionGetTestUids: + case DiagResultsDbCommon::ESubsessionSuspend: + case DiagResultsDbCommon::ESubsessionIsSuspended: + case DiagResultsDbCommon::ESubsessionLogTestResult: + case DiagResultsDbCommon::ESubsessionGetTestResult: + case DiagResultsDbCommon::ESubsessionGetTestResults: + case DiagResultsDbCommon::ESubsessionGetEngineParam: + case DiagResultsDbCommon::ESubsessionGetStatus: + case DiagResultsDbCommon::ESubsessionCancelLogTestResult: + LOGME("CDiagResultsDbSession::ESubsessionCancelLogTestResult"); + handle = aMessage.Int3(); + subsession = iSubsessionIndex->At( handle ); + return static_cast(subsession) + ->DispatchMessageL( aMessage ); + default: + aMessage.Panic( _L("DiagSrv panic: unknown command"), + DiagResultsDbCommon::EBadRequest ); + return EFalse; + } + } + +// --------------------------------------------------------------------------- +// Get function. +// --------------------------------------------------------------------------- +// +TBool CDiagResultsDbSession::SessionHasWritten() const + { + return iHasWrittenData; + } + +// --------------------------------------------------------------------------- +// Connect to a subsession. Test record represents the connected subsession. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::ConnectSubsessionL( const RMessage2 &aMessage ) + { + TPckgBuf uidpckg; + aMessage.Read(0, uidpckg); + TUid recordUid = uidpckg(); + + TPckgBuf readOnlyPckg; + aMessage.Read(1, readOnlyPckg); + TBool readonly = readOnlyPckg(); + + CDiagResultsDbTestRecordHandle* handle = iStore->OpenExistingHandleL( + recordUid ); + + if ( readonly ) + { + // keep the record as it is. + } + else + { + if ( handle->RecordInfo().iCompleted ) + { + delete handle; + handle = 0; + User::Leave( KErrAlreadyExists ); + } + + handle->RecordInfo().iRecordStatus = + TDiagResultsDatabaseTestRecordInfo::EOpen; + } + + CreateSubsessionL( handle, readonly ); + } + +// --------------------------------------------------------------------------- +// Create a new record. This does not write data into the db file. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CreateNewRecordL ( const RMessage2 &aMessage ) + { + + ReadBufferL( aMessage, 1, iBuffer ); + + RBufReadStream stream( *iBuffer ); + CleanupClosePushL ( stream ); + + CDiagResultsDbRecordEngineParam* params = + CDiagResultsDbRecordEngineParam::NewL ( stream ); + CleanupStack::PushL( params ); + + CDiagResultsDbTestRecordHandle* handle = iStore->CreateNewRecordL( params ); + + handle->RecordInfo().iRecordStatus = + TDiagResultsDatabaseTestRecordInfo::EOpen; + + CleanupStack::Pop(); //params + CleanupStack::PopAndDestroy( &stream ); + + CreateSubsessionL(handle, EFalse); + + TPckgBuf recorduidpckg( handle->RecordInfo().iRecordId ); + + aMessage.Write(0, recorduidpckg ); + } + +// --------------------------------------------------------------------------- +// Return the database file uid. +// --------------------------------------------------------------------------- +// +TUid CDiagResultsDbSession::DbUid() const + { + return iDbUid; + } + +// --------------------------------------------------------------------------- +// Create a new subsession. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CreateSubsessionL( + CDiagResultsDbTestRecordHandle* aTestRecordHandle, + TBool aReadonly ) + { + TInt handle(0); + CObject* subsession = NULL; + + // Create sub-session object + subsession = CDiagResultsDbTestRecordSubsession::NewL( this, + aTestRecordHandle, + aReadonly ); + CleanupStack::PushL( subsession ); + iSubsessionContainer->AddL( subsession ); + CleanupStack::Pop(); + + // Create sub-session handle + TRAPD( err, handle = iSubsessionIndex->AddL( subsession ) ); + + // Remember to remove session object from object container + if( err != KErrNone ) + { + iSubsessionContainer->Remove( subsession ); + User::Leave( DiagResultsDbCommon::ESvrCreateSubsession ); + } + + // Package to pass information to the client + TPckgC handlePckg(handle); + + // Send handle to the client + TRAP( err, iMsg.WriteL( 3, handlePckg ) ); + if( err != KErrNone ) + { + iSubsessionIndex->Remove(handle); + User::Leave( DiagResultsDbCommon::ESvrCreateSubsession ); + } + + return; + } + +// --------------------------------------------------------------------------- +// Close existing subsession. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CloseSubsessionL() + { + TInt handle = iMsg.Int3(); + iSubsessionIndex->Remove(handle); + } + +// --------------------------------------------------------------------------- +// Return store that is responsible for handling the database file. +// --------------------------------------------------------------------------- +// +CDiagResultsDbStore& CDiagResultsDbSession::Store() + { + return *iStore; + } + +// --------------------------------------------------------------------------- +// Service function. Searches for newest test results. +// Related functions: +// ExistingRecordsAsyncL (initiates async fetch of test records) +// CancelLastResultsL (cancel async fetch) +// ExistingRecordsL (retrieves test records) +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetLastResultsL( const RMessage2 &aMessage ) + { + if ( iBufferedLastResults == NULL ) + { + User::Leave ( KErrNotFound ); + } + + ReadBufferL( aMessage, 0, iBuffer ); + + RBufWriteStream stream ( *iBuffer ); + CleanupClosePushL( stream ); + + stream.WriteInt16L( iBufferedLastResults->Count() ); + + for ( TInt i = 0; i < iBufferedLastResults->Count(); ++i ) + { + CDiagResultsDatabaseItem* item = (*iBufferedLastResults)[i]; + + if ( item == NULL ) + { + stream.WriteUint8L(0); + } + else + { + stream.WriteUint8L(1); + (*iBufferedLastResults)[i]->ExternalizeL( stream ); + } + } + + if ( iBuffer->Ptr(0).Length() > aMessage.GetDesMaxLength(0) ) + { + User::Leave( KErrOverflow ); + } + + stream.CommitL(); + + CleanupStack::PopAndDestroy( &stream ); + + aMessage.Write( 0, iBuffer->Ptr(0) ); //write to client's address space + + iBufferedLastResults->ResetAndDestroy(); + iBufferedLastResults->Close(); + delete iBufferedLastResults; + iBufferedLastResults = NULL; + } + + +// --------------------------------------------------------------------------- +// Service function. Get single test result (the newest). +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetSingleLastResultL( const RMessage2 &aMessage ) + { + ReadBufferL( aMessage, 0, iBuffer ); + + RBufWriteStream stream ( *iBuffer ); + CleanupClosePushL( stream ); + + if ( iBufferedSingleResult ) + { + stream.WriteInt8L( 1 ); + + iBufferedSingleResult->ExternalizeL( stream ); + + } + else //NULL + { + stream.WriteInt8L( 0 ); + } + + stream.CommitL(); + CleanupStack::PopAndDestroy( &stream ); + + aMessage.Write( 0, iBuffer->Ptr(0) ); + + delete iBufferedSingleResult; + iBufferedSingleResult = NULL; + } + + +// --------------------------------------------------------------------------- +// Service function. Cancel InitiateGetLastResults method. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CancelLastResultsL( const RMessage2 & /*aMessage*/ ) + { + if ( !iLastResultsMsg.IsNull() ) + { + iStore->Cancel(); + iLastResultsMsg.Complete( KErrCancel ); + + if ( iBufferedLastResults ) + { + iBufferedLastResults->ResetAndDestroy(); + delete iBufferedLastResults; + iBufferedLastResults = NULL; + } + } + } + +// --------------------------------------------------------------------------- +// Service function. Retrieve uid of the newest test record. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetLastRecordL( const RMessage2 &aMessage ) + { + RArray uids; + CleanupClosePushL ( uids ); + iStore->RecordUidsL( uids ); + + if ( uids.Count() == 0 ) + { + User::Leave( KErrNotFound ); + } + + TPckgBuf pckg( uids[uids.Count() -1] ); //newest record is the last + aMessage.Write( 0, pckg ); + + CleanupStack::PopAndDestroy( &uids ); + } + + +// --------------------------------------------------------------------------- +// Service function. Try to find a test record that was suspended. +// Leave with KErrNotFound if such test record is not found. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetLastNotCompletedRecordL( const RMessage2 &aMessage ) + { + RArray uids; + CleanupClosePushL ( uids ); + iStore->RecordUidsL( uids ); + + TBool found = EFalse; + + // newest record are on the top. + for (TInt i = uids.Count() -1; i >= 0; --i) + { + CDiagResultsDbTestRecordHandle* handle = + iStore->OpenExistingHandleL( uids[i] ); + + CleanupStack::PushL (handle); + + if( handle->RecordInfo().iRecordStatus != + TDiagResultsDatabaseTestRecordInfo::ECompleted && + !handle->RecordInfo().iCompleted ) + + { + TPckgBuf pckg( handle->RecordInfo().iRecordId ); + aMessage.Write( 0, pckg ); + found = ETrue; + CleanupStack::PopAndDestroy( handle ); + break; + } + + CleanupStack::PopAndDestroy( handle ); + } + + CleanupStack::PopAndDestroy( &uids ); + + if ( !found ) + { + User::Leave ( KErrNotFound ); + } + } + +// --------------------------------------------------------------------------- +// Service function. Retrieve all test record uids that there are. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetRecordListL( const RMessage2 &aMessage ) + { + ReadBufferL( aMessage, 0, iBuffer ); + + RArray uids; + CleanupClosePushL( uids ); + iStore->RecordUidsL( uids ); + + RBufWriteStream stream ( *iBuffer ); + CleanupClosePushL( stream ); + stream.WriteInt16L( uids.Count() ); + + for ( TInt i = 0; i < uids.Count(); ++i ) + { + stream.WriteInt32L( uids[i].iUid ); + } + + if ( iBuffer->Ptr(0).Length() > aMessage.GetDesMaxLength(0) ) + { + User::Leave( KErrOverflow ); + } + + aMessage.Write( 0, iBuffer->Ptr(0) ); //write to client's address space + + CleanupStack::PopAndDestroy( &stream ); + CleanupStack::PopAndDestroy( &uids ); + + } + +// --------------------------------------------------------------------------- +// Service function. Return overviews of test records. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::GetRecordInfoListL( const RMessage2 &aMessage ) + { + ReadBufferL( aMessage, 0, iBuffer ); + + CArrayFixFlat* array = new (ELeave) + CArrayFixFlat(KArrayGranuality); + CleanupStack::PushL (array); + + RArray uids; + CleanupClosePushL ( uids ); + iStore->RecordUidsL( uids ); + + for (TInt i = 0; i < uids.Count(); ++i) + { + CDiagResultsDbTestRecordHandle* handle = + iStore->OpenExistingHandleL( uids[i] ); + + CleanupStack::PushL (handle); + array->AppendL( handle->RecordInfo() ); + + CleanupStack::PopAndDestroy( handle ); + } + + CleanupStack::PopAndDestroy( &uids ); + + TDiagResultsDbRecordInfoArrayPacked packedArray ( iBuffer ); + packedArray.PackArrayL ( *array ); + + if ( iBuffer->Ptr(0).Length() > aMessage.GetDesMaxLength(0) ) + { + User::Leave( KErrOverflow ); + } + + aMessage.Write( 0, iBuffer->Ptr(0) ); //write to client's address space + + CleanupStack::PopAndDestroy( array ); + + } + +// --------------------------------------------------------------------------- +// Helper function to read a buffer from client side. Leaves the buffer +// onto cleanup stack IF it has to create a new one. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::ReadBufferL(const RMessage2& aMessage, TInt aParam, + CBufFlat*& aBuffer) + { + TInt desLen = aMessage.GetDesLengthL(aParam); + + if(desLen >= 0) + { + if (aBuffer==NULL) + { + aBuffer = CBufFlat::NewL(KArrayGranuality); + aBuffer->ResizeL(desLen); + CleanupStack::PushL(aBuffer); + } + else if (desLen > aBuffer->Ptr(0).MaxLength()) + { + iBuffer->Delete( 0, iBuffer->Size() ); // delete old data. + // we have to increase the size of aBuffer + aBuffer->ResizeL(desLen); + } + + TPtr8 desPtr = aBuffer->Ptr(0); + aMessage.ReadL(aParam, desPtr); + + } + else + { + // desLen is negative leave with an error. + User::Leave(KErrArgument); + } + } + +// --------------------------------------------------------------------------- +// Store observer method. This is called after store has retrieved +// test records from the DB file. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::ExistingRecordsL( TInt aError, + RPointerArray* aArray ) + { + CleanupStack::PushL ( aArray ); + CleanupResetAndDestroyClosePushL( *aArray ); + + //Check if there were any errors during loading the test records. + if ( aError != KErrNone ) + { + + if ( iLastResultCommand == + DiagResultsDbCommon::EInitiateGetLastResults ) + { + iLastResultsMsg.Complete ( aError ); + } + else if ( iLastResultCommand == + DiagResultsDbCommon::EInitiateGetSingleLastResult ) + { + iLastSingleResultsMsg.Complete ( aError ); + } + + CleanupStack::PopAndDestroy( aArray ); + CleanupStack::PopAndDestroy( aArray ); //delete the pointer + return; + } + + switch( iLastResultCommand ) + { + + //Find multiple last results + //buffer contains the uids to be searched. + //See RDiagResultsDatabase::InitiateGetLastResults. + case DiagResultsDbCommon::EInitiateGetLastResults: + { + //Trap is needed so that we can complete client's request + //if any errors occur. + TRAPD(error, FindLastResultsL( *aArray )); + + if ( error != KErrNone ) + { + iLastResultsMsg.Complete (error); + break; + } + else + { + iLastResultsMsg.Complete (KErrNone); + } + + break; + } + + // Find single test result + // see RDiagResultsDatabase::InitiateGetLastResult. + case DiagResultsDbCommon::EInitiateGetSingleLastResult: + { + TPckgBuf uidpckg; + iLastSingleResultsMsg.Read(0, uidpckg); + TUid resultsItemUid = uidpckg(); + + CDiagResultsDatabaseItem* item = NULL; + + // Trap any errors and complete client's request if there + // are any errors. + TRAPD(error, item = FindDatabaseItemL( resultsItemUid, aArray)) ; + + if ( error != KErrNone ) + { + delete item; + item = NULL; + + iLastSingleResultsMsg.Complete (error); + break; + } + + if ( item == NULL ) //Not found + { + iBufferedSingleResult = NULL; + + //Check also the last results buffer + CheckLastResultsBufferL( resultsItemUid, iBufferedSingleResult ); + } + else + { + iBufferedSingleResult = item; + } + + + + iLastSingleResultsMsg.Complete (KErrNone); + break; + } + + default: + { + User::Panic ( _L("Diag results DB"), + DiagResultsDbCommon::EUnknownLastResultState ); + } + } + + iLastResultCommand = 0; + + CleanupStack::PopAndDestroy( aArray ); //call reset and destroy + close + CleanupStack::PopAndDestroy( aArray ); //delete the pointer + } + + +// --------------------------------------------------------------------------- +// Read UIDs from a stream and search last results. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::FindLastResultsL( + RPointerArray& aArray ) + { + + CArrayFixFlat* uidArray = new (ELeave) + CArrayFixFlat(KArrayGranuality); + CleanupStack::PushL( uidArray ); + + RBufReadStream stream( *iBuffer ); + CleanupClosePushL ( stream ); + + TInt8 count = stream.ReadInt8L(); + + for ( TInt i = 0; i < count; ++i ) + { + TInt32 uid = stream.ReadInt32L(); + + uidArray->AppendL( TUid::Uid( uid )); + } + + CleanupStack::PopAndDestroy( &stream ); + + iBufferedLastResults = new (ELeave) RPointerArray; + + SearchLastResultsL( *uidArray, aArray, *iBufferedLastResults ); + + //last results could be also in the last results buffer. + CheckLastResultsBufferL( *uidArray, *iBufferedLastResults ); + + // there must be exactly the same number of cells in both arrays. + if ( uidArray->Count() != iBufferedLastResults->Count() ) + { + User::Panic ( _L("Diag results DB"), + DiagResultsDbCommon::EGetLastResultsMismatch ); + } + + CleanupStack::PopAndDestroy( uidArray ); + } + + +// --------------------------------------------------------------------------- +// Check last result buffer for test results. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CheckLastResultsBufferL( + const CArrayFixFlat& aUidArray, + RPointerArray& aResultsArray ) + { + CDiagResultsDbTestRecord* buffer = iStore->OpenExistingLastResultsBufferL(); + CleanupStack::PushL( buffer ); + + for (TInt i = 0; i < aResultsArray.Count(); ++i) + { + if ( aResultsArray[i] == NULL ) + { + TUid uid = aUidArray[i]; + + CDiagResultsDatabaseItem* item = buffer->FindTestRecord( uid ); + + if ( item ) + { + buffer->RemoveL( uid ); + aResultsArray[i] = item; + } + } + } + + CleanupStack::PopAndDestroy( buffer ); + } + + +// --------------------------------------------------------------------------- +// Check last result buffer for test result. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::CheckLastResultsBufferL( + TUid aTestUid, + CDiagResultsDatabaseItem*& aResult ) + { + + + CDiagResultsDbTestRecord* buffer = iStore->OpenExistingLastResultsBufferL(); + CleanupStack::PushL( buffer ); + + CDiagResultsDatabaseItem* item = buffer->FindTestRecord( aTestUid ); + + if ( item ) + { + buffer->RemoveL( aTestUid ); + aResult = item; + } + + CleanupStack::PopAndDestroy( buffer ); + } + + +// --------------------------------------------------------------------------- +// Searches for the newest test results. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::SearchLastResultsL( + const CArrayFixFlat& aUidArray, + RPointerArray& aTestRecordArray, + RPointerArray& aResultsArray ) + + { + //Search all records for certain uid. + for ( TInt i = 0; i < aUidArray.Count(); ++i ) + { + + CDiagResultsDatabaseItem* item = + FindDatabaseItemL( aUidArray[i], &aTestRecordArray ); + + if ( item == NULL ) //Not found + { + aResultsArray.Append(NULL); + } + else + { + aResultsArray.Append( item ); + } + } + } + + +// --------------------------------------------------------------------------- +// Indicates has session written any data into the DB file. +// To be exact only subsession writes data into the file. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::HasWritten() + { + iHasWrittenData = ETrue; + } + +// --------------------------------------------------------------------------- +// Turn off compacting. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbSession::DoNotCompact() + { + iHasWrittenData = EFalse; + } + +// --------------------------------------------------------------------------- +// Helper function to seach an item from a pointer array. +// Starts from the newest record to search for an UID. +// --------------------------------------------------------------------------- +// +CDiagResultsDatabaseItem* CDiagResultsDbSession::FindDatabaseItemL( TUid aUid, + RPointerArray* aArray ) + { + + //Check that there is a test record. + if ( !aArray || aArray->Count() == 0 ) + { + return NULL; + } + + // start from the newest record + for (TInt x = aArray->Count() -1; x >= 0 ; --x ) + { + CDiagResultsDbTestRecord* record = (*aArray)[x]; + + //Assumes that there is only MAX one specific UID in the test record. + for ( TInt y = 0; y < record->Count(); ++y ) + { + CDiagResultsDatabaseItem* item = record->GetItem( y ); + + // Search for a test result that is not skipped / cancelled. + if ( item->TestUid() == aUid && + (item->TestResult() == CDiagResultsDatabaseItem::ESuccess || + item->TestResult() == CDiagResultsDatabaseItem::EFailed )) + { + //Remove the found item to speed up look up times. + //This does not remove it from the DB file. + record->RemoveL( y ); + return item; + } + } + } + + return NULL; + }