--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devicediagnosticsfw/diagresultsdb/server/src/diagresultsdbstore.cpp Thu Dec 17 09:07:52 2009 +0200
@@ -0,0 +1,1267 @@
+/*
+* 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
+*
+*/
+
+#include <s32std.h>
+#include <sysutil.h>
+#include <f32file.h>
+#include <centralrepository.h>
+
+#include "diagresultsdbtestrecord.h"
+#include "diagresultsdbstore.h"
+#include "diagresultsdatabasecommon.h"
+#include "diagresultsdbcrdc.h"
+#include "diagresultsdatabase.h"
+#include "diagresultsdbtestrecordhandle.h"
+#include "diagresultsdbrecordengineparam.h"
+#include "diagresultsdbprivatecrkeys.h"
+
+_LIT( KFileType, ".dat" );
+_LIT( KDriveC, "C:" );
+
+const TInt KMininumDiskSpace = 500;
+
+const TInt KFirstStreamId = 2;
+
+// ---------------------------------------------------------------------------
+// Constructor.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbStore::CDiagResultsDbStore( TUid aDbUid ):
+ CActive( CActive::EPriorityStandard ), iStore( NULL ),
+ iDbUid( aDbUid ), iLoadingObserver( NULL ), iCompletionObserver( NULL ),
+ iRecordArray( NULL ), iRecordIds(NULL), iRecordNumber(0),
+ iTestRecord( NULL ), iState( ENotRunning )
+ {
+ CActiveScheduler::Add(this);
+ }
+
+// ---------------------------------------------------------------------------
+// NewL. Creates a DB file based on aDbUid if it does not exist.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbStore* CDiagResultsDbStore::NewL( TUid aDbUid )
+ {
+ LOGME("CDiagResultsDbStore* CDiagResultsDbStore::NewL");
+ CDiagResultsDbStore* store = new (ELeave) CDiagResultsDbStore( aDbUid );
+ CleanupStack::PushL( store );
+ store->ConstructL();
+ CleanupStack::Pop();
+ return store;
+ }
+
+// ---------------------------------------------------------------------------
+// NewLC.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbStore* CDiagResultsDbStore::NewLC( TUid aDbUid )
+ {
+ LOGME("CDiagResultsDbStore::NewLC");
+ CDiagResultsDbStore* store = new (ELeave) CDiagResultsDbStore( aDbUid );
+ CleanupStack::PushL( store );
+ store->ConstructL();
+ return store;
+ }
+
+// ---------------------------------------------------------------------------
+// Starts asynchronous loading of all test records from the DB.
+// Observer is notified when test records have been loaded.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::ExistingRecordsAsyncL( MRecordLoadingObserver& aObserver )
+ {
+ if ( IsActive() )
+ {
+ User::Leave( KErrInUse );
+ }
+
+ iLoadingObserver = &aObserver;
+
+ iState = EGetRootStream;
+ iRecordNumber = 0;
+
+ CompleteOwnRequest();
+
+ SetActive();
+ }
+
+// ---------------------------------------------------------------------------
+// Cancel asynchronous functionality. Do not call this directly (use Cancel())
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::DoCancel()
+ {
+ if ( iRecordArray )
+ {
+ iRecordArray->ResetAndDestroy();
+
+ delete iRecordArray;
+ iRecordArray = NULL;
+ }
+
+ //Note that Store is not responsible for deleting the iTestRecord.
+ //iTestRecord is deleted by subsession.
+
+ delete iRecordIds;
+ iRecordIds = NULL;
+
+ iState = ENotRunning;
+ iRecordNumber = 0;
+ }
+
+// ---------------------------------------------------------------------------
+// This is called if there are leaves or errors in RunL.
+// ---------------------------------------------------------------------------
+//
+TInt CDiagResultsDbStore::RunError(TInt aError)
+ {
+ TInt err = KErrNone;
+
+ switch ( iState )
+ {
+ case EGetRootStream:
+ case EGetRecord:
+ case ERecordsReady:
+ {
+ TRAP( err, iLoadingObserver->ExistingRecordsL( aError, NULL ) );
+ }
+ break;
+ default:
+ {
+ ///@@@KSR: changes for Codescanner error val = High
+ //User::Leave( aError );
+ }
+ }
+
+ return err;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Keeps the database file small as possible. Compacting must be done often as
+// possible keeping in mind that compacting could take a long time.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CompactDatabase()
+ {
+ TRAPD( error, iStore->CompactL());
+
+ if ( error != KErrNone )
+ {
+ User::Panic(_L("compact error"),
+ DiagResultsDbCommon::EDatabaseCompact );
+ }
+ else
+ {
+ //Commit must be called in order to really reduce the size of DB file.
+ TRAP( error, iStore->CommitL() );
+ if ( error != KErrNone )
+ {
+ User::Panic(_L("compact/commit error"),
+ DiagResultsDbCommon::EDatabaseCompactCommit );
+ }
+ }
+ }
+
+// ---------------------------------------------------------------------------
+// Completes own request. This changes the value of TRequestStatus and causes
+// RunL to be called afterwards (if CActive is active).
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CompleteOwnRequest()
+ {
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrNone );
+ }
+
+// ---------------------------------------------------------------------------
+// Asynchronous handling function. States are needed to separate two different
+// tasks. The first one is loading test records from the db file. The second
+// is writing a test record into the DB file.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::RunL()
+ {
+
+ switch ( iState )
+ {
+ case EGetRootStream:
+ {
+ iRecordArray = new (ELeave) RPointerArray<CDiagResultsDbTestRecord>;
+ iRecordIds = new (ELeave) RArray<TStreamId>;
+ // Find available stream ids
+ ReadRootStreamL( *iRecordIds );
+
+ if ( iRecordIds->Count() == 0 ) //no records, no need to continue
+ {
+ //Ownership is transferred
+ iLoadingObserver->ExistingRecordsL( KErrNone, iRecordArray );
+ iRecordArray = NULL; //do not delete
+
+ iRecordIds->Close();
+ delete iRecordIds;
+ iRecordIds = NULL;
+
+ iState = ENotRunning;
+ iRecordNumber = 0;
+
+ iLoadingObserver = NULL;
+ return;
+ }
+
+ iState = EGetRecord;
+
+ CompleteOwnRequest();
+ SetActive();
+ }
+ break;
+
+ case EGetRecord:
+ {
+ iRecordArray->Append ( OpenExistingRecordL(
+ TUid::Uid( (*iRecordIds)[iRecordNumber].Value()), ETrue ) );
+ iRecordNumber++;
+ if ( iRecordNumber < iRecordIds->Count() )
+ {
+ iState = EGetRecord;
+ }
+ else
+ {
+ iState = ERecordsReady;
+ }
+
+ CompleteOwnRequest();
+ SetActive();
+ }
+ break;
+
+ case ERecordsReady:
+ {
+ //Ownership is transferred
+ iLoadingObserver->ExistingRecordsL( KErrNone, iRecordArray );
+ iRecordArray = NULL; //do not delete
+
+ iRecordIds->Close();
+ delete iRecordIds;
+ iRecordIds = NULL;
+
+ iState = ENotRunning;
+ iRecordNumber = 0;
+
+ iLoadingObserver = NULL;
+ }
+ break;
+
+
+ default:
+ User::Leave( KErrNotFound );
+ break;
+
+ }
+
+ }
+
+// ---------------------------------------------------------------------------
+// Constructs the store. Creates the DB file if it does not exist or opens it.
+// Also creates an empty root stream into the store if file was created.
+// There is some exception handling included for example if file exist but it
+// cannot be opened.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::ConstructL()
+ {
+ LOGME("CDiagResultsDbStore::ConstructL");
+ //Read from central repository.
+ TInt algorithm = GetDeletionAlgorithmL();
+
+ if ( algorithm == 0 )
+ {
+ iDeletionAlgorithm = EMaxRecordCountAlgorithm;
+ }
+ else if ( algorithm == 1 )
+ {
+ iDeletionAlgorithm = ELastResultsAlgorithm;
+ }
+ else
+ {
+ User::Panic( _L("Unknown Deletion Algorithm"),
+ DiagResultsDbCommon::EUnknownDeletionAlgorithm);
+ }
+
+ LOGME("resultdbstore::Before irfs.connect");
+ User::LeaveIfError(iRfs.Connect());
+ LOGME("iRfs.connect");
+ TBool tooLowSpace = SysUtil::DiskSpaceBelowCriticalLevelL(
+ &iRfs,
+ KMininumDiskSpace,
+ EDriveC );
+ LOGME1("tooLowSpace %d",tooLowSpace);
+ if ( tooLowSpace )
+ {
+ User::Leave( KErrDiskFull );
+ }
+
+ TInt err = iRfs.CreatePrivatePath( EDriveC );
+
+ LOGME1("privatepathcreate %d",err);
+ //Create private path if it does not exists
+ if ( err != KErrAlreadyExists && err != KErrNone )
+ {
+ User::Leave ( err ); //Unexpected error
+ }
+
+ TBuf<256> filename;
+ iRfs.PrivatePath( filename );
+ filename.Insert( 0, KDriveC );
+ filename.Append( iDbUid.Name() );
+ filename.Append( KFileType );
+
+ //Check is the File already there
+ //open for reading and writing.
+ err = iFile.Open( iRfs, filename, EFileShareAny|EFileWrite );
+
+ LOGME1("ifile.open %d",err);
+ TInt fileError = KErrNotFound;
+ TInt fileSize = 0;
+
+ if ( err == KErrNone )
+ {
+ fileError = iFile.Size( fileSize );
+ }
+
+
+ //Checking is done when the file exists.
+ //File size reading error should never happen.
+ //if the file is too large, the function will panic.
+ if ( err == KErrNone && fileError == KErrNone )
+ {
+ CheckMaximumFileSizeLimitL( iRfs, filename, fileSize );
+ }
+
+ iFile.Close();
+
+ // File exist
+ if ( err == KErrNone )
+ {
+ TRAPD( err, iStore = CPermanentFileStore::OpenL(iRfs, filename,
+ EFileShareAny|EFileWrite|EFileRead|EFileStream ));
+
+ if ( err != KErrNone )
+ {
+ err = iRfs.Delete ( filename );
+
+ if ( err != KErrNone )
+ {
+ User::Panic( _L("DiagServer corrupt file delete failed"),
+ DiagResultsDbCommon::EServerCorruptFileDelete);
+ }
+
+ User::Leave( KErrCorrupt );
+ }
+
+ //This must be called, otherwise RStoreWriteStream write will fail.
+ iStore->SetTypeL( iStore->Layout() );
+ }
+
+ else if ( err == KErrNotFound ) //Create new file if the file is not found
+ {
+ LOGME("createnewfile");
+ TRAPD( err, iStore = CPermanentFileStore::CreateL(iRfs, filename,
+ EFileShareAny|EFileWrite|EFileRead|EFileStream ));
+
+ LOGME1("createnewfile = %d",err);
+ if ( err != KErrNone ) //File could not be created for some reason
+ {
+ LOGME1("createnewfile panic = %d",err);
+ User::Panic( _L("DiagServer unable to create DB file"),
+ DiagResultsDbCommon::EServerFileCreationError);
+ }
+
+ //This must be called, otherwise RStoreWriteStream write will fail.
+ iStore->SetTypeL( iStore->Layout() );
+ CreateEmptyRootStreamL( *iStore );
+
+ if ( iDeletionAlgorithm == ELastResultsAlgorithm )
+ {
+ CreateLastResultsBufferStreamL();
+ }
+
+ iStore->CommitL();
+
+ }
+ else
+ {
+ LOGME1("else userleave = %d",err);
+ User::Leave( err );
+ }
+ }
+
+
+// ---------------------------------------------------------------------------
+// Get central repository key that controls maximum file size.
+// ---------------------------------------------------------------------------
+//
+TInt CDiagResultsDbStore::GetMaximumFileSizeL()
+ {
+ CRepository* cr = CRepository::NewLC( KCRUidDiagnosticsResults );
+
+ TInt result = 0;
+ TInt error = cr->Get( KDiagDatabaseMaxFileSize, result );
+
+ CleanupStack::PopAndDestroy();
+
+ if ( error != KErrNone )
+ {
+ User::Leave(error);
+ }
+ return result;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Get central repository key that controls usage of the deletion algorithm.
+// ---------------------------------------------------------------------------
+//
+TInt CDiagResultsDbStore::GetDeletionAlgorithmL()
+ {
+ CRepository* cr = CRepository::NewLC( KCRUidDiagnosticsResults );
+
+ TInt result = 0;
+ TInt error = cr->Get( KDiagDatabaseDeletionAlgorithm, result );
+
+ CleanupStack::PopAndDestroy();
+
+ if ( error != KErrNone )
+ {
+ User::Leave(error);
+ }
+ return result;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Check DB file size and delete the file if it is too big.
+// Checking is controlled by a central repository key.
+//
+// Note that this function deletes the file and after that panics, because
+// the error is very serious.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CheckMaximumFileSizeLimitL( RFs& aRfs,
+ const TDesC& aFileName,
+ TInt aFileSize )
+ {
+ //Read central repository value
+ TInt maxFileSize = GetMaximumFileSizeL();
+
+ //If == 0, feature is disabled i.e. there is no limitation
+ //to the size of DB files.
+ if ( maxFileSize == 0 )
+ {
+ return;
+ }
+ else
+ {
+ if ( aFileSize > maxFileSize )
+ {
+ //Delete the file.
+ aRfs.Delete( aFileName );
+ User::Panic(_L("Maxfile Size exceeded"),
+ DiagResultsDbCommon::EMaximumFileSizeExceeded );
+ }
+ }
+ }
+
+// ---------------------------------------------------------------------------
+// Create a stream for last results.
+// The stream is created when DB file is created.
+//
+// Because this is the first stream in the PermanentFileStore,
+// we do not need to store the stream ID anywhere. Consider it as a constant.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CreateLastResultsBufferStreamL()
+ {
+ RArray<TUid>* uids = new (ELeave) RArray<TUid>;
+
+ CDiagResultsDbRecordEngineParam* params =
+ CDiagResultsDbRecordEngineParam::NewL( uids, EFalse );
+ CleanupStack::PushL( params );
+
+ TStreamId id = iStore->ExtendL();
+
+ //We assume that the permanent file store uses this as the first stream ID.
+ __ASSERT_ALWAYS( id == TStreamId( KFirstStreamId ),
+ User::Panic( _L("DiagServer Incorrect Last Results Buffer"),
+ DiagResultsDbCommon::EIncorrectExtendNumberForLastResults) );
+
+ CleanupStack::Pop();
+
+ CDiagResultsDbTestRecordHandle* handle = CDiagResultsDbTestRecordHandle::NewL(
+ id,
+ iDbUid,
+ params );
+
+ CleanupStack::PushL( handle );
+
+ WriteHandleIntoDbL( *handle ); //writes a new stream
+
+ //Do not append on to the root stream!!
+ //this should be visible only for the server.
+
+ CleanupStack::PopAndDestroy();
+
+ }
+
+// ---------------------------------------------------------------------------
+// Destructor.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbStore::~CDiagResultsDbStore()
+ {
+ if ( iRecordIds )
+ {
+ iRecordIds->Close();
+ delete iRecordIds;
+ iRecordIds = NULL;
+ }
+
+ Cancel();
+
+ delete iStore;
+ iStore = NULL;
+
+ iFile.Close();
+ iRfs.Close();
+ }
+
+// ---------------------------------------------------------------------------
+// Create a new record.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbTestRecordHandle* CDiagResultsDbStore::CreateNewRecordL(
+ CDiagResultsDbRecordEngineParam* aEngineParam )
+ {
+
+ TBool tooLowSpace = SysUtil::DiskSpaceBelowCriticalLevelL(
+ &iRfs,
+ KMininumDiskSpace,
+ EDriveC );
+ if ( tooLowSpace )
+ {
+ User::Leave( KErrDiskFull );
+ }
+
+
+
+ TStreamId id = iStore->ExtendL();
+
+ CDiagResultsDbTestRecordHandle* handle = CDiagResultsDbTestRecordHandle::NewL(
+ id,
+ iDbUid,
+ aEngineParam );
+ CleanupStack::PushL( handle );
+
+ WriteHandleIntoDbL( *handle ); //writes a new stream
+
+ AppendRootStreamL( id ); //update root stream
+
+ iStore->CommitL();
+
+ CleanupStack::Pop();
+ return handle;
+
+ }
+
+// ---------------------------------------------------------------------------
+// Write test record handle into the store.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::WriteHandleIntoDbL( CDiagResultsDbTestRecordHandle& aHandle )
+ {
+ RStoreWriteStream writestream;
+
+ writestream.ReplaceLC( *iStore, aHandle.RecordId() );
+
+ aHandle.ExternalizeL( writestream );
+
+ writestream.CommitL();
+
+ CleanupStack::PopAndDestroy( &writestream );
+ }
+
+// ---------------------------------------------------------------------------
+// Writes data into the DB file (.dat file).
+// ---------------------------------------------------------------------------
+//
+TInt CDiagResultsDbStore::CompleteTestResult( TBool aCommit,
+ CDiagResultsDbTestRecordHandle& aHandle,
+ CDiagResultsDatabaseItem& aTestItem )
+ {
+ TRAPD( err, DoCompleteTestResultL( aCommit, aHandle, aTestItem ));
+
+ return err;
+ }
+
+// ---------------------------------------------------------------------------
+// Writes handle into the DB. Test results items have been written already.
+// see CompleteTestResult.
+// ---------------------------------------------------------------------------
+//
+TInt CDiagResultsDbStore::CompleteRecord(
+ CDiagResultsDbTestRecordHandle& aHandle )
+ {
+ // ignore DRM error.
+ TRAP_IGNORE( aHandle.RecordInfo().iDrmFinishTime =
+ TDiagResultsDatabaseTestRecordInfo::DRMTimeL() );
+
+ aHandle.RecordInfo().iFinishTime.HomeTime();
+
+ TRAPD( error, DoCompleteRecordL( aHandle ));
+
+ return error;
+ }
+
+// ---------------------------------------------------------------------------
+// Writes handle into the DB. Test results items have been written already.
+// see CompleteTestResult.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::DoCompleteRecordL(
+ CDiagResultsDbTestRecordHandle& aHandle )
+ {
+
+ TBool tooLowSpace = SysUtil::DiskSpaceBelowCriticalLevelL(
+ &iRfs,
+ KMininumDiskSpace,
+ EDriveC );
+ if ( tooLowSpace )
+ {
+ User::Leave( KErrDiskFull );
+ }
+
+ WriteHandleIntoDbL( aHandle );
+
+ iStore->CommitL();
+ }
+
+
+// ---------------------------------------------------------------------------
+// Write aTestItem and aHandle into the DB. These must be in sync.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::DoCompleteTestResultL( TBool aCommit,
+ CDiagResultsDbTestRecordHandle& aHandle,
+ CDiagResultsDatabaseItem& aTestItem )
+ {
+
+ TBool tooLowSpace = SysUtil::DiskSpaceBelowCriticalLevelL(
+ &iRfs,
+ aTestItem.Size() *2,
+ EDriveC );
+ if ( tooLowSpace )
+ {
+ User::Leave( KErrDiskFull );
+ }
+
+ RStoreWriteStream writestream;
+
+ CDiagResultsDbTestRecordHandle::TTestResultHandle testresulthandle;
+
+ TBool found = aHandle.Find( aTestItem.TestUid() );
+
+
+ if ( found ) //Replace old test result
+ {
+ TStreamId id;
+ TInt error = aHandle.MatchingStreamId( aTestItem.TestUid(), id);
+ User::LeaveIfError ( error );
+ writestream.ReplaceLC( *iStore, id );
+ }
+ else //New test result
+ {
+ TStreamId newstreamid = writestream.CreateLC( *iStore );
+ testresulthandle.iStreamId = newstreamid;
+ testresulthandle.iTestUid = aTestItem.TestUid();
+ }
+
+ aTestItem.ExternalizeL( writestream );
+ writestream.CommitL();
+ CleanupStack::PopAndDestroy( &writestream );
+
+ if ( !found )
+ {
+ aHandle.AddL( testresulthandle );
+ }
+
+ WriteHandleIntoDbL( aHandle );
+
+ if ( aCommit )
+ {
+ iStore->CommitL();
+ }
+ }
+
+// ---------------------------------------------------------------------------
+// Read a test record from the DB. The record should not be ever written into
+// the same DB again. First read the number of items there are in the record.
+// After that read the items and the record info.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbTestRecord* CDiagResultsDbStore::OpenExistingRecordL( TUid aUid,
+ TBool aReadOnly )
+ {
+ TStreamId id = TStreamId ( aUid.iUid );
+
+ RStoreReadStream handlereadstream;
+ handlereadstream.OpenLC( *iStore, id );
+
+ CDiagResultsDbTestRecord* testrecord = CDiagResultsDbTestRecord::NewL( aReadOnly,
+ aUid );
+ CleanupStack::PushL ( testrecord );
+
+ CDiagResultsDbTestRecordHandle* handle =
+ CDiagResultsDbTestRecordHandle::NewL( handlereadstream );
+
+ CleanupStack::PushL ( handle );
+
+ for ( TInt i = 0; i < handle->Count(); ++i )
+ {
+ RStoreReadStream readstream;
+ readstream.OpenLC( *iStore, ((*handle)[i]) );
+
+ testrecord->AddL( CDiagResultsDatabaseItem::NewL ( readstream ) );
+
+ CleanupStack::PopAndDestroy( &readstream );
+ }
+
+ CleanupStack::PopAndDestroy(); //handle
+ CleanupStack::Pop(); //testrecord
+
+ CleanupStack::PopAndDestroy( &handlereadstream );
+
+ return testrecord;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Open last results buffer. It contains the test results that would have
+// been otherwise deleted.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbTestRecord* CDiagResultsDbStore::OpenExistingLastResultsBufferL()
+ {
+ CDiagResultsDbTestRecord* testrecord =
+ OpenExistingRecordL( TUid::Uid(KFirstStreamId),
+ ETrue );
+ return testrecord;
+ }
+
+
+void CDiagResultsDbStore::OpenExistingRecordAndHandleL( TStreamId aId,
+ CDiagResultsDbTestRecord*& aRecord,
+ CDiagResultsDbTestRecordHandle*& aHandle )
+ {
+ RStoreReadStream handlereadstream;
+ handlereadstream.OpenLC( *iStore, aId );
+
+ CDiagResultsDbTestRecord* testrecord = CDiagResultsDbTestRecord::NewL( ETrue,
+ TUid::Uid( aId.Value() ));
+ CleanupStack::PushL ( testrecord );
+
+ CDiagResultsDbTestRecordHandle* handle =
+ CDiagResultsDbTestRecordHandle::NewL( handlereadstream );
+
+ CleanupStack::PushL ( handle );
+
+ for ( TInt i = 0; i < handle->Count(); ++i )
+ {
+ RStoreReadStream readstream;
+ readstream.OpenLC( *iStore, ((*handle)[i]) );
+
+ testrecord->AddL( CDiagResultsDatabaseItem::NewL ( readstream ) );
+
+ CleanupStack::PopAndDestroy( &readstream );
+ }
+
+ CleanupStack::Pop(); //handle
+ CleanupStack::Pop(); //testrecord
+
+ aRecord = testrecord;
+ aHandle = handle;
+
+ CleanupStack::PopAndDestroy( &handlereadstream );
+
+
+ }
+
+
+// ---------------------------------------------------------------------------
+// Retrieve all record infos from the store.
+// ---------------------------------------------------------------------------
+//
+RArray<TDiagResultsDatabaseTestRecordInfo>* CDiagResultsDbStore::ExistingRecordInfosL()
+ {
+ RArray<TStreamId> ids;
+ CleanupClosePushL( ids );
+ // Find available stream ids
+ ReadRootStreamL( ids );
+
+ RArray<TDiagResultsDatabaseTestRecordInfo>* infos = new (ELeave)
+ RArray<TDiagResultsDatabaseTestRecordInfo>;
+ CleanupClosePushL( *infos );
+
+
+ for ( TInt i = 0; i < ids.Count(); ++i )
+ {
+ CDiagResultsDbTestRecordHandle* handle = OpenExistingHandleL( TUid::Uid( ids[i].Value() ));
+
+ TDiagResultsDatabaseTestRecordInfo info = handle->RecordInfo();
+
+ infos->Append( info );
+ }
+
+ CleanupStack::PopAndDestroy( &ids );
+ CleanupStack::Pop();
+
+ return infos;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Open a test result from the DB.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDatabaseItem* CDiagResultsDbStore::OpenExistingTestResultL( TStreamId aId )
+ {
+ RStoreReadStream readstream;
+ readstream.OpenLC( *iStore, aId );
+
+ CDiagResultsDatabaseItem* item = CDiagResultsDatabaseItem::NewL ( readstream );
+
+ CleanupStack::PopAndDestroy( &readstream );
+
+ return item;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Open handle from the DB. Uid matches the handle's stream Id.
+// ---------------------------------------------------------------------------
+//
+CDiagResultsDbTestRecordHandle* CDiagResultsDbStore::OpenExistingHandleL(
+ TUid aUid )
+ {
+ TStreamId id = TStreamId ( aUid.iUid );
+
+ RStoreReadStream handlereadstream;
+ handlereadstream.OpenLC( *iStore, id );
+
+
+ CDiagResultsDbTestRecordHandle* handle =
+ CDiagResultsDbTestRecordHandle::NewL( handlereadstream );
+
+ CleanupStack::PopAndDestroy( &handlereadstream );
+
+ return handle;
+ }
+
+// ---------------------------------------------------------------------------
+// Cleanup Database if there are two many test records.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CleanUpDatabaseL(TBool aCommit)
+ {
+ if ( iDeletionAlgorithm == EMaxRecordCountAlgorithm )
+ {
+ CleanUpDatabaseNoLastResultsL( aCommit );
+ }
+ else if ( iDeletionAlgorithm == ELastResultsAlgorithm )
+ {
+ CleanUpDatabaseUseLastResultsBufferL( aCommit );
+ }
+ else
+ {
+ User::Panic( _L("Unknown Deletion Algorithm"),
+ DiagResultsDbCommon::EUnknownDeletionAlgorithm);
+ }
+ }
+
+// ---------------------------------------------------------------------------
+// Cleanup Database if there are two many test records.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CleanUpDatabaseNoLastResultsL(TBool aCommit)
+ {
+ RArray<TStreamId> ids;
+ CleanupClosePushL( ids );
+ ReadRootStreamL( ids );
+
+ //Fetch the maximum number of test records from Central Repository. Panic if
+ //the value is not found. Also panic if value is less than zero.
+ TInt maxsize = 0;
+ TInt err = RDiagResultsDatabase::GetDatabaseMaximumSize( maxsize ) ;
+
+ if ( err != KErrNone || maxsize < 0 )
+ {
+ User::Panic ( _L("Diag ResultsDB panic"),
+ DiagResultsDbCommon::ECentralRepositoryFailure );
+ }
+
+ TBool cleanedUp = EFalse;
+
+ while ( ids.Count() > maxsize ) //Do cleanup
+ {
+ DeleteOldestHandleL( ids );
+
+ cleanedUp = ETrue;
+ }
+
+ if ( cleanedUp && aCommit )
+ {
+ iStore->CommitL();
+ }
+
+ CleanupStack::PopAndDestroy( &ids );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Cleanup Database if there are two many test records.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CleanUpDatabaseUseLastResultsBufferL(TBool aCommit)
+ {
+ RArray<TStreamId> ids;
+ CleanupClosePushL( ids );
+ ReadRootStreamL( ids );
+
+ //Fetch the maximum number of test records from Central Repository. Panic if
+ //the value is not found. Also panic if value is less than zero.
+ TInt maxsize = 0;
+ TInt err = RDiagResultsDatabase::GetDatabaseMaximumSize( maxsize ) ;
+
+ if ( err != KErrNone || maxsize < 0 )
+ {
+ User::Panic ( _L("Diag ResultsDB panic"),
+ DiagResultsDbCommon::ECentralRepositoryFailure );
+ }
+
+ TBool cleanedUp = EFalse;
+
+ while ( ids.Count() > maxsize ) //Do cleanup
+ {
+ TInt count = ids.Count();
+
+ ///@@@KSR: changes for Codescanner error val = High
+ //CheckOverflowingTestResults( ids );
+ CheckOverflowingTestResultsL( ids );
+
+ cleanedUp = ETrue;
+
+ __ASSERT_ALWAYS( ids.Count() == count - 1, User::Panic( _L("Diag ResultsDB panic"),
+ DiagResultsDbCommon::EUnableToDelete ) );
+ }
+
+ if ( cleanedUp && aCommit )
+ {
+ iStore->CommitL();
+ }
+
+ CleanupStack::PopAndDestroy( &ids );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Load last results buffer and add test results if needed.
+//
+// Test results are only moved to the last results buffer.
+//
+// We want to preserve only test results that have Pass/fail result.
+//
+// The algorithm.
+//
+// 1. Open the test record that is going to be removed (handle + test results)
+// 2. Open last results buffer
+// 3. Go through the test record and search for test results that have pass/fail
+// 4. If found, then check last results buffer is there a similar result.
+// 5. If similar test results are found from the last results buffer, delete the old
+// result and add the new one.
+// 6. If similar test result is not found, then append the test result into the
+// buffer.
+// 7. Finally update the last result buffer
+// 8. delete the test record and any of the test results that did not go into
+// the last results buffer.
+// ---------------------------------------------------------------------------
+//
+//void CDiagResultsDbStore::CheckOverflowingTestResults( RArray<TStreamId>& aIds )
+///@@@KSR: changes for Codescanner error val = High
+void CDiagResultsDbStore::CheckOverflowingTestResultsL( RArray<TStreamId>& aIds )
+ {
+ TStreamId id = aIds[0];
+
+ CDiagResultsDbTestRecordHandle* handleToBeRemoved = NULL;
+ CDiagResultsDbTestRecord* recordToBeRemoved = NULL;
+
+
+ OpenExistingRecordAndHandleL( id,
+ recordToBeRemoved,
+ handleToBeRemoved );
+
+ CleanupStack::PushL( handleToBeRemoved );
+ CleanupStack::PushL( recordToBeRemoved );
+
+
+ CDiagResultsDbTestRecordHandle* lastResultsBufferHandle = NULL;
+
+ lastResultsBufferHandle = OpenExistingHandleL( TUid::Uid( KFirstStreamId ) );
+
+ TInt testCount = recordToBeRemoved->Count();
+
+ TInt movedCount = 0;
+
+ CleanupStack::PushL( lastResultsBufferHandle );
+
+ for ( TInt x = 0; x < recordToBeRemoved->Count(); ++x )
+ {
+
+ CDiagResultsDatabaseItem* item = recordToBeRemoved->GetItem(x);
+
+ //We are looking for only results that are either ESuccess or
+ //EFailed.
+ if ( item->TestResult() == CDiagResultsDatabaseItem::ESuccess ||
+ item->TestResult() == CDiagResultsDatabaseItem::EFailed )
+ {
+ TBool found = lastResultsBufferHandle->Find( item->TestUid() );
+ TInt index = lastResultsBufferHandle->FindIndex( item->TestUid() );
+
+ CDiagResultsDbTestRecordHandle::TTestResultHandle resulthandle =
+ handleToBeRemoved->Get( handleToBeRemoved->FindIndex( item->TestUid() ));
+
+ if ( resulthandle.iTestUid != item->TestUid() )
+ {
+ User::Panic( _L("Diag ResultsDB panic"), DiagResultsDbCommon::EIncorrectStreamId);
+ }
+
+ if ( found )
+ {
+ //remove old result from last results buffer, because we add new one.
+ iStore->DeleteL ( lastResultsBufferHandle->Get(index).iStreamId );
+
+ lastResultsBufferHandle->RemoveL( item->TestUid() );
+ lastResultsBufferHandle->AddL( resulthandle );
+ }
+ else
+ {
+ lastResultsBufferHandle->AddL( resulthandle );
+ }
+
+ movedCount++;
+
+ handleToBeRemoved->RemoveL( item->TestUid() );
+
+ }
+ }
+
+ WriteHandleIntoDbL( *lastResultsBufferHandle );
+
+ iStore->DeleteL( aIds[0] ); //delete oldest first
+ aIds.Remove( 0 );
+
+ ReplaceRootStreamL( aIds );
+
+ //Delete test results streams as well.
+ for (TInt i=0; i < handleToBeRemoved->Count(); ++i)
+ {
+ iStore->DeleteL ( handleToBeRemoved->Get(i).iStreamId );
+ movedCount++;
+ }
+
+ if ( movedCount != testCount )
+ {
+ User::Panic( _L("EIncorrectStreamId"), DiagResultsDbCommon::EIncorrectStreamId);
+ }
+
+ CleanupStack::PopAndDestroy( lastResultsBufferHandle );
+ CleanupStack::PopAndDestroy( recordToBeRemoved );
+ CleanupStack::PopAndDestroy( handleToBeRemoved );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Deletes the oldest handle from the store.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::DeleteOldestHandleL( RArray<TStreamId>& aIds )
+ {
+ CDiagResultsDbTestRecordHandle* handle = OpenExistingHandleL(
+ TUid::Uid( aIds[0].Value() ) );
+ CleanupStack::PushL( handle );
+
+ iStore->DeleteL( aIds[0] ); //delete oldest first
+ aIds.Remove( 0 );
+
+ ReplaceRootStreamL( aIds );
+
+ //Delete test results streams as well.
+ for (TInt i=0; i < handle->Count(); ++i)
+ {
+ iStore->DeleteL ( handle->Get(i).iStreamId );
+ }
+
+ CleanupStack::PopAndDestroy( handle );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Delete handle and its test results.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::DeleteHandleL( TInt aIndex, RArray<TStreamId>& aIds )
+ {
+ CDiagResultsDbTestRecordHandle* handle = OpenExistingHandleL(
+ TUid::Uid( aIds[aIndex].Value() ) );
+ CleanupStack::PushL( handle );
+
+ iStore->DeleteL( aIds[aIndex] );
+ aIds.Remove( aIndex );
+
+ ReplaceRootStreamL( aIds );
+
+ //Delete test results streams as well.
+ for (TInt i=0; i < handle->Count(); ++i)
+ {
+ iStore->DeleteL ( handle->Get(i).iStreamId );
+ }
+
+ CleanupStack::PopAndDestroy( handle );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Reads the root stream from the DB. Root stream contains uids of the
+// test records. TUid is convertible to TStreamId and opposite.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::ReadRootStreamL( RArray<TStreamId>& aIds )
+ {
+
+ if (!iStore)
+ {
+ User::Panic( _L("Store is null"),
+ DiagResultsDbCommon::EStoreNullPointer );
+ }
+
+ RStoreReadStream readstream;
+
+ TStreamId rootId = iStore->Root();
+ readstream.OpenLC( *iStore, rootId );
+
+ TInt count = readstream.ReadInt16L();
+
+ for ( TInt i = 0; i < count; ++i )
+ {
+ TStreamId id;
+ id.InternalizeL( readstream );
+ aIds.AppendL ( id );
+
+ }
+
+ CleanupStack::PopAndDestroy( &readstream );
+ }
+
+// ---------------------------------------------------------------------------
+// Add one stream Id into the root stream.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::AppendRootStreamL( TStreamId& aId )
+ {
+ RArray<TStreamId> ids;
+ CleanupClosePushL( ids );
+
+ ReadRootStreamL( ids );
+
+ ids.Append( aId );
+
+ ReplaceRootStreamL( ids );
+
+ CleanupStack::PopAndDestroy( &ids);
+ }
+
+// ---------------------------------------------------------------------------
+// Returns TUids from the root stream.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::RecordUidsL(RArray<TUid>& aUids)
+ {
+ RArray<TStreamId> ids;
+ CleanupClosePushL ( ids );
+
+ ReadRootStreamL( ids );
+
+ for (TInt i = 0; i < ids.Count(); ++i)
+ {
+ aUids.Append( TUid::Uid( ids[i].Value() ));
+ }
+
+ CleanupStack::PopAndDestroy( &ids );
+ }
+
+
+// ---------------------------------------------------------------------------
+// Replace the current root stream with a new array.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::ReplaceRootStreamL( RArray<TStreamId>& aArray )
+ {
+ RStoreWriteStream writestream;
+ writestream.ReplaceLC( *iStore, iStore->Root() );
+
+ TInt count = aArray.Count();
+
+ writestream.WriteInt16L( count );
+
+ for ( TInt i = 0; i < count; ++i )
+ {
+ writestream << aArray[i];
+ }
+
+ writestream.CommitL();
+
+ CleanupStack::PopAndDestroy( &writestream );
+ }
+
+// ---------------------------------------------------------------------------
+// Empty root stream is needed when DB file is created for the first time.
+// ---------------------------------------------------------------------------
+//
+void CDiagResultsDbStore::CreateEmptyRootStreamL(CPermanentFileStore& aStore)
+ {
+ RStoreWriteStream writestream;
+
+ TStreamId id = writestream.CreateLC( *iStore );
+ writestream.WriteInt16L( 0 ); // stream id count = 0
+
+ writestream.CommitL();
+
+ aStore.SetRootL( id );
+
+ aStore.CommitL();
+
+ CleanupStack::PopAndDestroy( &writestream );
+ }
+
+