diff -r 000000000000 -r 3553901f7fa8 cellularsrvapitest/datatransferhaitest/common/src/DataDealer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cellularsrvapitest/datatransferhaitest/common/src/DataDealer.cpp Tue Feb 02 01:41:59 2010 +0200 @@ -0,0 +1,341 @@ +/* +* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#include +#include +#include + +#include "DataDealer.h" +#include "DataDealerParams.h" + +// We cannot use UIDs that are reserved from Symbian (neither protected +// nor unprotected) since Publish&Subscribe properties require that +// the running process they are defined in has the secure id that matches +// the id of the P&S property (secure id check is not made for legacy UIDs +// which are < 0x10273357). +// So we use a UID from development range: 0x00000000 - 0x0FFFFFFF +const TUid KPropertyCategory = { 0x077A5355 }; +//const TUid KPropertyCategory = { 0xA00023E0 }; + +// Time to wait before re-checking property values +const TInt KWaitForPropertyTime( 100000 ); + + +const TUid KDataDealerImplUID1 = {0xA00023E2}; + +/** + * @param aProducer Set to ETrue if this is a producer instance. + * @param aKey The Publish&Subscribe key that is used for data passing. + */ +CDataDealer* CDataDealer::NewL( const TBool aProducer, const TUint aKey ) + { + TDataDealerParams params( aProducer, aKey ); + + TAny* implementation = + REComSession::CreateImplementationL( KDataDealerImplUID1, + _FOFF( CDataDealer, iDtorID ), + ¶ms ); + + + CDataDealer* self = new (ELeave) CDataDealer( params.iProducer, params.iKey ); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + + return self; + } + +CDataDealer::~CDataDealer() + { + // wait that the property is empty (= data consumed) + TRAPD( err, WaitForConsumerL() ); + if( err != KErrNone ) + { + // WaitForConsumerL error! + } + + // The producer is responsible of creating and destroying the property + if( iProducer ) + { + // CDataDealer: This is a producer instance, delete the P&S property + TInt err = iProperty.Delete( KPropertyCategory, iKey ); + if( err != KErrNone ) + { + // CDataDealer: Could not delete property + } + iProperty.Close(); + } + + + // Notify the ECom server that we released one implementation of some + // DLL. When the counter hits zero ECom server may unload the DLL. + REComSession::DestroyedImplementation( iDtorID ); + } + +void CDataDealer::ConstructL() + { + // The producer is responsible of creating and destroying the property + if( iProducer ) + { + // CDataDealer: This is a producer instance, create the P&S property + TInt err = iProperty.Define( KPropertyCategory, + iKey, + RProperty::EByteArray, + iPropertySize ); + if( err == KErrAlreadyExists ) + { + // Property already exists. Clear contents. + User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); + } + else if( err != KErrNone ) + { + // Could not create pub&sub property + User::Leave( err ); + } + } + // Consumer waits that the property is created by producer + else + { + WaitForPropertyToBeCreatedL(); + } + } + + +/** + * Consumer uses this to wait for the property to be created by producer + * when DataDealer is constructed. + */ +void CDataDealer::WaitForPropertyToBeCreatedL() + { + const TInt KTempPropertyValueLength(2); + TBuf8 tempValue; + + TInt err = iProperty.Get( KPropertyCategory, iKey, tempValue ); + while( err == KErrNotFound ) + { + // Property did not created yet, wait + const TInt KWaitForProperty( 500000 ); + // User::After here is not a critical error. It is used here to wait for the RProperty instance + // to be created. The best solution would be to use RProperty notifications to do that but it is + // currently too heavy considering how DataDealer is used: in testing context without any UI + User::After( KWaitForProperty ); + err = iProperty.Get( KPropertyCategory, iKey, tempValue ); + } + if( err != KErrNone && err != KErrOverflow ) + { + // Unexpected error! + User::Leave( err ); + } + } + +CDataDealer::CDataDealer( const TBool aProducer, const TUint aKey ) +: CBase(), + // Use KMaxPropertySize as default size to ensure "real time" setting of a value + iPropertySize( RProperty::KMaxPropertySize ), + iProducer( aProducer ), + iKey( aKey ), + iDtorID() + { + } + +/** + * Produce data to the data storage defined in the constructor. + */ +void CDataDealer::ProduceDataL( const TDesC8& aData ) + { + // "CDataDealer::ProduceDataL: %S", &aData + + // Check for misuse + if( !iProducer ) User::Leave( KErrNotSupported ); + + for( TInt i(0); i < aData.Length(); i += iPropertySize ) + { + // wait that the property is empty (= data consumed) + WaitForConsumerL(); + + // Then write data block-by-block + TPtrC8 dataBlock( KNullDesC8 ); + if( ( i + iPropertySize ) < aData.Length() ) + dataBlock.Set( aData.Mid( i, iPropertySize ) ); + else + dataBlock.Set( aData.Mid( i, aData.Length() - i ) ); + // Write single block + User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, dataBlock ) ); + } + } + +/** + * Wait synchronously until the consumer has deleted the data we have produced. + * Used by data dealer that is producing data. + */ +void CDataDealer::WaitForConsumerL() + { + const TInt KTempPropertyValueLength(2); + TBuf8 tempValue; + + TInt err = iProperty.Get( KPropertyCategory, iKey, tempValue ); + + if( err != KErrOverflow && err != KErrNone ) + { + // un-expected error + // CDataDealer: Could not get property value. + User::Leave( err ); + } + + while( err == KErrOverflow || tempValue != KNullDesC8 ) + { + // Not empty yet, wait + + // User::After here is not a critical error. It is used here to wait for the RProperty instance + // to be created. The best solution would be to use RProperty notifications to do that but it is + // currently too heavy considering how DataDealer is used: in testing context without any UI + User::After( KWaitForPropertyTime ); // CSI: 92 # + err = iProperty.Get( KPropertyCategory, iKey, tempValue ); + } + + } + +/** + * @param aData On return contains a pointer to heap descriptor containing the received data. + */ +void CDataDealer::ConsumeDataL( HBufC8*& aData ) + { + // Check for misuse + if( iProducer ) User::Leave( KErrNotSupported ); + + TInt err( KErrNone ); + HBufC8* data = HBufC8::NewLC( iPropertySize ); + TPtr8 dataPtr = data->Des(); + const TInt KMinHBufCLength(1); + HBufC8* wholeData = HBufC8::NewLC( KMinHBufCLength ); + + // Loop until the property is destroyed by the producer + for( TInt i(0); err != KErrNotFound; ) + { + // check if the property exists and has data + err = iProperty.Get( KPropertyCategory, iKey, dataPtr ); + TBool empty = ( dataPtr == KNullDesC8 ); + if( err == KErrNone ) + { + if( empty ) + { + // Property empty, cannot consume, wait + // If the property exists but is empty, wait for data + // User::After here is not a critical error. It is used here to wait for the RProperty instance + // to be created. The best solution would be to use RProperty notifications to do that but it is + // currently too heavy considering how DataDealer is used: in testing context without any UI + User::After( KWaitForPropertyTime ); // CSI: 92 # + } + else + { + // Clear the property... + User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); + + // Append the retrieved data to the buffer that will be returned + // CDataDealer: Data ready, consumed + HBufC8* newWholeData = wholeData->ReAllocL( wholeData->Length() + iPropertySize ); + CleanupStack::Pop( wholeData ); + wholeData = newWholeData; + CleanupStack::PushL( wholeData ); + wholeData->Des().Append( *data ); + // Clear the buffer + dataPtr.Zero(); + + // ..and set new storage index + i += iPropertySize; + } + } + // If the property is not found, it means that there is (no more) data to be consumed available + else if( err != KErrNotFound ) + { + // CDataDealer: Could not open the property. + User::Leave( err ); + } + } + + // "CDataDealer::ConsumeDataL: %S", wholeData + // Ownership transferred + CleanupStack::Pop( wholeData ); + aData = wholeData; + CleanupStack::PopAndDestroy( data ); + } + +/** + * Consume data that is produced to the data storage defined in the constructor. + * @param aData On return contains a pointer to heap descriptor containing the received data. + * @param aMaxSize The maximum size of the data block. + * @param aMore ETrue if there is more data to be consumed. + */ +void CDataDealer::ConsumeDataL( HBufC8*& aData, const TInt aMaxSize, TBool& aMore ) + { + // Check for misuse + if( iProducer ) User::Leave( KErrNotSupported ); + + HBufC8* data = HBufC8::NewLC( iPropertySize ); + TPtr8 dataPtr = data->Des(); + + // Loop until data is produced to the property + TInt err( KErrNone ); + while( err != KErrNotFound && dataPtr.Length() == 0 ) + { + // check if the property exists and has data + err = iProperty.Get( KPropertyCategory, iKey, dataPtr ); + TBool empty = ( dataPtr == KNullDesC8 ); + + if( err == KErrNone ) + { + if( empty ) + { + // If the property exists but is empty, wait for data + // User::After here is not a critical error. It is used here to wait for the RProperty instance + // to be created. The best solution would be to use RProperty notifications to do that but it is + // currently too heavy considering how DataDealer is used: in testing context without any UI + User::After( KWaitForPropertyTime ); // CSI: 92 # + } + else if( dataPtr.Length() > aMaxSize ) + { + // Too long data (%i, max: %i): consuming only the first block", dataPtr.Length(), aMaxSize + // Consuming only the first aMaxSize characters; save the rest of the data to the property + iProperty.Set( KPropertyCategory, iKey, dataPtr.Right( dataPtr.Length() - aMaxSize ) ); + // Take the first aMaxSize characters and ignore the rest + data->Des().Copy( dataPtr.Left( aMaxSize ) ); + } + else + { + // Clear the property... + User::LeaveIfError( iProperty.Set( KPropertyCategory, iKey, KNullDesC8 ) ); + } + } + // If the property is not found, it means that there is (no more) data to be consumed available + else if( err == KErrNotFound ) + { + // CDataDealer: No more data in the property (KErrNotFound) + aMore = EFalse; + } + else + { + // CDataDealer: Could not open the property. + aMore = EFalse; + User::Leave( err ); + } + } + + CleanupStack::Pop( data ); + aData = data; + } +