diff -r 000000000000 -r 72b543305e3a mmsengine/mmswatcher/src/mmswatcher.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmsengine/mmswatcher/src/mmswatcher.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,1453 @@ +/* +* Copyright (c) 2002-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: +* Mms event watcher +* +*/ + + + +// INCLUDE FILES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmsconst.h" +#include "mmsservercommon.h" +#include "mmscmds.h" +#include "mmswatcher.h" +#include "watcher.h" +#include "mmsclient.h" +#include "mmserrors.h" +#include "mmssettings.h" // for message variation directory +#include "MmsEnginePrivateCRKeys.h" +#include "mmscenrepobserver.h" +#include "mmspollingtimer.h" +#include "mmswatcherdebuglogging.h" +#include "mmssettings.h" + +// EXTERNAL DATA STRUCTURES + +// EXTERNAL FUNCTION PROTOTYPES + +// CONSTANTS + +// MACROS + +// LOCAL CONSTANTS AND MACROS +const TInt KPauseTime = 10 * KMmsMillion; // 10 s +const TInt KMmsMessageGranularity = 5; +const TInt KMmsStartDelay = 2 * KMmsMillion; // 2 s +const TInt KMmsMediaAvailableDelay = 3 * KMmsMillion; // 3 s +const TInt KMmsMessageVariationDelay = 5 * KMmsMillion; // 5 s +const TInt KMmsDiskFullPause = 300 * KMmsMillion; // 5 min +#ifdef __WINS__ +const TInt KMmsDefaultPollingInterval = 3 * KMmsMillion; +const TInt KMmsTidBuffer = 18; +const TInt KMmsRowBufferLength = 128; +#endif + +// MODULE DATA STRUCTURES + +// LOCAL FUNCTION PROTOTYPES + +// ==================== LOCAL FUNCTIONS ==================== + +// +// MmsWatcher is an ECOM plugin +// + +const TImplementationProxy ImplementationTable[] = + { + IMPLEMENTATION_PROXY_ENTRY(0x10005948, CMmsWatcher::NewL) + }; + +EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) + { + aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); + return ImplementationTable; + } + +// ================= MEMBER FUNCTIONS ======================= + +// Helper class constructor and destructor +CMmsPushEntry::CMmsPushEntry(): + iData( NULL ), + iStatus( 0 ), + iOperation( 0 ) + { + } + +CMmsPushEntry::~CMmsPushEntry() + { + delete iData; + delete iOperation; + } + +// --------------------------------------------------------- +// CMmsWatcher::CMmsWatcher +// --------------------------------------------------------- +// +CMmsWatcher::CMmsWatcher( + TInt aPriority, + CWatcherLog& aLog) + : CActive( aPriority ), + iService( KMsvNullIndexEntryId ), + iNotificationFolder( KMsvNullIndexEntryId ), + iState( 0 ), + iLog( aLog ), + iLastError( KErrNone ), + iSession( NULL ), + iQueuedMessages( NULL ), + iOperation( NULL ) + { + CActiveScheduler::Add(this); + } + +// --------------------------------------------------------- +// CMmsWatcher::ConstructL +// --------------------------------------------------------- +// +void CMmsWatcher::ConstructL() + { + LOG(_L("ConstructL")); + + // + // ConstructL basically creates some member objects and + // starts a couple of observers + // + + iEvents = KMmsReasonBoot; + iQueuedMessages = new(ELeave) CArrayPtrFlat( KMmsMessageGranularity ); + iState = EStartup; + iMediaAvailable = ETrue; + iMmsVersion = KMmsDefaultVersion; + + User::LeaveIfError( iTimer.CreateLocal() ); + User::LeaveIfError( iFs.Connect() ); + + // Offline state observer + iOfflineObserver = CMmsCenRepObserver::NewL(); + iOfflineObserver->SubscribeNotification( + KCRUidCoreApplicationUIs, + KCoreAppUIsNetworkConnectionAllowed, + this ); + +#ifdef __WINS__ // Support for localmode in WINS emulator + iLocalModeIn = KMmsDefaultLocalModeDir; + iPollingInterval = 0; + ReadLocalModeConfigData(); +#endif + + // CEnvironmentChangeNotifier detects changes in system time. + // This is needed to run garbage collection if the user moves + // system time too much towards future. + // Task scheduler can handle short changes, but if the change is a large one, + // task scheduler does not reschedule the entry, and we must do it ourselves. + iNotifier = CEnvironmentChangeNotifier::NewL( + CActive::EPriorityLow, + TCallBack( EnvironmentChanged, this ) ); + iNotifier->Start(); + + // + // NOTE: message server session is created just before used (in garbage + // collection). This ensures that MessageServer has properly booted. + // + + // A little delay before any real actions in order to ensure everything + // else is properly up and running. + // iTimer cannot be active because it was just created + iTimer.After( iStatus, KMmsStartDelay ); // 2 s - even this may be to much + SetActive(); +#ifndef _NO_MMSS_LOGGING_ + if ( IsActive() ) + { + LOG(_L("MMS watcher is now active" )); + } + else + { + LOG(_L("MMS watcher is NOT active" )); + } +#endif + LOG(_L("End of ConstructL")); + } + +// --------------------------------------------------------- +// CMmsWatcher::NewL +// --------------------------------------------------------- +// +CMmsWatcher* CMmsWatcher::NewL( TAny* aWatcherParams ) + { + TWatcherParams* params = reinterpret_cast( aWatcherParams ); + CMmsWatcher* self = new (ELeave) CMmsWatcher( EPriorityStandard, params->iLog ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------- +// CMmsWatcher::~CMmsWatcher +// --------------------------------------------------------- +// +CMmsWatcher::~CMmsWatcher() + { + // + // Watchers should be running all the time + // + LOG(_L("Destructor called")); + + Cancel(); + delete iOperation; // unfinished garbage collection... + if ( iQueuedMessages ) + { + iQueuedMessages->ResetAndDestroy(); + } + delete iQueuedMessages; + iTimer.Close(); + iFs.Close(); + delete iSession; + delete iNotifier; + delete iOfflineObserver; +#ifdef __WINS__ // Support for localmode polling in WINS emulator + if( iPollingTimer ) + { + delete iPollingTimer; + } +#endif + } + +// --------------------------------------------------------- +// CMmsWatcher::HandleCenRepNotificationL +// --------------------------------------------------------- +// +void CMmsWatcher::HandleCenRepNotificationL( + const TUid /*aRepositoryUid*/, // not needed currently because only one observer + const TInt32 /*aKey*/, // not needed currently because only one observer + const TInt aValue ) + { + // Offline state has changed. + // If switch has been from offline to online -> invoke GarbageCollection + LOG2(_L("Offline state: %d"), aValue ); + + if( aValue != ECoreAppUIsNetworkConnectionAllowed ) + { + // Now in offline -> nothing to do + return; + } + else // back in online + { + iEvents |= KMmsReasonNetworkAllowed; + TRequestStatus* status = &iStatus; + if( iState != EStartup && iState != EMessageVariation ) + { + iState = EGarbageCollection; + } + if( !IsActive() ) + { + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KErrNone ); + } + } + } + + +// --------------------------------------------------------- +// CMmsWatcher::HandleSessionEventL +// --------------------------------------------------------- +// +void CMmsWatcher::HandleSessionEventL( + TMsvSessionEvent aEvent, + TAny* aArg1, + TAny* aArg2, + TAny* /*aArg3*/) + { + // CMmsSession requires as a parameter a class that implements + // session observer interface (this function). Therefore this + // function must always exist, though it may return immediately + // without doing anything. + + // This function is needed to watch events related to moving + // the message store in systems where the message store can + // be moved to a memory card. + + // For test purposes, we use this as a test callback. + // This function is called every time an entry changes. + + // we check what happened, and if an entry appears in + // sent folder, we know that it has been sent to MMSC, and + // we try to fetch it back. + + if ( aEvent == EMsvGeneralError ) + { + return; + } + +#ifdef __WINS__ + TMsvId parentId = KMsvNullIndexEntryId; + if( aArg2 ) + { + parentId = *(TMsvId*)aArg2; + } + + CMsvEntrySelection* entries = NULL; + if( aArg1 ) + { + entries = STATIC_CAST(CMsvEntrySelection*, aArg1); + } +#endif + + TRequestStatus* status = &iStatus; + +#ifdef __WINS__ + TInt oldState = iState; +#endif + + switch (aEvent) + { + case EMsvCloseSession: + LOG(_L("Session event EMsvCloseSession")); + // fall through on purpose + case EMsvServerTerminated: + LOG(_L("Session event EMsvServerTerminated")); + if ( iSession ) + { + delete iSession; + iSession = NULL; + // if we have deleted the session, we get no more + // events. We don't know when to restart. + } + break; + case EMsvMediaChanged: + { + // MessageStore drive has changed. + LOG(_L("Session event EMsvMediaChanged")); + LOG3(_L("- from drive %d to drive %d"), *(TInt*) aArg1, *(TInt*) aArg2 ); + iMediaAvailable = ETrue; + iMessageDrive = *(TInt*) aArg2; + if( *(TInt*) aArg1 != *(TInt*) aArg2 ) + { + iEvents |= KMmsReasonMessageStoreChanged; + } + // CenRep values for all entries must be updated + // The ids of the MMS folders must be synchronized even if the drive has not changed + // as we get this event when backup/restore happens, and in some case the restored + // folder ids may be different from those in central repository (if restore is done + // from a different phone or after phone has been flashed) + GetMessagingEntriesL( ETrue ); + if( iEvents != 0 ) + { + if ( iState != EStartup && iState != EMessageVariation ) + { + iState = EGarbageCollection; + } + if ( !IsActive() ) + { + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KErrNone ); + } + else + { + // if timer is running, cancel it because it is obvious + // that we have waited long enough and can start garbage + // collection now. + // Cancelling timer should bring us back to RunL() + LOG(_L("- already active, cancelling timer to speed up the process")); + iTimer.Cancel(); + } + } + break; + } + case EMsvMediaUnavailable: + { + LOG(_L("Session event EMsvMediaUnavailable")); + LOG2(_L("- Drive %d no longer available"), *(TInt*) aArg1 ); + iMediaAvailable = EFalse; + if ( *(TInt*) aArg1 == iMessageDrive ) + { + iMediaUnavailableTime.UniversalTime(); + } + break; + } + case EMsvMediaAvailable: + { + LOG(_L("Session event EMsvMediaAvailable")); + LOG2(_L("- Drive %d again available"), *(TInt*) aArg1 ); + // MessageStore has become available again. This could be due to + // insertion of MMC card or after backup/restore operation + // has finished + iMediaAvailable = ETrue; + if ( !(iEvents & KMmsReasonMessageStoreChanged) ) + { + iEvents |= KMmsReasonHotswap; + iEvents |= KMmsReasonBackupEnded; + } + if( iState != EStartup && iState != EMessageVariation ) + { + iState = EGarbageCollection; + } + if( !IsActive() ) + { + // Delay here to prevent EMsvMediaAvailable from triggering + // Garbage collection when message store is moved to another drive. + // When message store is moved, the store is copied first. + // After the copy is complete, we get EMsvMediaAvailable event, + // but we must not start garbage collection on the original drive + // because the message store is about to be changed. + // + // 3 s delay - we are cautious + // if we are not active, timer cannot be running - no need to cancel + iTimer.After( iStatus, KMmsMediaAvailableDelay ); + SetActive(); + } + break; + } + case EMsvMediaIncorrect: + { + LOG(_L("Session event EMsvMediaIncorrect")); + LOG2(_L("- Incorrect disk in drive %d"), *(TInt*) aArg1 ); + break; + } + case EMsvServerReady: + { + LOG(_L("Session event EMsvServerReady")); + break; + } +#ifdef __WINS__ + case EMsvEntriesCreated: + case EMsvEntriesMoved: + { + // + // These events are relevant in Localmode only + // + if ( parentId != KMsvSentEntryId ) + { + // only sent items are interesting + return; + } + + // Get localmode setting from CenRep + TBool localMode = EFalse; + TInt retval = KErrNone; + TRAP( retval, + { + localMode = LocalModeL(); + }); + #ifndef _NO_MMSS_LOGGING_ + if( retval != KErrNone ) + { + LOG(_L("ERROR connecting CenRep, defaulting to normal mode")); + } + #endif + if( localMode == EFalse ) + { + // Only localmode case is interesting + return; + } + + // Now invoke the server mtm to fetch it. + // We queue the entry, set ourselves complete and + // Let the RunL initiate the fetching. + // We don't do anything unless the entry was MMS entry + // Then get a list of files in MMSC directory, and + // create notifications for them. + // This will probably produce extra notifications, + // but it is good to test that notifications about + // unexisting messages or duplicates won't mess + // up the system. + // Messages should be fetched one at a time, so that + // there should not be too many conflicts. + TInt notifications = 0; + TInt size = 1; + TEntry entry; + TInt err = KErrNone; + CDir* fileList = NULL; + err = iFs.Entry( iLocalModeIn, entry ); + if ( err == KErrNone ) + { + err = iFs.SetSessionPath( iLocalModeIn ); + TFindFile finder( iFs ); + _LIT( KWild, "*.mms" ); + if ( err == KErrNone ) + { + err = finder.FindWildByPath( KWild, NULL, fileList ); + } + if ( err == KErrNone ) + { + notifications = fileList->Count(); + } + TInt i; + for (i = 0; i < notifications; i++ ) + { + // generate notification from filename and file size + TParse fullEntry; + fullEntry.Set( ( ( *fileList )[i] ).iName, &iLocalModeIn, NULL ); + TBuf8 buffer; + buffer.Copy( fullEntry.FullName() ); + RFile file; + err = file.Open(iFs, fullEntry.FullName(), EFileShareAny ); + if (err == KErrNone ) + { + err = file.Size(size); + } + file.Close(); + HBufC8 * notif = CreateNotificationL(buffer, size); + CleanupStack::PushL( notif ); + TBool found = EFalse; + TInt i = 0; + while ( found == EFalse && i < iQueuedMessages->Count() ) + { + if ( iQueuedMessages->At(i)->iData->Compare(*notif) == 0 ) + { + found = ETrue; + } + i++; + } + if (! found ) + { + CMmsPushEntry* newEntry = new( ELeave )CMmsPushEntry; + newEntry->iData = notif; + newEntry->iStatus = EWaiting; + newEntry->iOperation = NULL; + CleanupStack::Pop( notif ); + CleanupStack::PushL( newEntry ); + iQueuedMessages->AppendL( newEntry ); + // notif has gone to our member... + CleanupStack::Pop( newEntry ); + } + else + { + // get rid of notif + CleanupStack::PopAndDestroy( notif ); + } + iState = EScheduling; + } + } + + if ( oldState == EWaiting && notifications > 0) + { + // if we are not currently sending something to server, + // we must declare ourselves complete in order to get back to + // RunL. + // Actually we should be active already, but + // we say so just in case (to prevent stray signal) + if ( !IsActive() ) + { + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KErrNone ); + } + } + break; + } +#endif // __WINS__ + + default: + break; + } + } + +// --------------------------------------------------------- +// CMmsWatcher::EnvironmentChanged +// --------------------------------------------------------- +// +TInt CMmsWatcher::EnvironmentChanged(TAny* aThis ) + { + CMmsWatcher* self = reinterpret_cast(aThis); + self->HandleEnvironmentChange(); + return KErrNone; + } + +// --------------------------------------------------------- +// CMmsWatcher::HandleEnvironmentChange +// --------------------------------------------------------- +// +void CMmsWatcher::HandleEnvironmentChange() + { + + TInt changes = iNotifier->Change(); + if ( changes & EChangesSystemTime ) + { +#ifndef _NO_MMSS_LOGGING_ + LOG(_L("System time Change")); + TTime time; + time.UniversalTime(); + TDateTime due(time.DateTime()); + LOG7( _L("- system time is now: [%02d/%02d/%d] @ %02d:%02d:%02d"), + due.Day(), (TInt)due.Month() + 1, due.Year(), due.Hour(), due.Minute(), due.Second()); +#endif + iEvents |= KMmsReasonEnvironmentTimeChanged; + TRequestStatus* status = &iStatus; + if ( iState != EStartup && iState != EMessageVariation ) + { + iState = EGarbageCollection; + } + + if ( !IsActive() ) + { + iStatus = KRequestPending; + SetActive(); + User::RequestComplete( status, KErrNone ); + } + } + } + +// --------------------------------------------------------- +// CMmsWatcher::Dequeue +// --------------------------------------------------------- +// +void CMmsWatcher::Dequeue() + { + delete iQueuedMessages->At( 0 ); + iQueuedMessages->Delete( 0 ); + iQueuedMessages->Compress(); + } + +// --------------------------------------------------------- +// CMmsWatcher::RemoveSent +// --------------------------------------------------------- +// +void CMmsWatcher::RemoveSent() + { + LOG(_L("RemoveSent called")); + while (iQueuedMessages->Count() > 0 && + iQueuedMessages->At(0)->iStatus != EWaiting ) + { + // done, delete + Dequeue(); + } + } + +// --------------------------------------------------------- +// CMmsWatcher::HandleNextInQueueL +// --------------------------------------------------------- +// +void CMmsWatcher::HandleNextInQueueL() + { + // access to first entry in queue need not be protected by + // semaphore: + // This function is called only after we have decided + // that there is at least one entry. + // Entries are removed only when we get to the RunL + // the next time (we are still in DoRunL now). + // Adding new entries to the end of the array does not hurt us. + // Removing and adding entries must be mutally exclusive, + // as entries are added to the end and removed from the beginning. + + LOG(_L("HandleNextInQueueL")); + + if( !iSession ) + { + // if we have lost our session, we must try to get a new one + OpenSessionL(); + } + + // If there was no service entry when we were started, we must + // check if one has appeared now. Otherwise message server does + // not know where to send the request + + if( iService == KMsvNullIndexEntryId || + iNotificationFolder == KMsvNullIndexEntryId ) + { + GetMessagingEntriesL(); + } + + if( iService == KMsvNullIndexEntryId || + iNotificationFolder == KMsvNullIndexEntryId ) + { + // Something really weird is going on. + User::Leave( KErrNotFound ); + } + + TMsvId entryId = CreateEntryL( iNotificationFolder ); + + CMsvEntrySelection* selection = new ( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + + // Now we have an entry that says: local service, MMS MTM + if ( entryId != KMsvNullIndexEntryId ) + { + selection->AppendL( entryId ); + } + else + { + selection->AppendL( iService ); + } + + iQueuedMessages->At(0)->iStatus = EScheduling; + TWatcherParameters parameters; // initialized to zero + TWatcherParametersBuf paramPack( parameters ); + + iQueuedMessages->At(0)->iOperation = iSession->TransferCommandL( + *selection, EMmsDecodePushedMessage, paramPack, iStatus ); + + CleanupStack::PopAndDestroy( selection ); + SetActive(); + } + +// --------------------------------------------------------- +// CMmsWatcher::RunL() +// --------------------------------------------------------- +// +void CMmsWatcher::RunL() + { + LOG2(_L("RunL (iStatus = %d)"), iStatus.Int()); + + iLastError = iStatus.Int(); + if ( iLastError == KErrCancel ) + { + // cancel occurs only if a new event cancels timer + iLastError = KErrNone; + } + + // when we get here after doing garbage collection, + // the operation can go + if( iOperation ) + { + if( iOperation->iStatus == KRequestPending ) + { + LOG(_L("- operation still pending")); + // 5 second delay needed for message variation + iTimer.After( iStatus, KMmsMessageVariationDelay ); + SetActive(); + return; + } + else + { + LOG(_L("- operation completed")); + if( iLastError == KErrNone ) + { + iLastError = iOperation->iStatus.Int(); + } + delete iOperation; + iOperation = NULL; + } + } + + // Now we check if Garbage collection was successful. + // If not, old events must be retried + + TBool onlineEvent = iEvents & KMmsReasonNetworkAllowed; + + if ( iLastError != KErrNone ) + { + // We combine all flags in case we are getting loads of events + // faster than we can handle them. + iEvents |= iOldEvents; + } + + // clear these now to prevent endless loop. + iOldEvents = 0; + + // If we have got error in the range KMsvMediaUnavailable - KMsvIndexRestore + // we must wait until we get media available event, otherwise we are in trouble. + if ( iLastError <= (TInt) KMsvMediaUnavailable && + iLastError >= (TInt) KMsvIndexRestore && !iMediaAvailable ) + { + // media is not available, no use to retry until we get media available event. + // If media is available, it is worth retrying in 10 s. + return; + } + + if ( iLastError == KMmsErrorOfflineMode && !onlineEvent ) + { + // if we are not yet online, wait for event before retrying + return; + } + + TRAPD( error, DoRunL() ); + if( error ) + { + LOG2(_L("DoRunL error %d"), error ); + // An error occurred - try again after a while + TInt pause = KPauseTime; // default 10 s + if ( error == KErrDiskFull ) + { + pause = KMmsDiskFullPause; // 5 min + } + LOG2(_L("- Retry after %d seconds"), pause/KMmsMillion ); + iTimer.Cancel(); + iTimer.After(iStatus, pause); + if ( !IsActive() ) + { + SetActive(); + } + } // error + + iLastError = error; + + // Handle next in queue sets us active again, if there + // are more pushed messages to be sent to Server MTM. + // If there is nothing to send, we are not active. + // In that case the callback must call HandleNextInQueueL() + // to start the active loop. + } + + +// --------------------------------------------------------- +// CMmsWatcher::DoRunL() +// --------------------------------------------------------- +// +void CMmsWatcher::DoRunL() + { + LOG2(_L("DoRunL, state == %d"), iState); + switch ( iState ) + { + case EStartup: + // Make sure no error in status + StartupL(); + iState = EMessageVariation; + break; + case EMessageVariation: + { + // Message variation is tried every time because watchers are no started + // so late that first boot indicator is always off. + // Precreated messages are in private directory and cannot be accessed + // by outsiders except by those that have ALL FILES capability + MessageVariationL(); + iState = EGarbageCollection; +#ifdef __WINS__ + // Support for localmode polling in WINS emulator is started here if needed + if( iPollingInterval >= KMmsDefaultPollingInterval ) // ReadLocalModeConfigDataL might have changed it + { + iPollingTimer = CMmsPollingTimer::NewL(); + iPollingTimer->Start( this, iPollingInterval ); + } +#endif + break; + } + case EGarbageCollection: + GarbageCollectionL(); + iState = EScheduling; + break; + case EScheduling: + { + TInt count = 0; + if ( iLastError != KErrNone ) + { + LOG2(_L("iLastError %d"), iLastError ); + // I assume only the first one has been tried and failed. + // However, we retry all. + count = iQueuedMessages->Count(); + TInt i; + for ( i = 0; i < count; i++ ) + { + iQueuedMessages->At( i )->iStatus = EWaiting; + } + // Leave here, and RunL will retry after a pause + User::Leave( iLastError ); + } + + // Now we must check if there is anything to send to + // MMS server mtm + if (iQueuedMessages->Count() > 0) + { + RemoveSent(); + } + count = iQueuedMessages->Count(); + // If there is anything left, queue the next one + if ( count > 0 ) + { + HandleNextInQueueL(); + } + else if ( iEvents != 0 ) + { + iState = EGarbageCollection; + iStatus = KRequestPending; + TRequestStatus* status = &iStatus; + SetActive(); + User::RequestComplete( status, KErrNone ); + } + else + { + // If there is nothing left, continue waiting + iState = EWaiting; + } + break; + } + case EWaiting: + iStatus = KErrNone; + break; + default: + __ASSERT_DEBUG(EFalse, gPanic(EMmsUnknownState)); + break; + } + } + +// --------------------------------------------------------- +// CMmsWatcher::DoCancel +// --------------------------------------------------------- +// +void CMmsWatcher::DoCancel() + { + LOG(_L("DoCancel")); + + iTimer.Cancel(); + if ( iOperation ) + { + iOperation->Cancel(); + } + // only one operation at a time is active + if ( iQueuedMessages->Count() > 0 && + iQueuedMessages->At(0)->iOperation ) + { + iQueuedMessages->At(0)->iOperation->Cancel(); + } + + iState = EInvalid; + } + +// --------------------------------------------------------- +// CMmsWatcher::StartupL() +// --------------------------------------------------------- +// +void CMmsWatcher::StartupL() + { + LOG(_L("StartupL")); + TInt error = KErrNone; + + iMessageDrive = EDriveC; // default + // Open session to MessageServer + TRAP( error, OpenSessionL() ); + // We ignore the error. If the session could not be opened now, + // it will be retried later + error = KErrNone; + + // Get serviceId and NotificationFolderId from MMS settings + TRAP( error, GetMessagingEntriesL() ); + if( error ) + { + LOG(_L("- ERROR reading settings")); + } + + // Complete in order to get back to RunL + iStatus = KRequestPending; + TRequestStatus* status = &iStatus; + SetActive(); + User::RequestComplete( status, KErrNone ); + LOG(_L("- End of StartupL")); + } + +// --------------------------------------------------------- +// CMmsWatcher::GarbageCollectionL() +// --------------------------------------------------------- +// +void CMmsWatcher::GarbageCollectionL() + { + LOG(_L("GarbageCollectionL")); + + // Open session in case the message server has died + // but we have received an event from elsewhere + if( !iSession ) + { + // if we have lost our session, we must try to get a new one + OpenSessionL(); + } + + if ( iService != KMsvNullIndexEntryId ) + { + // if there is no service, there is no garbage either... + CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection; + CleanupStack::PushL( selection ); + selection->AppendL( iService ); + // Start Garbage collection + TMMSGarbageCollectionParameters parameters; // initialized to zero + parameters.iReasonFlags = iEvents; + parameters.iMediaUnavailableTime = iMediaUnavailableTime; + TMMSGarbageCollectionParametersBuf paramPack( parameters ); + LOG(_L("Invoking garbage collection")); + iOperation = iSession->TransferCommandL( + *selection, EMmsGarbageCollection, paramPack, iStatus ); + CleanupStack::PopAndDestroy( selection ); + // These events were handled. + iOldEvents = iEvents; + iEvents = 0; + iMediaUnavailableTime = 0; + SetActive(); + } + LOG(_L("- End of GarbageCollectionL")); + } + +// --------------------------------------------------------- +// CMmsWatcher::MessageVariationL() +// --------------------------------------------------------- +// +void CMmsWatcher::MessageVariationL() + { + LOG(_L("MessageVariationL")); + if( !iSession ) + { + // if we have lost our session, we must try to get a new one + OpenSessionL(); + } + + // If there is no service, it must be created first + if( iService == KMsvNullIndexEntryId ) + { + LOG(_L("no service - must create one")); + GetMessagingEntriesL(); + } + CMsvEntrySelection* selection = new(ELeave) CMsvEntrySelection; + CleanupStack::PushL( selection ); + selection->AppendL( iService ); + LOG(_L("Invoking message variation")); + iOperation = iSession->TransferCommandL( + *selection, EMmsMessageGeneration, TPtrC8(), iStatus ); + CleanupStack::PopAndDestroy( selection ); + SetActive(); + + LOG(_L("End of MessageVariationL")); + } + +// --------------------------------------------------------- +// CMmsWatcher::CreateEntryL() +// --------------------------------------------------------- +// +TMsvId CMmsWatcher::CreateEntryL( TMsvId aFolder ) + { + TMsvId entryId = KMsvNullIndexEntryId; + + if ( aFolder == KMsvNullIndexEntryId ) + { + // no folder no entry + return entryId; + } + + if( !iSession ) + { + // if we have lost our session, we must try to get a new one + OpenSessionL(); + } + CMsvEntry* cEntry = iSession->GetEntryL( aFolder ); + CleanupStack::PushL( cEntry ); // *** + + TMsvEntry entry; + entry.iType = KUidMsvMessageEntry; + entry.iMtm = KUidMsgTypeMultimedia; + entry.SetVisible( ETrue ); + // If we want to put data here, InPreparation must be set to false first + entry.SetInPreparation( EFalse ); + entry.iServiceId = KMsvLocalServiceIndexEntryId; + entry.iRelatedId = iService; + entry.iMtmData2 = KMmsNotificationBinary; + cEntry->CreateL( entry ); + entryId = entry.Id(); + + // + // Stream + // 1) length of the data as 32 bit integer + // 2) pushed message data + // into created entry's stream + // + cEntry->SetEntryL( entryId ); + CMsvStore* store = cEntry->EditStoreL(); + CleanupStack::PushL( store ); // *** + RMsvWriteStream outs; + outs.AssignLC( *store, KUidBinaryNotificationStream ); // *** + outs.WriteUint32L( iQueuedMessages->At(0)->iData->Size() ); + outs.WriteL( *iQueuedMessages->At(0)->iData ); + outs.CommitL(); + outs.Close(); + store->CommitL(); + CleanupStack::PopAndDestroy( &outs ); // close outs + CleanupStack::PopAndDestroy( store ); + CleanupStack::PopAndDestroy( cEntry ); + + return entryId; + } + +// --------------------------------------------------------- +// CMmsWatcher::OpenSessionL() +// --------------------------------------------------------- +// +void CMmsWatcher::OpenSessionL() + { + LOG(_L("Opening session")); + if ( !iSession ) + { + // leaves if session cannot be opened + iSession = CMsvSession::OpenSyncL( *this ); + } + + TRAPD( error2, iMessageDrive = iSession->CurrentDriveL() ); + if( error2 != KErrNone ) + { + // Using default + iMessageDrive = EDriveC; + } + } + +// --------------------------------------------------------- +// CMmsWatcher::CreateNotificationL() +// --------------------------------------------------------- +// +#ifdef __WINS__ +HBufC8* CMmsWatcher::CreateNotificationL(TDesC8& aUrl, TInt aSize) + { + // for test purposes aUrl will contain the filename. + TInt position = 0; + CBufFlat* encodeBuffer = CBufFlat::NewL( 0x400 ); + CleanupStack::PushL( encodeBuffer ); // *** + encodeBuffer->ResizeL( 0 ); + encodeBuffer->ResizeL( 0x400 ); + + // encode message type + encodeBuffer->Write( position, &KMmsAssignedMessageType, 1 ); + position++; + encodeBuffer->Write( position, &KMmsMessageTypeMNotificationInd, 1 ); + position++; + + // encode tid + encodeBuffer->Write( position, &KMmsAssignedTID, 1 ); + position++; + + // generate random TID + TPtrC8 cid; + TBufC8 target; + TTime now; + now.UniversalTime(); + TInt random = 0; + + // we don't generate a true random TID: We generate the + // TID from the URL so that if we generate a notification + // twice from the same file, we get the same TID and the + // same URL. This way we can test the pruning function in + // server MTM + + TInt i; + for ( i = 0; i < aUrl.Length(); i++ ) + { + random += aUrl[ i ]; + } + + target.Des().Num( random ); + cid.Set( target.Des() ); + + TInt length = cid.Length(); + encodeBuffer->Write( position, cid, length ); + position += length; + encodeBuffer->Write( position, &KMmsNull, 1 ); + position++; + + encodeBuffer->Write( position, &KMmsAssignedMmsVersion, 1 ); + position++; + TUint8 version = iMmsVersion | 0x80; + encodeBuffer->Write( position, &version, 1 ); + position++; + + // from is optional - we don't care + + // message class + encodeBuffer->Write( position, &KMmsAssignedMessageClass, 1 ); + position++; + encodeBuffer->Write( position, &KMmsMessageClassPersonal, 1 ); + position++; + + // message size + encodeBuffer->Write( position, &KMmsAssignedMessageSize, 1 ); + position++; + + TUint8 byte; + const TInt KConst4 = 4; + const TInt KConst8 = 8; + TUint8 array[KConst4]; + + if ( aSize <= 0x7f ) + { + byte = ( TInt8 ) aSize; + byte |= 0x80; + encodeBuffer->Write( position, &byte, 1); + position++; + } + else + { + + length = KConst4; + + TUint temp = aSize; + byte = TInt8( ( temp >> 24 ) & 0xFF ); + + while ( byte == 0 && length > 0 ) + { + length--; + temp = temp << KConst8; + byte = TInt8( ( temp >> 24 ) & 0xFF ); + } + + for ( i = 0; i < length; i++ ) + { + array[i] = TInt8( ( temp >> ( KConst8 * (3 - i) ) ) & 0xFF ); + } + + // write short length + encodeBuffer->Write( position, &length, 1 ); + position++; + + // write as many bytes as were non-zero + // There will be always at least one byte in the array + // because aSize > 127 if we are here + encodeBuffer->Write( position, &array[0], length ); + position+= length; + } + + // expiry + + const TUint KTenHours = 10 * 60 * 60; // 10 hours + TUint temp = KTenHours; + encodeBuffer->Write( position, &KMmsAssignedExpiry, 1 ); + position++; + + // calculate value length + + length = KConst4; + byte = TInt8( ( temp >> 24 ) & 0xFF ); + + while ( byte == 0 && length > 0 ) + { + length--; + temp = temp << KConst8; + byte = TInt8( ( temp >> 24 ) & 0xFF ); + } + + // now add short length and absolute/relative token + + length += 2; + + encodeBuffer->Write( position, &length, 1 ); + position++; + + encodeBuffer->Write( position, &KMmsRelativeToken, 1 ); + position++; + + length -= 2; // actual integer length + + for (i = 0; i < length; i++) + { + array[i] = TInt8( ( temp >> ( KConst8 * (3 - i) ) ) & 0xFF ); + } + + // write short length + encodeBuffer->Write( position, &length, 1 ); + position++; + + // write as many bytes as were non-zero + encodeBuffer->Write( position, &array[0], length ); + position+= length; + + // And now the salt of the notification: + // the actual location of the message. + + encodeBuffer->Write( position, &KMmsAssignedContentLocation, 1 ); + position++; + + // Check if we need a Quote (This does not mean the quoted string.) + if ( aUrl[0] >= 0x80 ) + { + encodeBuffer->Write( position, &KMmsQuote, 1 ); + position++; + } + + length = aUrl.Length(); + encodeBuffer->Write( position, aUrl, length ); + position += length; + + encodeBuffer->Write( position, &KMmsNull, 1 ); + position++; + + // DONE. + + TPtrC8 ptr; + encodeBuffer->ResizeL( position ); + ptr.Set( encodeBuffer->Ptr(0).Left( position ) ); + HBufC8* result = ptr.AllocL(); + CleanupStack::PopAndDestroy( encodeBuffer ); + + return result; + } +#else +HBufC8* CMmsWatcher::CreateNotificationL(TDesC8& /*aUrl*/, TInt /*aSize*/) + { + User::Leave( KErrNotSupported ); + return NULL; // this is actually unnecessary as we always leave. + } +#endif + +// --------------------------------------------------------- +// CMmsWatcher::GetMessagingEntriesL +// --------------------------------------------------------- +// +void CMmsWatcher::GetMessagingEntriesL( const TBool aMessageStoreHasChanged ) + { + LOG(_L("ReadMessagingEntriesL")); + // Connect CenRep + CMmsSettings* settings = CMmsSettings::NewL(); + CleanupStack::PushL( settings ); // *** + settings->LoadSettingsL(); + iMmsVersion = settings->MmsVersion(); + + if( aMessageStoreHasChanged == EFalse ) + { + // Read entries from CenRep + iService = settings->Service(); + iNotificationFolder = settings->NotificationFolder(); + } + else + { + // MessageStore has changed + // -> CenRep values are false and have to be updated + iService = KMsvNullIndexEntryId; + iNotificationFolder = KMsvNullIndexEntryId; + } + + // If entries are null, create them to MessageStore + if( iSession && + ( iService == KMsvNullIndexEntryId || + iNotificationFolder == KMsvNullIndexEntryId ) ) + { + LOG(_L("- No service exist or message store changed")); + LOG(_L("- Must update service id or create new")); + settings->CreateNewServiceL( *iSession ); + iService = settings->Service(); + iNotificationFolder = settings->NotificationFolder(); + + // Retest + if( iService == KMsvNullIndexEntryId || + iNotificationFolder == KMsvNullIndexEntryId ) + { + LOG(_L("- ERROR: Still no service or notification folder")); + } + settings->SaveSettingsL(); + } + + // Cleanup + CleanupStack::PopAndDestroy( settings ); + } + +// +// Following methods are only in WINS compilations +// for testing purposes +// +#ifdef __WINS__ +// --------------------------------------------------------- +// CMmsWatcher::LocalModeL +// --------------------------------------------------------- +// +TBool CMmsWatcher::LocalModeL() + { + CRepository* repository = CRepository::NewL( KUidMmsServerMtm ); + CleanupStack::PushL( repository ); + TInt temp = 0; + TInt retval = repository->Get( KMmsEngineLocalMode, temp ); + CleanupStack::PopAndDestroy( repository ); + if( retval == KErrNone ) + { + return (TBool)temp; + } + else + { + LOG(_L("- ERROR reading localmode key from CenRep, assuming normal mode")); + return EFalse; + } + } + +// --------------------------------------------------------- +// CMmsWatcher::ReadLocalModeConfigData +// --------------------------------------------------------- +// +void CMmsWatcher::ReadLocalModeConfigData() + { + RFileReadStream reader; + TInt err = reader.Open( iFs, KMmsLocalModeConfigFile, EFileShareReadersOnly ); + if( err != KErrNone ) + { + reader.Close(); + return; + } + + TChar delim = 0x000A; + TBuf rowBuffer; + FOREVER + { + TRAP( err, reader.ReadL( rowBuffer, delim ) ); + if( err == KErrEof ) + { + reader.Close(); + return; + } + TInt length = rowBuffer.Length(); + if( length > 2 ) + { + // Check for comment line + if( rowBuffer[0] == 0x0023 ) // 0x23 == '#' + { + continue; + } + // Check for start of file (BOM) + if( rowBuffer[0] == 0xFEFF ) + { + rowBuffer.Delete( 0, 1 ); + length = rowBuffer.Length(); + } + // Drop CR+LF from the end of line + rowBuffer.Delete( length - 2, 2 ); + + TInt separatorPosition = 0; + _LIT( KSeparator, "=" ); + separatorPosition = rowBuffer.Find( KSeparator ); + if( separatorPosition > 0 ) + { + if( rowBuffer.Left( separatorPosition ).CompareF( KMmsLocalmodeInDirectory ) == 0 ) + { + iLocalModeIn = rowBuffer.Mid( separatorPosition+1 ); + } + if( rowBuffer.Left( separatorPosition ).CompareF( + KMmsLocalmodePollingInterval ) == 0 ) + { + TLex16 lex; + lex.Assign( rowBuffer.Mid( separatorPosition+1 ) ); + lex.Val( iPollingInterval ); + iPollingInterval *= KMmsMillion; + } + } + } + } + } + +#endif // __WINS__ emulator polling support + + +// ================= OTHER EXPORTED FUNCTIONS ============== + +// +// --------------------------------------------------------- +// Panic implements +// panic, for debug version only +// +GLDEF_C void gPanic( + TMmsPanic aPanic ) // error number enumerations + { + _LIT( KMmsPanic,"MMS" ); + User::Panic( KMmsPanic, aPanic ); + } + +// End of File