/*
* 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