/*
* Copyright (c) 2002-2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: Implements the double buffering functionality in the DownloadManager
*
*/
#include <bldvariant.hrh>
#include "FileExt.h"
#include "HttpClientApp.h"
#include "HttpDownload.h"
#include "HttpStorage.h"
#include "BuffStorage.h"
#include "HttpDownloadManagerServerEngine.h"
#include "HttpDownloadMgrLogger.h"
#include "HeaderField.h"
#include <SysUtil.h>
#include <DocumentHandler.h>
#include <APMSTD.H>
#include "HttpDownloadMgrLogger.h"
#ifdef __SYNCML_DM_FOTA
#include <fotaengine.h>
#endif
#ifdef RD_MULTIPLE_DRIVE
#include <driveinfo.h>
#endif //RD_MULTIPLE_DRIVE
// EXTERNAL DATA STRUCTURES
//extern ?external_data;
// EXTERNAL FUNCTION PROTOTYPES
//extern ?external_function( ?arg_type,?arg_type );
// CONSTANTS
//const ?type ?constant_var = ?constant;
// ---------------------------------------------------------
// CBuffStorage::CBuffStorage
// ---------------------------------------------------------
//
CBuffStorage::CBuffStorage( CHttpStorage* aHttpStorage )
:CActive( EPriorityHigh ),
iFile(aHttpStorage->iFile),
iDownloadedSize(aHttpStorage->iDownloadedSize),
iBufferedSize(aHttpStorage->iBufferedSize),
iHttpStorageBufferSize(aHttpStorage->iBufferSize),
iHttpStorageLength(aHttpStorage->iLength),
iBufferingEnabled(aHttpStorage->iBufferingEnabled),
iProgressiveDownload(aHttpStorage->iProgressiveDownload)
#ifdef __SYNCML_DM_FOTA
,iFotaStream(aHttpStorage->iFotaStream)
#endif // __SYNCML_DM_FOTA
{
}
// -----------------------------------------------------------------------------
// CBuffStorage::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CBuffStorage::ConstructL()
{
LOGGER_ENTERFN( "ConstructL" );
CActiveScheduler::Add( this );
iWait = new (ELeave) CActiveSchedulerWait;
}
// -----------------------------------------------------------------------------
// CBuffStorage::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CBuffStorage* CBuffStorage::NewL( CHttpStorage* aHttpStorage )
{
CBuffStorage* self = new( ELeave ) CBuffStorage( aHttpStorage );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop();
return self;
}
// ---------------------------------------------------------
// CBuffStorage::~CBuffStorage
// ---------------------------------------------------------
//
CBuffStorage::~CBuffStorage()
{
Cancel();
ResetBuffers();
delete iWritePtr; iWritePtr = 0;
if(iWait)
{
delete iWait;
iWait = NULL;
}
}
// ---------------------------------------------------------
// CBuffStorage::RunL
// ---------------------------------------------------------
//
void CBuffStorage::RunL()
{
LOGGER_ENTERFN( "RunL" );
// Save the error code
iLastWriteErrorCode = iStatus.Int();
if(iLastWriteErrorCode==KErrNone && iWritePtr)
{
// Update how much was written on the file
iDownloadedSize += iWritePtr->Length();
CLOG_WRITE_2( "(%08X) CBuffStorage::RunL: Async write finished, downloaded now: %d", this, iDownloadedSize);
}
else
{
CLOG_WRITE_2( "(%08X) CBuffStorage::RunL DH-iStat: %d, ", this, iStatus.Int() );
}
if(iWait && iWait->IsStarted())
{
CLOG_WRITE_1 ( "(%08X) CBuffStorage::RunL() Stopping iWait", this );
iWait->AsyncStop();
}
}
void CBuffStorage::DoCancel()
{
CLOG_WRITE_1("(%08X) CBuffStorage::DoCancel", this);
// This is ok, CActive::Cancel always waits for the async operation to finish in this case
iLastWriteErrorCode = KErrCancel;
}
// ---------------------------------------------------------
// CBuffStorage::ResetBuffer
// ---------------------------------------------------------
//
void CBuffStorage::ResetBuffers()
{
CLOG_WRITE_1("(%08X) CBuffStorage::ResetBuffers >>", this);
if(IsActive()&& iWait && !iWait->IsStarted())
{
// Make sure async writes are finished
iWait->Start();
}
// Cleanup
delete iBuff1; iBuff1 = NULL;
delete iBuff2; iBuff2 = NULL;
iClientBuffer = NULL;
iBufferSize = 0;
CLOG_WRITE_1("(%08X) CBuffStorage::ResetBuffers <<", this);
}
// ---------------------------------------------------------
// CBuffStorage::WriteOutNextBodyDataL
// ---------------------------------------------------------
//
TBool CBuffStorage::WriteOutNextBodyDataL( const TDesC8& aBuf )
{
TBool retVal( ETrue );
if( iFile )
{
if( iBufferingEnabled )
{
DoBufferingWriteL(aBuf);
}
else
{
DoNonbufferingWriteL(aBuf);
}
}
#ifdef __SYNCML_DM_FOTA
else if( iFotaStream )
{
iFotaStream->WriteL( aBuf );
}
#endif // __SYNCML_DM_FOTA
if( iHttpStorageLength != KDefaultContentLength &&
iBufferedSize > iHttpStorageLength )
// we don't know actually how many bytes will come down.
{
CLOG_WRITE_1( "(%08X) CBuffStorage::WriteOutNextBodyDataL: Length modified", this);
iHttpStorageLength = KDefaultContentLength;
retVal = EFalse;
}
return retVal;
}
// ---------------------------------------------------------
// CBuffStorage::FlushBuffersL
// ---------------------------------------------------------
//
void CBuffStorage::FlushBuffersL()
{
if(!iFile)
{
return;
}
CLOG_WRITE_1("(%08X) CBuffStorage::FlushBuffersL >>", this);
// Make sure async writes are finished before doing anything
if(IsActive() && iWait && !iWait->IsStarted())
{
CLOG_WRITE_1("(%08X) CBuffStorage::FlushBuffersL: stalling >>", this);
iWait->Start();
CLOG_WRITE_1("(%08X) CBuffStorage::FlushBuffersL: stalling <<", this);
}
if(iLastWriteErrorCode != KErrNone)
{
CLOG_WRITE_2("(%08X) CBuffStorage::FlushBuffersL: last error = %d", this, iLastWriteErrorCode);
TInt err = iLastWriteErrorCode;
iLastWriteErrorCode = KErrNone;
User::Leave(err);
}
if(iClientBuffer)
{
// Flush whatever is left on the client buffer
TInt len = iClientBuffer->Length();
CLOG_WRITE_2("(%08X) CBuffStorage::FlushBuffersL: %d bytes", this, len);
if(len > 0)
{
// Write to file
TPtr8 des(iClientBuffer->Des());
User::LeaveIfError(iFile->Write(des));
// Update how much was written to file
iDownloadedSize += len;
des.Zero();
}
}
CLOG_WRITE_1("(%08X) CBuffStorage::FlushBuffersL <<", this);
}
// ---------------------------------------------------------
// CBuffStorage::DoBufferingWriteL
// ---------------------------------------------------------
//
void CBuffStorage::DoBufferingWriteL(const TDesC8& aBuf)
{
if(iBufferSize != iHttpStorageBufferSize)
{
CLOG_WRITE_3("(%08X) CBuffStorage::DoBufferingWriteL: Allocate %d - downloaded %d", this, iHttpStorageBufferSize, iBufferedSize);
// Make sure nothing is leaked
delete iBuff1; iBuff1 = 0;
delete iBuff2; iBuff2 = 0;
iBufferSize = 0;
// Allocate buffers
iBuff1 = HBufC8::NewL(iHttpStorageBufferSize);
iBuff2 = HBufC8::NewL(iHttpStorageBufferSize);
// Mark that we now have buffers that are iBufferSize big
iBufferSize = iHttpStorageBufferSize;
// Set client to use buffer 1
iClientBuffer = iBuff1;
}
// Get incoming data pointer and size
const TUint8* src = aBuf.Ptr();
TInt incomingBuffSize = aBuf.Length();
// Process the data in blocks
while(incomingBuffSize)
{
// Get pointer to current client buffer data (at start of loop since iClientBuffer can change)
TPtr8 buff = iClientBuffer->Des();
TInt buffLength = buff.Length();
// How much space in the buffer
TInt toFillUp = iBufferSize - buffLength;
// Check if we can just fit everything in the current buffer
if(incomingBuffSize < toFillUp)
{
// Dump data on the current client buffer since ample space exists
buff.Append(src, incomingBuffSize);
// Update the 'received' counter on how much we got from air
iBufferedSize += incomingBuffSize;
CLOG_WRITE_4("(%08X) CBuffStorage::DoBufferingWriteL: Copied %d, now %d / %d", this, incomingBuffSize, buff.Length(), iBufferSize);
// And break out of the loop since we're finished for this round
break;
}
// At this point we have data that either fills or overflows the buffer so append as much as fits
buff.Append( src, toFillUp );
// Update the 'received' counter on how much we copied
iBufferedSize+=toFillUp;
CLOG_WRITE_4("(%08X) CBuffStorage::DoBufferingWriteL: Copied %d, now %d / %d", this, toFillUp, buff.Length(), iBufferSize);
// Subtract how much we copied from the loop counter and advance source data pointer
incomingBuffSize -= toFillUp;
src+=toFillUp;
// Now we have a full client buffer, better do something with it
// Check if previous async write is still ongoing
// Done here so if somebody switched on progressive download midway through we don't mix buffers
if(IsActive()&& iWait && !iWait->IsStarted())
{
CLOG_WRITE_1("(%08X) CBuffStorage::DoBufferingWriteL: stalling >>", this);
iWait->Start();
CLOG_WRITE_1("(%08X) CBuffStorage::DoBufferingWriteL: stalling <<", this);
}
// In case of async writes we have to check if there was error previously
if(iLastWriteErrorCode != KErrNone)
{
// Clear the error code and leave with it
TInt err = iLastWriteErrorCode;
iLastWriteErrorCode = KErrNone;
User::Leave(err);
}
if(iProgressiveDownload)
{
CLOG_WRITE_1("(%08X) CBuffStorage::DoBufferingWriteL: Buffer full -> Progressive download", this);
TInt len = buff.Length();
// Progressive download -> just do a synchronous write and update counter
User::LeaveIfError(iFile->Write(buff));
// Update how much was written to file (sync write)
iDownloadedSize += len;
// Clear out client buffer
buff.Zero();
// Continue for next write
continue;
}
// We must keep the descriptor alive until async operations have finished
if(!iWritePtr)
{
iWritePtr = new (ELeave) TPtr8(iClientBuffer->Des());
}
else
{
iWritePtr->Set(iClientBuffer->Des());
}
#ifdef _DEBUG
// Print out file position so we can check alignments
TInt currentPos(0);
iFile->Seek(ESeekCurrent, currentPos);
CLOG_WRITE_3("(%08X) CBuffStorage::DoBufferingWriteL - Start async write %d bytes, filepos=%d", this, iWritePtr->Length(), currentPos);
#endif
// Start the async write and set AO active
iFile->Write( *iWritePtr, iStatus ) ;
SetActive();
// Swap buffer pointers (we can't use the same buffer here
// since it is not known when the buffer can be used again)
if(iClientBuffer == iBuff1)
{
iClientBuffer = iBuff2;
}
else
{
iClientBuffer = iBuff1;
}
// Make sure the new client buffer is empty
iClientBuffer->Des().Zero();
}
}
// ---------------------------------------------------------
// CBuffStorage::DoNonbufferingWriteL
// ---------------------------------------------------------
//
void CBuffStorage::DoNonbufferingWriteL(const TDesC8& aBuf)
{
CLOG_WRITE_2("(%08X) CBuffStorage::DoNonbufferingWriteL: %d bytes", this, aBuf.Length());
if(IsActive() && iWait && !iWait->IsStarted())
{
CLOG_WRITE_1("(%08X) CBuffStorage::DoNonbufferingWriteL: stalling >>", this);
iWait->Start();
CLOG_WRITE_1("(%08X) CBuffStorage::DoNonbufferingWriteL: stalling <<", this);
}
TInt len = aBuf.Length();
if(len)
{
User::LeaveIfError( iFile->Write( aBuf ) );
if( iProgressiveDownload )
{
User::LeaveIfError( iFile->Flush() );
}
// Update how much received from air
iBufferedSize += len;
// Update how much written to file
iDownloadedSize += len;
}
}
// End of File