smartinstaller/adm/src/ADMDownloadHandler.cpp
branchADM
changeset 48 364021cecc90
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/smartinstaller/adm/src/ADMDownloadHandler.cpp	Wed Jun 30 11:01:26 2010 +0530
@@ -0,0 +1,608 @@
+/*
+* Copyright (c) 2009-2010 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: 
+*     CDownloadHandler implementation
+*
+*
+*/
+
+
+#include <aknappui.h>
+#include <COEUTILS.H>
+#include <wlanerrorcodes.h> //For download error handling
+#include <etelpckt.h>       //For download error handling
+#include <exterror.h>       //For download error handling
+#include <httpdownloadmgrcommon.h> // For download error handling (not in S60 5.0 SDK)
+
+#include "ADM.hrh"
+#include "ADMDownloadHandler.h"
+#include "ADMAppUi.h"
+#include "networkstatuslistener.h"
+
+#include "globals.h"
+#include "macros.h"
+/*
+#ifdef USE_LOGFILE
+#include "debug.h"
+#undef LOG
+#undef LOG2
+#undef LOG3
+#undef LOG4
+#undef LOG5
+#undef LOG6
+#undef LOG7
+#undef LOG8
+#undef LOG8_2
+
+#define LOG( aMsg ) { _LIT(KMsg, aMsg); iAppUi.iLog.Write( KMsg ); RDebug::Print( KMsg ); }
+#define LOG2( aMsg, aParam1 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1 ); RDebug::Print( KMsg, aParam1 ); }
+#define LOG3( aMsg, aParam1, aParam2 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2 ); RDebug::Print( KMsg, aParam1, aParam2 ); }
+#define LOG4( aMsg, aParam1, aParam2, aParam3 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2, aParam3 ); RDebug::Print( KMsg, aParam1, aParam2, aParam3 ); }
+#define LOG5( aMsg, aParam1, aParam2, aParam3, aParam4 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2, aParam3, aParam4 ); RDebug::Print( KMsg, aParam1, aParam2, aParam3, aParam4 ); }
+#define LOG6( aMsg, aParam1, aParam2, aParam3, aParam4, aParam5 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5 ); RDebug::Print( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5 ); }
+#define LOG7( aMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6 ); RDebug::Print( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6 ); }
+#define LOG8( aMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6, aParam7 ) { _LIT(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6, aParam7 ); RDebug::Print( KMsg, aParam1, aParam2, aParam3, aParam4, aParam5, aParam6, aParam7 ); }
+#define LOG8_2( aMsg, aParam1 ) { _LIT8(KMsg, aMsg); iAppUi.iLog.WriteFormat( KMsg, aParam1 ); RDebug::Print(_L("RDebug _L8() at line %d"), __LINE__); }
+#endif
+*/
+#ifdef USE_LOGFILE
+// Logging version
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+CDownloadHandler* CDownloadHandler::NewL(MDownloadClient* aDlClient, RFileLogger& aLogger, const TBool aMasterInstance)
+	{
+	CDownloadHandler* object = new ( ELeave ) CDownloadHandler(aDlClient, aLogger, aMasterInstance);
+	CleanupStack::PushL( object );
+	object->ConstructL();
+	CleanupStack::Pop();
+	return object;
+	}
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+CDownloadHandler::CDownloadHandler(MDownloadClient* aDlClient, RFileLogger& aLogger, const TBool aMasterInstance) :
+	iLog(aLogger),
+	iDlClient(aDlClient),
+	iMasterInstance(aMasterInstance),
+	iPtrToResponseBodyFileName(0,0)
+	{
+	}
+#else
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+CDownloadHandler* CDownloadHandler::NewLC(MDownloadClient* aDlClient, const TBool aMasterInstance)
+	{
+	CDownloadHandler* object = new ( ELeave ) CDownloadHandler(aDlClient, aMasterInstance);
+	CleanupStack::PushL( object );
+	object->ConstructL();
+	return object;
+	}
+
+CDownloadHandler* CDownloadHandler::NewL(MDownloadClient* aDlClient, const TBool aMasterInstance)
+	{
+	CDownloadHandler* object = CDownloadHandler::NewLC(aDlClient, aMasterInstance);
+	CleanupStack::Pop();
+	return object;
+	}
+
+CDownloadHandler::CDownloadHandler(MDownloadClient* aDlClient, const TBool aMasterInstance) :
+	iDlClient(aDlClient),
+	iMasterInstance(aMasterInstance),
+	iPtrToResponseBodyFileName(0,0)
+	{
+	}
+#endif
+
+// ---------------------------------------------------------------------------
+// Default destructor
+// ---------------------------------------------------------------------------
+//
+CDownloadHandler::~CDownloadHandler()
+	{
+	DELETE_IF_NONNULL( iUrl );
+	DELETE_IF_NONNULL( iResponseBodyFileName );
+
+	delete iNetworkStatusListener;
+	iDownloadManager.DeleteAll();
+	iDownloadManager.Close();
+	}
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+void CDownloadHandler::ConstructL()
+	{
+//	TBool masterInstance(!(iADMAppUiObj->EikonEnv()->StartedAsServerApp()));
+	const TUid uid = {_UID3};
+	iDownloadManager.ConnectL(uid , *this, iMasterInstance );
+	iNetworkStatusListener = CNetworkStatusListener::NewL( *this );
+	}
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+void CDownloadHandler::SetIAP(TUint32 aIAP)
+	{
+	iIAP = aIAP;
+	if ( iIAP > 0)
+		{
+		LOG2( "CDownloadHandler::SetIap(): %d", aIAP );
+		// Set the download manager to silent mode
+		iDownloadManager.SetBoolAttribute( EDlMgrSilentMode, ETrue );
+		iDownloadManager.SetIntAttribute( EDlMgrIap, iIAP );
+		iNetworkStatusListener->SetIapIdToMonitor(iIAP);
+		}
+	}
+
+#ifdef USE_LOGFILE
+void CDownloadHandler::Log(TRefByValue<const TDesC16> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, list);
+	iLog.Write(buf);
+	}
+#endif
+
+// -----------------------------------------------------------------------------
+// Callback for CNetworkStatusObserver
+//
+// Called when the connection status changes
+// -----------------------------------------------------------------------------
+//
+void CDownloadHandler::HandleNetworkStatusChangedL(const TInt aStatus)
+	{
+	LOG2( "+ HandleNetworkStatusChangedL(): %d", aStatus );
+
+	// Did we lose the network connection?
+	if (aStatus == EConnMonStatusNotAvailable)
+		{
+		iNetworkLost = ETrue;
+		}
+
+	//LOG2( "- HandleNetworkStatusChangedL(): %d", iNetworkLost );
+	}
+
+// -----------------------------------------------------------------------------
+// Processes download errors and sets aDownloadError
+//
+// @param aGlErrId Global error ID from download manager.
+// @param aErrId Error ID from download manager.
+// @param aDownloadError Reference to the download error variable function is
+//      going to set in case of known error.
+// -----------------------------------------------------------------------------
+//
+TBool CDownloadHandler::ProcessDlErrors(const TInt32 aGlErrId, const TInt32 aErrId, EDownloadError& aDownloadError)
+	{
+	if (iFetchType && (aGlErrId || aErrId))
+		LOG4( "+ ProcessDlErrors(%d, %d, %d)", aGlErrId, aErrId, aDownloadError );
+	TBool requestRestart = EFalse;
+	// Process the error codes
+	switch (aErrId)
+		{
+		case KErrNone:
+			// everything a-OK!
+			break;
+		case EConnectionFailed:
+			aDownloadError = EDlErrDownloadFailure;
+			break;
+		case ETransactionFailed:
+			// We set the download error to network failure as that's the
+			// error propagated to TState::DownloadFailed().
+			// The state machine will try to restart the download, if the
+			// error code is EDlErrDownloadFailure, but NOT for EDlErrNetworkFailure.
+			aDownloadError = EDlErrDownloadFailure;
+			requestRestart = ETrue;
+			break;
+		case EObjectNotFound:
+			aDownloadError = EDlErrFileNotFound;
+			break;
+		default:
+			LOG2( "Unhandled ErrId %d", aErrId );
+			aDownloadError = EDlErrDownloadFailure;
+			break;
+		}
+	// Process the global error code
+	switch (aGlErrId)
+		{
+		case KErrNone:
+			// everything a-OK!
+			break;
+		case KErrCancel:
+		case KErrAbort:
+			// The user has cancelled the download / IAP selection box
+			aDownloadError = EDlErrCancelled;
+			break;
+		case KErrDiskFull:
+			// Disk full
+			aDownloadError = EDlErrNotEnoughSpace;
+			break;
+		case KErrUnknown:
+			// act on KErrUnknown only, if error ID is not set
+			if (aErrId == KErrNone)
+				{
+				aDownloadError = EDlErrDownloadFailure;
+				requestRestart = ETrue;
+				}
+			break;
+		case KErrNotFound:
+		case KErrBadName:
+		case KErrNotSupported:
+		case KErrCommsLineFail:
+		case KErrTimedOut:
+		case KErrCouldNotConnect:
+		case KErrDisconnected:
+		case KErrGprsServicesNotAllowed:
+		case KErrGsmMMNetworkFailure:
+		// WLAN network related errors:
+		case KErrWlanNetworkNotFound:
+		case KErrWlanRoamingFailed:
+		case KErrWlanNetworkLost:
+#if 0
+		case KErrBadName:
+		case KErrNotSupported:
+		case KErrWlanOpenAuthFailed:
+		case KErrWlanSharedKeyAuthRequired:
+		case KErrWlanSharedKeyAuthFailed:
+		case KErrWlanWpaAuthRequired:
+		case KErrWlanIllegalEncryptionKeys:
+		case KErrWlanPskModeRequired:
+		case KErrWlanEapModeRequired:
+		case KErrWlanSimNotInstalled:
+		case KErrWlanNotSubscribed:
+		case KErrWlanAccessBarred:
+		case KErrWlanPasswordExpired:
+		case KErrWlanNoDialinPermissions:
+		case KErrWlanAccountDisabled:
+		case KErrWlanRestrictedLogonHours:
+		case KErrWlanServerCertificateExpired:
+		case KErrWlanCerficateVerifyFailed:
+		case KErrWlanNoUserCertificate:
+		case KErrWlanNoCipherSuite:
+		case KErrWlanUserRejected:
+		case KErrWlanUserCertificateExpired:
+		// less fatal, but still needs to fail the download:
+		case KErrWlanWpaAuthFailed:
+		case KErrWlan802dot1xAuthFailed:
+		case KErrWlanIllegalWpaPskKey:
+		case KErrWlanEapSimFailed:
+		case KErrWlanEapTlsFailed:
+		case KErrWlanEapPeapFailed:
+		case KErrWlanEapMsChapv2:
+		case KErrWlanEapAkaFailed:
+		case KErrWlanEapTtlsFailed:
+		case KErrWlanLeapFailed:
+		case KErrWlanEapGtcFailed:
+#endif
+			// A fatal network error has occured, don't retry the download
+			requestRestart = EFalse;
+			aDownloadError = EDlErrNetworkFailure;
+			break;
+		default:
+			if (!requestRestart)
+				{
+				// We assume all the other error codes to be 'hard' network errors
+				LOG2( "Unhandled GlErrId %d", aGlErrId );
+				aDownloadError = EDlErrNetworkFailure;
+				}
+			break;
+		}
+	if (iFetchType && aDownloadError != ENoError)
+		LOG3( "- ProcessDlErrors(): %d, %d", requestRestart, aDownloadError );
+	return requestRestart;
+	}
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+void CDownloadHandler::HandleDMgrEventL( RHttpDownload& aDownload, THttpDownloadEvent aEvent )
+	{
+	TInt32 glErrId = KErrNone;
+	TInt32 errId = ENoError;
+
+	// Get error IDs
+	aDownload.GetIntAttribute( EDlAttrGlobalErrorId, glErrId );
+	aDownload.GetIntAttribute( EDlAttrErrorId, errId );
+
+#ifdef DO_LOG
+	if (iFetchType)
+		{
+		_LIT(KFmt, ", GlErrId=%6d, ErrId=%d");
+		TBuf<64> buf;
+
+		if ( ((glErrId != KErrNone) || (errId != ENoError)) && glErrId != KErrUnknown )
+			{
+			buf.AppendFormat(KFmt, glErrId, errId);
+			}
+
+		if ( ( iDownloadState != aEvent.iDownloadState ) ||
+			 ( iProgressState != aEvent.iProgressState ) )
+			{
+			iDownloadState = aEvent.iDownloadState;
+			iProgressState = aEvent.iProgressState;
+
+			LOG5( "DlSt=%5d, PrSt=%5d, L=%d%S", iDownloadState, iProgressState, iNetworkLost, &buf );
+			}
+		}
+#endif
+
+	if (iNetworkLost && !iCancelled)
+		{
+		TBuf<KMaxPath> fn;
+		aDownload.GetStringAttribute( EDlAttrDestFilename, fn );
+		iDownloadError = EDlErrNetworkFailure;
+		LOG2( "Connection lost! Cancelling '%S' download.", &fn );
+		DoHandleHttpFetchFailure(fn, glErrId, errId);
+		return;
+		}
+
+	switch ( aEvent.iDownloadState )
+		{
+		case EHttpDlCreated:
+			{
+			break;
+			}
+		case EHttpDlInprogress:
+			{
+			// Already downloaded content size
+			TInt32 downloadedSize( 0 );
+			// Total download size
+			TInt32 dlLength( 0 );
+
+			if ( aEvent.iProgressState == EHttpProgConnected )
+				{
+				TInt32 iap;
+				// Get the selected IAP (ignore any errors from Get())
+				iDownloadManager.GetIntAttribute( EDlMgrIap, iap );
+				if (iIAP != iap)
+					{
+					LOG2( "* Using IAP %d", iap );
+					iIAP = iap;
+					iDlClient->HandleIapChanged(iIAP);
+					}
+				}
+
+			aDownload.GetIntAttribute( EDlAttrDownloadedSize, downloadedSize );
+			aDownload.GetIntAttribute( EDlAttrLength, dlLength );
+
+			UpdateDownloadSpeed(downloadedSize);
+
+			iDlClient->HandleHttpFetchInProgress( downloadedSize, dlLength, iDlAvgSpeed );
+			break;
+			}
+		case EHttpDlPaused:
+		case EHttpDlFailed:
+// These two fill fold to EHttpDlFailed. We don't want to process failure events twice.
+//		case EHttpDlNonPausableNetworkLoss:
+//		case EHttpDlMultipleMOFailed:
+			{
+			TBuf<KMaxPath> buf;
+
+			aDownload.GetStringAttribute( EDlAttrDestFilename, buf );
+
+			// We just monitor the progress state and start download, when
+			// content-type is received. Any errors are ignored in this state.
+			if (
+				// we have to check error codes in EHttpProgNone state, otherwise
+				// we'll end up infinite loop, for instance, in AP selection cancellation
+				 ( aEvent.iProgressState == EHttpContentTypeReceived ) ||
+				 ( ( aEvent.iProgressState == EHttpProgNone ) && ( errId == ENoError) ) )
+				{
+				// TODO: Should we check glErrId and do Reset() before starting download?
+				// need to start the download if not already started
+				LOG( "Starting download" );
+				aDownload.Start();
+				// Start timing the download
+				iDlStartTime.UniversalTime();
+				// ignore any errors, so we can start the download
+				break;
+				}
+			if ( ProcessDlErrors(glErrId, errId, iDownloadError) )
+				{
+				if (++iConnectionAttempt < KDownloadConnectionRetries)
+					{
+					LOG4( "Restarting download due to network failure (%d: %d, %d)", iConnectionAttempt, glErrId, errId );
+					// TODO: Do we need to do a Reset() before Start()?
+					aDownload.Start();
+					// We don't restart the download timer on purpose
+					//
+					// Clear the error id so it doesn't get caught below
+					// as this is not an error situation.
+					iDownloadError = EDlErrNoError;
+					}
+				else
+					{
+					LOG4( "Too many retries, cancelling download (%d; %d, %d)", iDownloadError, glErrId, errId );
+					// iDownloadError has been set properly by ProcessDlErrors()
+					}
+				}
+			if ( iDownloadError != KErrNone )
+				{
+				if (iFetchType)
+					LOG2( "DlErr=%d", iDownloadError );
+				// Inform about it
+				DoHandleHttpFetchFailure(buf, glErrId, errId);
+				// TODO: Just in case: don't delete the download in HandleDMgrEventL
+				// That seems to cause problems on different devices.
+				// Download failed, delete it
+				// aDownload.Delete();
+				}
+			break;
+			}
+		case EHttpDlCompleted:
+			{
+			TBuf<KMaxPath> buf;
+			// Download Manager total Length of the download
+			TInt32 dlLength( 0 );
+			aDownload.GetIntAttribute( EDlAttrLength, dlLength );
+			aDownload.GetStringAttribute( EDlAttrDestFilename, buf );
+			const TInt64 dlTime = UpdateDownloadSpeed(dlLength);
+			iDlClient->HandleHttpFetchCompleted(buf, iFetchType, dlLength, iDlAvgSpeed, dlTime);
+			break;
+			}
+		case EHttpDlDeleting:
+			// Signal that we're deleting the download
+			iDlClient->HandleDownloadDeleting();
+			break;
+		default:
+			{
+			break;
+			}
+		} // switch
+	}
+
+// -----------------------------------------------------------------------------
+// Updates the average download speed counter
+// @param aBytesDownloaded Bytes downloaded during this download
+// @returns Time in microseconds spend in this download
+// -----------------------------------------------------------------------------
+//
+TInt64 CDownloadHandler::UpdateDownloadSpeed(const TInt32 aBytesDownloaded)
+	{
+	TInt64 fromDlStart = 0;
+	if (aBytesDownloaded > 0)
+		{
+		TTime time;
+		time.UniversalTime();
+
+		fromDlStart = time.MicroSecondsFrom(iDlStartTime).Int64();
+
+		// Use ifs to avoid divide-by-zero
+		if (fromDlStart > 0)
+			{
+			iDlAvgSpeed = aBytesDownloaded*1000000.0 / fromDlStart;
+			}
+		}
+	return fromDlStart;
+	}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+void CDownloadHandler::CancelAllDownloads()
+	{
+	// Remove all downloads
+	iDownloadManager.DeleteAll();
+	}
+
+// -----------------------------------------------------------------------------
+// Handles client callback when download fails
+// -----------------------------------------------------------------------------
+//
+void CDownloadHandler::DoHandleHttpFetchFailure(const TDesC& aDlFilename, const TInt aGlobalErrorId, const TInt aErrorId)
+	{
+	if (!iCancelled)
+		{
+		iDlClient->HandleHttpFetchFailure(aDlFilename, aGlobalErrorId, aErrorId, iFetchType);
+		iCancelled = ETrue;
+		}
+	else
+		{
+		LOG( "DoHandleHttpFetchFailure(): Called, but already cancelled" );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+//
+void CDownloadHandler::StartDownloadL(const TDesC8& aDepUrl, const TDesC& aFileName,
+								const TDesC& aDownloadPath, const TInt aFetchType)
+	{
+	__ASSERT_ALWAYS( (aDepUrl.Length() > 0) && (aFileName.Length() > 0) && (aDownloadPath.Length() > 0), User::Leave( KErrArgument) );
+
+	// Cleanup is required in case the API is called
+	// multiple times.
+	DELETE_IF_NONNULL( iUrl );
+	DELETE_IF_NONNULL( iResponseBodyFileName );
+
+	// Set the full Url after appending the filename. +1 for '/'
+	iUrl = HBufC8::NewL( aDepUrl.Length() + aFileName.Length() + 1 );
+	TPtr8 ptr(iUrl->Des());
+	ptr.Copy(aDepUrl);
+	// Append forward slash if that's missing
+	if (ptr.Right(1)[0] != '/')
+		ptr.Append('/');
+
+	ptr.Append(aFileName);
+
+	iDownloadError = EDlErrNoError;
+	iCancelled = EFalse;
+	iConnectionAttempt = 0;
+
+	// Create a new download
+	TBool retried = EFalse;
+	TBool isNewDl = ETrue;
+	iPtrToUrl.Set(*iUrl);
+
+	// Set the full response body file name
+	// based on the actual filename to be downloaded.
+	iResponseBodyFileName = HBufC::NewL(aDownloadPath.Length()+ aFileName.Length());
+	iPtrToResponseBodyFileName.Set(iResponseBodyFileName->Des());
+	iPtrToResponseBodyFileName.Copy(aDownloadPath);
+	iPtrToResponseBodyFileName.Append(aFileName);
+
+	ConeUtils::EnsurePathExistsL(iPtrToResponseBodyFileName);
+
+	TBuf<KMaxFileName> buf;
+	buf.Copy(ptr);
+retry:
+	if (aFetchType)
+		{
+		LOG2( "+ StartDownload(): '%S'", &buf );
+		LOG2( "  to '%S'", &iPtrToResponseBodyFileName );
+		}
+
+	RHttpDownload& dl = iDownloadManager.CreateDownloadL( iPtrToUrl, isNewDl );
+
+	if (isNewDl)
+		{
+		// Start download
+		dl.SetStringAttribute( EDlAttrDestFilename, iPtrToResponseBodyFileName );
+		User::LeaveIfError( dl.Start() );
+		// This timer will be reset, when content-type is received, but if it's not
+		// we need to have a sane starting time, otherwise dl avg speed calculation
+		// does not work properly.
+		iDlStartTime.UniversalTime();
+		iFetchType = aFetchType;
+		iDlAvgSpeed = 0.0;
+		}
+	else
+		{
+		LOG( "Download exists, deleting all" );
+		// Remove any downloads
+		iDownloadManager.DeleteAll();
+		if (!retried)
+			{
+			LOG( "Download exists, retrying.." );
+			retried = ETrue;
+			// we leave isNewDl set to ETrue, so we would accept any existing download for the same URL
+			goto retry;
+			}
+		LOG( "Download exists, retry failed. Panic!" );
+		User::Leave( KErrAlreadyExists );
+		}
+	}
+// EOF