diff -r 000000000000 -r b497e44ab2fc devicediagnosticsfw/diagresultsdb/server/src/diagresultsdbstore.cpp --- /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 +#include +#include +#include + +#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; + iRecordIds = new (ELeave) RArray; + // 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* uids = new (ELeave) RArray; + + 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* CDiagResultsDbStore::ExistingRecordInfosL() + { + RArray ids; + CleanupClosePushL( ids ); + // Find available stream ids + ReadRootStreamL( ids ); + + RArray* infos = new (ELeave) + RArray; + 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 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 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& aIds ) +///@@@KSR: changes for Codescanner error val = High +void CDiagResultsDbStore::CheckOverflowingTestResultsL( RArray& 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& 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& 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& 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 ids; + CleanupClosePushL( ids ); + + ReadRootStreamL( ids ); + + ids.Append( aId ); + + ReplaceRootStreamL( ids ); + + CleanupStack::PopAndDestroy( &ids); + } + +// --------------------------------------------------------------------------- +// Returns TUids from the root stream. +// --------------------------------------------------------------------------- +// +void CDiagResultsDbStore::RecordUidsL(RArray& aUids) + { + RArray 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& 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 ); + } + +