devicediagnosticsfw/diagresultsdb/server/src/diagresultsdbsession.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 09:07:52 +0200
changeset 0 b497e44ab2fc
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* 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 <s32mem.h>

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<TUid> 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<TUid> uids;
        CleanupClosePushL ( uids );
        iStore->RecordUidsL( uids );
        
        TPckgBuf<TUint> 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<CDiagResultsDbTestRecordSubsession*>(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<TUid> uidpckg;
	aMessage.Read(0, uidpckg);
    TUid recordUid = uidpckg(); 
    
    TPckgBuf<TBool> 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<TUid> 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<TInt> 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<TUid> uids;
    CleanupClosePushL ( uids );
    iStore->RecordUidsL( uids );
    
    if ( uids.Count() == 0 )
        {
        User::Leave( KErrNotFound );
        }
    
    TPckgBuf<TUid> 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<TUid> 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<TUid> 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<TUid> 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<TDiagResultsDatabaseTestRecordInfo>* array = new (ELeave) 
        CArrayFixFlat<TDiagResultsDatabaseTestRecordInfo>(KArrayGranuality);
    CleanupStack::PushL (array);
    
    RArray<TUid> 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<CDiagResultsDbTestRecord>* 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<TUid> 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<CDiagResultsDbTestRecord>& aArray )
    {
    
	CArrayFixFlat<TUid>* uidArray = new (ELeave) 
	                                    CArrayFixFlat<TUid>(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<CDiagResultsDatabaseItem>;
	    	    
	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<TUid>& aUidArray,                           
                         RPointerArray<CDiagResultsDatabaseItem>& 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<TUid>& aUidArray, 
                         RPointerArray<CDiagResultsDbTestRecord>& aTestRecordArray, 
                         RPointerArray<CDiagResultsDatabaseItem>& 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<CDiagResultsDbTestRecord>* 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;  
    }