--- /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 <msvapi.h>
+#include <msvids.h>
+#include <msvuids.h>
+#include <e32math.h>
+#include <apparc.h>
+#include <mtclreg.h>
+#include <bacntf.h>
+#include <centralrepository.h>
+#include <CoreApplicationUIsSDKCRKeys.h>
+#include <implementationproxy.h>
+
+#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<CMmsPushEntry>( 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<TWatcherParams*>( 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<KMaxFileName> 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<CMmsWatcher*>(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<KMmsTidBuffer> 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<KMmsRowBufferLength> 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