diff -r 3f419852be07 -r 364021cecc90 smartinstaller/adm/src/ADMDownloadHandler.cpp --- /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 +#include +#include //For download error handling +#include //For download error handling +#include //For download error handling +#include // 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 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 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 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 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 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