sysresmonitoring/oommonitor/src/oommonitor.cpp
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
child 35 13fd6fd25fe7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysresmonitoring/oommonitor/src/oommonitor.cpp	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,1052 @@
+/*
+* Copyright (c) 2006 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:  Definitions of OOM Monitor.
+*
+*/
+
+
+#include <hal.h>
+#include <s32file.h>
+#include <w32std.h>
+#include <akneiksrvc.h>
+#include <LafMemoryWatcher.rsg>
+#include <bautils.h>
+#include <apgwgnam.h>
+#include <apgtask.h>
+#include <barsc.h>
+#include <barsread2.h>
+
+#include <e32property.h>
+#include <UikonInternalPSKeys.h>
+#include <UiklafInternalCRKeys.h>
+
+#include <data_caging_path_literals.hrh>
+
+#include "oommonitor.h"
+#include "oommonitorplugin.h"
+#include "oommonitorplugin.hrh"
+#include "oommonitorclientserver.h"
+
+#define OOM_WATCHDOG_STATUS_IDLE -1
+
+const TInt KPreallocatedSpaceForAppList = 50;
+const TInt KPluginFreeMemoryTime = 100000;       // 100 milliseconds for plugins to free memory, overridden by LafMemoryWatcher.rss
+
+_LIT(KDriveZ, "z:");
+_LIT(KOOMWatcherResourceFileName, "lafmemorywatcher.rsc");
+_LIT(KOOMWatcherThreadName, "OOM FW");
+_LIT(KDummyWgName, "20");
+
+
+// Implements just Error() to avoid panic
+class CSimpleScheduler : public CActiveScheduler
+    {
+    void Error( TInt ) const{} // From CActiveScheduler
+    };
+
+// thread function for OOM watcher
+GLDEF_C TInt WatcherThreadFunction( TAny* )
+    {
+    TInt err( KErrNone );
+
+    CTrapCleanup* cleanup = CTrapCleanup::New();
+    CActiveScheduler* scheduler = new CSimpleScheduler();
+    CMemoryMonitor* oom = NULL;
+
+    if ( !cleanup || !scheduler)
+        {
+        err = KErrNoMemory;
+        }
+    else
+        {
+        CActiveScheduler::Install( scheduler );
+        TRAPD( err,
+            {
+            RThread me;
+            me.SetPriority(EPriorityAbsoluteHigh);
+            oom = CMemoryMonitor::NewL();
+            } )
+
+        if ( err == KErrNone )
+            {
+            // start the watchers
+            CActiveScheduler::Start();
+            }
+        }
+
+    delete cleanup;
+    delete scheduler;
+    delete oom;
+
+    return err;
+    }
+
+// Creates thread for OOM watchers
+EXPORT_C void CreateOOMWatcherThreadL()
+    {
+    RThread thread;
+    TInt ret = thread.Create( KOOMWatcherThreadName,
+                              WatcherThreadFunction,
+                              0x2000, // stack size
+                              NULL, // uses caller thread's heap
+                              (TAny*)NULL );
+
+    if ( ret == KErrNone )
+        {
+        thread.Resume();
+        thread.Close();
+        }
+
+    User::LeaveIfError( ret );
+    }
+
+
+// ======================================================================
+// class CMemoryMonitor
+// ======================================================================
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+CMemoryMonitor* CMemoryMonitor::NewL()
+    { // static
+    CMemoryMonitor* self = new(ELeave) CMemoryMonitor();
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+CMemoryMonitor::CMemoryMonitor()
+: iRamPluginRunTime(KPluginFreeMemoryTime), iCurrentTask(iWs), 
+  iMemoryAboveThreshold(ETrue), iPluginMemoryGood(ETrue)
+    {
+    SetMemoryMonitorTls(this);
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+CMemoryMonitor::~CMemoryMonitor()
+    {
+    if (iWatchdogStatusSubscriber)
+    {
+        iWatchdogStatusSubscriber->StopSubscribe();
+    }
+    iWatchdogStatusProperty.Close();
+    delete iWatchdogStatusSubscriber;
+
+    delete iServer;
+    delete iWservEventReceiver;
+    delete iAppCloseTimer;
+    delete iAppCloseWatcher;
+    delete iOOMWatcher;
+    delete iPlugins;
+    iAppCloseOrderMap.Close();
+    iWgIds.Close();
+    delete iWgName;
+    iFs.Close();
+    iWs.Close();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMemoryMonitor::ConstructL()
+    {
+    User::LeaveIfError(iWs.Connect());
+
+    // Reserve enough space to build an app list later.
+    iWgIds.ReserveL(KPreallocatedSpaceForAppList);
+    // Reserve enough space for CApaWindowGroupName.
+    iWgName = CApaWindowGroupName::NewL(iWs);
+    iWgNameBuf = HBufC::NewL(CApaWindowGroupName::EMaxLength);
+    (*iWgNameBuf) = KDummyWgName;
+    iWgName->SetWindowGroupName(iWgNameBuf);    // iWgName takes ownership of iWgNameBuf
+
+   // Load up threshold & OOM app lists from resource.
+    User::LeaveIfError(iFs.Connect());
+
+    RResourceFile resFile;
+    TFileName filename(KDriveZ);
+    filename.Append(KDC_RESOURCE_FILES_DIR);
+    filename.Append(KOOMWatcherResourceFileName);
+    BaflUtils::NearestLanguageFile(iFs, filename);
+    resFile.OpenL(iFs, filename);
+    CleanupClosePushL(resFile);
+
+    HBufC8* thresholds = resFile.AllocReadLC(R_APP_OOM_THRESHOLDS);
+    RResourceReader theReader;
+    theReader.OpenLC(*thresholds);
+
+    iLowThreshold = theReader.ReadInt32L();
+    iGoodThreshold = theReader.ReadInt32L();
+    iMaxExitTime = theReader.ReadInt32L();
+    TRAP_IGNORE(iRamPluginRunTime = theReader.ReadInt32L());    // trapped in case lafmemorywatcher.rss does not define this value
+
+    CleanupStack::PopAndDestroy(&theReader);
+    CleanupStack::PopAndDestroy(thresholds);
+
+    // apps to close first, first app in list gets closed first
+    ReadAppResourceArrayL(resFile, R_APP_OOM_EXIT_CANDIDATES, ECloseFirst, +1);	// +1 means apps later in the list are closed later
+    // apps to never close, all apps get order ENeverClose
+    ReadAppResourceArrayL(resFile, R_APP_OOM_EXIT_NEVER, ENeverClose, 0);	// 0 means that all apps get ENeverClose
+    // apps to close last, first app in list gets closed last
+    ReadAppResourceArrayL(resFile, R_APP_OOM_EXIT_LAST, ECloseLast, -1);	// -1 means apps later in the list are closed earlier
+    
+    CleanupStack::PopAndDestroy(); // resFile.Close();
+
+    iPlugins = new(ELeave) COomPlugins;
+    iPlugins->ConstructL();
+
+    TInt err = iWatchdogStatusProperty.Attach(KPSUidUikon, KUikOOMWatchdogStatus);
+#ifdef _DEBUG
+    RDebug::Print(_L("xxxx KUikOOMWatchdogStatus err=%d"), err);
+#endif
+    err = iWatchdogStatusProperty.Set(OOM_WATCHDOG_STATUS_IDLE);
+
+    iWatchdogStatusSubscriber = new (ELeave) CSubscribeHelper(TCallBack(WatchdogStatusStatusChanged, this), iWatchdogStatusProperty);
+    iWatchdogStatusSubscriber->SubscribeL();
+
+    iOOMWatcher = COutOfMemoryWatcher::NewL(*this, iLowThreshold, iGoodThreshold);
+    iOOMWatcher->Start();
+
+    iAppCloseTimer = CAppCloseTimer::NewL(*this);
+    iAppCloseWatcher = new(ELeave) CAppCloseWatcher(*this);
+    
+    iWservEventReceiver = new(ELeave) CWservEventReceiver(*this, iWs);
+    iWservEventReceiver->ConstructL();
+
+    iServer = CMemoryMonitorServer::NewL(*this);
+    }
+
+void CMemoryMonitor::ReadAppResourceArrayL(RResourceFile& aResFile, TInt aResId, TInt aOrderBase, TInt aOrderInc)
+    {
+    // apps in this list will be ordered starting from aOrderBase
+    TInt order = aOrderBase;
+    // get the resource and set up the resource reader
+    HBufC8* apps = aResFile.AllocReadLC(aResId);
+    TResourceReader theReader;
+    theReader.SetBuffer(apps);
+    // go through all apps in the list
+    TInt appsCount = theReader.ReadUint16();
+    for (TInt ii = 0; ii < appsCount; ii++)
+        {
+        TInt appUid = theReader.ReadInt32();
+        // insert the app UID with the appropriate order
+        iAppCloseOrderMap.Insert(appUid, order);
+        // change the order number as appropriate for this list
+        order += aOrderInc;
+        }
+    CleanupStack::PopAndDestroy(apps);
+    }
+
+void CMemoryMonitor::CancelAppCloseWatchers()
+    {
+    iAppCloserRunning = EFalse;
+    iCurrentTask.SetWgId(0);
+    if (iAppCloseTimer)
+        iAppCloseTimer->Cancel();
+    if (iAppCloseWatcher)
+        iAppCloseWatcher->Cancel();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMemoryMonitor::FreeMemThresholdCrossedL()
+    {
+    StartFreeSomeRamL(iGoodThreshold);
+    }
+
+void CMemoryMonitor::HandleFocusedWgChangeL()
+    {
+    // The focused window group has changed.
+    if (iAppCloserRunning)
+        {
+        // if the app closer is currently running, restart it
+        RestartAppCloser();
+        }
+    else if (!iMemoryAboveThreshold)
+    	{
+	    // If memory is low, rescan for free memory
+    	StartFreeSomeRamL(iGoodThreshold);
+    	}
+    }
+
+void CMemoryMonitor::RestartAppCloser()
+	{
+    CancelAppCloseWatchers();
+    // StartFreeSomeRamL is trapped so that clients waiting
+    // for completion will definitely receive it.
+	TRAPD(err, StartFreeSomeRamL(iCurrentTarget))
+	if (err != KErrNone)
+        CloseAppsFinished(FreeMemoryAboveThreshold());
+	}
+
+// ---------------------------------------------------------
+// This function attempts to free enough RAM to leave the target amount of space free.
+// This function could take a substantial time to return as it may have to close a number
+// of applications, and each will be given a timeout of KAPPEXITTIMEOUT.
+// ---------------------------------------------------------
+//
+void CMemoryMonitor::StartFreeSomeRamL(TInt aTargetFree)
+    {
+    // update the target if new target is higher
+    if (aTargetFree > iCurrentTarget)
+        iCurrentTarget = aTargetFree;
+
+    // do nothing more if app closer is already running
+    if (iAppCloserRunning)
+        return;
+
+       iCurrentTarget = aTargetFree;
+
+    // check if there is enough free memory already.
+    if (FreeMemoryAboveThreshold())
+        {
+        CloseAppsFinished(ETrue);
+        return;
+        }
+
+    // Tell plugins to free memory
+    bool pluginsToldToFreeMemory = iPluginMemoryGood;
+    SetPluginMemoryGood(EFalse);
+
+    // get the list of apps to free
+    GetWgsToCloseL();
+
+    if (pluginsToldToFreeMemory)
+        {
+        // Give the plugins a short time to free memory.
+        // App close timer will kick off the app closer.
+        iAppCloseTimer->After(iRamPluginRunTime);
+        }
+    else
+        {
+        // start closing apps
+        CloseNextApp();
+        }
+    }
+
+void CMemoryMonitor::CloseNextApp()
+    {
+    if(iNextAppToClose >= 0)
+        {
+        // close an app, if there's an app to be closed
+#ifdef _DEBUG
+        RDebug::Print(_L("OOM WATCHER: Target not achieved; continuing... Target:%d"),iCurrentTarget);
+#endif
+		// CloseNextApp() may have been called by one of the event
+		// watchers, cancel them all to prevent more events from the
+		// last app before restarting the watchers for the new app to close
+        CancelAppCloseWatchers();
+        iAppCloserRunning = ETrue;
+        // Set the TApaTask to the app
+        iCurrentTask.SetWgId(iWgIds[iNextAppToClose].iId);
+        // Start a timer and the thread watcher
+        iAppCloseTimer->After(iMaxExitTime);
+        iAppCloseWatcher->Start(iCurrentTask);
+        // Tell the app to close
+        iCurrentTask.EndTask();
+        iNextAppToClose--;
+        }
+    else
+        {
+        // stop if we have no more apps
+        CloseAppsFinished(EFalse);
+        }
+    }
+
+// handle an app closed event
+void CMemoryMonitor::CloseAppEvent()
+    {
+    if (FreeMemoryAboveThreshold())
+        {
+        // stop if we have enough memory
+        CloseAppsFinished(ETrue);
+        }
+    else
+        {
+        // otherwise try to close another app
+        CloseNextApp();
+        }
+    }
+
+// The app closer is finished
+void CMemoryMonitor::CloseAppsFinished(TBool aMemoryGood)
+    {
+    CancelAppCloseWatchers();
+#ifdef _DEBUG
+    RDebug::Print(_L("OOM WATCHER: Final result: Target:%d Good?:%d"),iCurrentTarget,aMemoryGood);
+#endif
+    iServer->CloseAppsFinished(aMemoryGood);
+    // plugins can start using memory if result is good
+    SetPluginMemoryGood(aMemoryGood);
+    }
+
+TBool CMemoryMonitor::FreeMemoryAboveThreshold()
+    {
+    // may cause some extra load but allows more precise action
+    User::CompressAllHeaps();
+
+    TInt current = 0;
+    HAL::Get( HALData::EMemoryRAMFree, current );
+
+#ifdef _DEBUG
+    RDebug::Print(_L("OOM WATCHER: Free RAM now:%d "),current);
+#endif
+
+	iMemoryAboveThreshold = (current >= iCurrentTarget);
+    return iMemoryAboveThreshold;
+    }
+
+void CMemoryMonitor::GetWgsToCloseL()
+    {
+    // get all window groups, with info about parents
+    TInt numGroups = iWs.NumWindowGroups(0);
+    iWgIds.ReserveL(numGroups);
+    User::LeaveIfError(iWs.WindowGroupList(0, &iWgIds));
+
+    // Remove all child window groups, promote parents to foremost child position
+    ColapseWindowGroupTree();
+    
+    // now rearange the list so that it only contains apps to close
+    // first remove the foreground window group
+    iWgIds.Remove(0);
+
+    // go through the list from start to end.
+    // this divides the list into two sections. Apps that never
+    // close move towards the end of the list, apps that will close
+    // go towards the start.
+    // apps to close first will be placed further back in the close list.
+    TInt numAppsToClose = 0;		// numAppsToClose marks the boundary between closing and non-closing apps
+    TInt count = iWgIds.Count();
+    for (TInt ii=0; ii<count; ii++)
+        {
+        ASSERT(ii >= numAppsToClose);	// we are always looking at apps after the sorted section
+        // get the next window group
+        RWsSession::TWindowGroupChainInfo info = iWgIds[ii];
+        // get the close order for the window group
+        TInt closeOrder = AppCloseOrder(ii, info.iId);
+        if (closeOrder == ENeverClose)
+            {
+            // leave apps which should not be closed in place,
+            // after the last app to close.
+            continue;
+            }
+        else
+            {
+            // We no longer need the parent id. Use it to store the close order, to avoid the need to allocate more array space
+            info.iParentId = closeOrder;
+               // remove the app from it's current position
+            iWgIds.Remove(ii);
+            // find the right place to insert the app (lower orders are put further back)
+            TInt insertPos;
+            for (insertPos = 0; insertPos < numAppsToClose; insertPos++)
+                {
+                // compare this close order with window groups already in the list,
+                // whose close order is stored in iParentId
+                if (closeOrder > iWgIds[insertPos].iParentId)
+                    break;
+                }
+            // Insert the app in the correct place
+            ASSERT(insertPos <= ii && insertPos <= numAppsToClose);	// apps to close are always moved to front, before the close boundary
+            iWgIds.Insert(info, insertPos);
+            numAppsToClose++;
+            }
+        }
+    
+    // start closing apps from the end of the list of apps to close
+    iNextAppToClose = numAppsToClose - 1;
+    }
+
+// Calculate the order number in which this app should be closed
+// This is based on the app's UID and its window group Z-order
+// Apps given a lower order will be closed before those with a
+// higher order.
+TInt CMemoryMonitor::AppCloseOrder(TInt aWgIndex, TInt aWgId)
+    {
+    // get the app's details
+    TPtr wgPtr(iWgNameBuf->Des());
+    if (iWs.GetWindowGroupNameFromIdentifier(aWgId, wgPtr) != KErrNone)
+        return ENeverClose;
+    iWgName->SetWindowGroupName(iWgNameBuf);
+    TUid uid = iWgName->AppUid(); // This UID comes from the app, not the mmp!
+    TInt* order = iAppCloseOrderMap.Find(uid.iUid);
+
+    // The default app close order is normal with further 
+    // back apps getting a lower order
+    TInt closeOrder = ECloseNormal - aWgIndex;
+    if (order)
+        {
+        // Apps with a defined close order get that order.
+        closeOrder = *order;
+        }
+    else if (uid.iUid == 0 || iWgName->IsSystem() || iWgName->Hidden() || iWgName->IsBusy())
+        {
+        // Apps that should never close get the ENeverClose rank
+        closeOrder = ENeverClose;
+        }
+
+    return closeOrder;
+    }
+
+void CMemoryMonitor::SetPluginMemoryGood(TBool aSetToGood)
+    {
+    if (aSetToGood && !iPluginMemoryGood)
+        iPlugins->MemoryGood();
+    else if (!aSetToGood && iPluginMemoryGood)
+        iPlugins->FreeRam();
+    iPluginMemoryGood = aSetToGood;
+    }
+
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+TInt CMemoryMonitor::WatchdogStatusStatusChanged(TAny* aPtr)
+    {
+    CMemoryMonitor* self = STATIC_CAST(CMemoryMonitor*,aPtr);
+    if (self)
+        self->HandleWatchdogStatusCallBackL();
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMemoryMonitor::HandleWatchdogStatusCallBackL()
+    {
+    // Someone has set the key to request some free memory.
+    TInt target = 0;
+    iWatchdogStatusProperty.Get(target);
+
+    // Try to free the RAM.
+    if (target >= 1)
+        {
+        iOOMWatcher->Cancel();              // Pause memory notifys.
+        TRAP_IGNORE(StartFreeSomeRamL(target)); // This call could take a few seconds to do its stuff.
+        iOOMWatcher->Start();               // Restarts memory monitoring.
+        }
+    // Set the key back to OOM_WATCHDOG_STATUS_IDLE to indicate we're done.
+    iWatchdogStatusProperty.Set(OOM_WATCHDOG_STATUS_IDLE);
+    }
+
+// ---------------------------------------------------------
+// Remove child window groups. Promote parent window groups forward to child position
+// ---------------------------------------------------------
+//
+void CMemoryMonitor::ColapseWindowGroupTree()
+    {
+    // start from the front, wg count can reduce as loop runs
+    for (TInt ii=0; ii<iWgIds.Count();)
+        {
+        RWsSession::TWindowGroupChainInfo& info = iWgIds[ii];
+        if (info.iParentId > 0)        // wg has a parent
+            {
+            // Look for the parent position
+            TInt parentPos = ii;        // use child pos as not-found signal
+            TInt count = iWgIds.Count();
+            for (TInt jj=0; jj<count; jj++)
+                {
+                if (iWgIds[jj].iId == info.iParentId)
+                    {
+                    parentPos = jj;
+                    break;
+                    }
+                }
+
+            if (parentPos > ii)  // parent should be moved forward
+                {
+                iWgIds[ii] = iWgIds[parentPos];
+                iWgIds.Remove(parentPos);
+                }
+            else if (parentPos < ii)  // parent is already ahead of child, remove child
+                iWgIds.Remove(ii);
+            else                    // parent not found, skip
+                ii++;
+            }
+        else    // wg does not have a parent, skip
+            ii++;
+        }
+    }
+
+void CMemoryMonitor::AppNotExiting(TInt aWgId)
+    {
+    if (aWgId == iCurrentTask.WgId())
+        CloseAppEvent();
+    }
+
+
+// ======================================================================
+// class COutOfMemoryWatcher
+// - notifies when free memory crosses preset thresholds
+// ======================================================================
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+COutOfMemoryWatcher* COutOfMemoryWatcher::NewL(CMemoryMonitor& aMonitor, TInt aLowThreshold, TInt aGoodThreshold)
+    {
+    COutOfMemoryWatcher* self = new (ELeave) COutOfMemoryWatcher(aMonitor);
+    CleanupStack::PushL(self);
+    self->ConstructL(aLowThreshold, aGoodThreshold);
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+COutOfMemoryWatcher::~COutOfMemoryWatcher()
+    {
+    Cancel();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+COutOfMemoryWatcher::COutOfMemoryWatcher(CMemoryMonitor& aMonitor)
+:   CActive(CActive::EPriorityStandard),
+    iLafShutdown(aMonitor)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void COutOfMemoryWatcher::ConstructL(TInt aLowThreshold, TInt aGoodThreshold)
+    {
+    UserSvr::SetMemoryThresholds(aLowThreshold,aGoodThreshold);
+    User::LeaveIfError(iChangeNotifier.Create());
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void COutOfMemoryWatcher::Start()
+    {
+    if (!IsActive())
+        {
+        iChangeNotifier.Logon(iStatus);
+        SetActive();
+        }
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void COutOfMemoryWatcher::DoCancel()
+    {
+    iChangeNotifier.LogonCancel();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void COutOfMemoryWatcher::RunL()
+    {
+    TInt status = iStatus.Int();
+
+    if (status < 0)
+        {
+        User::Leave(status);
+        }
+
+    // Check for memory status change.
+    if (status & EChangesFreeMemory)
+        {
+        iLafShutdown.FreeMemThresholdCrossedL();
+        }
+
+    // We are not active until FreeMemThresholdCrossedL returns.
+    Start();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+CSubscribeHelper::CSubscribeHelper(TCallBack aCallBack, RProperty& aProperty)
+    : CActive(EPriorityNormal), iCallBack(aCallBack), iProperty(aProperty)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+CSubscribeHelper::~CSubscribeHelper()
+    {
+    Cancel();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CSubscribeHelper::SubscribeL()
+    {
+    if (!IsActive())
+        {
+        iProperty.Subscribe(iStatus);
+        SetActive();
+        }
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CSubscribeHelper::StopSubscribe()
+    {
+    Cancel();
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CSubscribeHelper::RunL()
+    {
+    if (iStatus.Int() == KErrNone)
+        {
+        iCallBack.CallBack();
+        SubscribeL();
+        }
+    }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CSubscribeHelper::DoCancel()
+    {
+    iProperty.Cancel();
+    }
+
+
+COomPlugins::COomPlugins()
+    {
+    }
+
+COomPlugins::~COomPlugins()
+    {
+    TInt count = iPlugins.Count();
+    for (TInt ii=0; ii<count; ii++)
+        {
+        TPlugin& plugin = iPlugins[ii];
+        if (plugin.iImpl)    // only if successfully added
+            REComSession::DestroyedImplementation(plugin.iDtorUid);
+        }
+    iPlugins.Close();
+    }
+
+void COomPlugins::ConstructL()
+    {
+    RImplInfoPtrArray implArray;
+    CleanupClosePushL(implArray);
+    REComSession::ListImplementationsL(KOomPluginInterfaceUid, implArray);
+
+    TInt count = implArray.Count();
+    iPlugins.ReserveL(count);
+
+    for (TInt ii=0; ii<count; ii++)
+        {
+        iPlugins.AppendL(TPlugin());
+        TPlugin& plugin = iPlugins[ii];
+        TUid uid(implArray[ii]->ImplementationUid());
+        plugin.iImpl = static_cast<COomMonitorPlugin*>(REComSession::CreateImplementationL(uid, plugin.iDtorUid, NULL));
+        }
+    
+    CleanupStack::PopAndDestroy(&implArray);
+    }
+
+void COomPlugins::FreeRam()
+    {
+    TInt count = iPlugins.Count();
+    for (TInt ii=0; ii<count; ii++)
+        {
+        TPlugin& plugin = iPlugins[ii];
+        plugin.iImpl->FreeRam();
+        }
+    }
+
+void COomPlugins::MemoryGood()
+    {
+    TInt count = iPlugins.Count();
+    for (TInt ii=0; ii<count; ii++)
+        {
+        TPlugin& plugin = iPlugins[ii];
+        plugin.iImpl->MemoryGood();
+        }
+    }
+
+COomPlugins::TPlugin::TPlugin()
+: iImpl(0)
+    {
+    }
+
+
+CAppCloseTimer* CAppCloseTimer::NewL(CMemoryMonitor& aMonitor)
+    {
+    CAppCloseTimer* self = new(ELeave)CAppCloseTimer(aMonitor);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+CAppCloseTimer::CAppCloseTimer(CMemoryMonitor& aMonitor)
+: CTimer(CActive::EPriorityStandard), iMonitor(aMonitor)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+void CAppCloseTimer::RunL()
+    {
+    iMonitor.CloseAppEvent();
+    }
+
+
+
+CAppCloseWatcher::CAppCloseWatcher(CMemoryMonitor& aMonitor)
+: CActive(CActive::EPriorityStandard), iMonitor(aMonitor)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+CAppCloseWatcher::~CAppCloseWatcher()
+    {
+    Cancel();
+    }
+
+void CAppCloseWatcher::Start(const TApaTask& aTask)
+    {
+    TInt err = iThread.Open(aTask.ThreadId());
+    if (err == KErrNone)
+        {
+        iOriginalProcessPriority = iThread.ProcessPriority();
+        iThread.SetProcessPriority(EPriorityForeground);
+        iThread.Logon(iStatus);
+        SetActive();
+        }
+    else
+        {
+        iStatus = KRequestPending;
+        TRequestStatus* s = &iStatus;
+        User::RequestComplete(s, err);
+        SetActive();
+        }
+    }
+
+void CAppCloseWatcher::DoCancel()
+    {
+    iThread.LogonCancel(iStatus);
+    iThread.SetProcessPriority(iOriginalProcessPriority);
+    iThread.Close();
+    }
+
+void CAppCloseWatcher::RunL()
+    {
+    if (iThread.Handle())
+        iThread.SetProcessPriority(iOriginalProcessPriority);
+    iThread.Close();
+    // Experimentation shows that memory may take up to 40ms
+    // to be released back to the system after app thread close.
+    // Using this delay should minimise the number of apps that
+    // need to be closed to recover the necessary memory.
+    const TInt KAppTidyUpDelay = 40000;
+    User::After(KAppTidyUpDelay);
+    iMonitor.CloseAppEvent();
+    }
+
+
+
+CWservEventReceiver::CWservEventReceiver(CMemoryMonitor& aMonitor, RWsSession& aWs)
+: CActive(CActive::EPriorityStandard), iMonitor(aMonitor), iWs(aWs), iWg(aWs)
+    {
+    CActiveScheduler::Add(this);
+    }
+
+CWservEventReceiver::~CWservEventReceiver()
+    {
+    Cancel();
+    iWg.Close();
+    }
+
+void CWservEventReceiver::ConstructL()
+    {
+    User::LeaveIfError(iWg.Construct((TUint32)this, EFalse));
+    iWg.SetOrdinalPosition(0, ECoeWinPriorityNeverAtFront);
+    iWg.EnableFocusChangeEvents();
+    Queue();
+    }
+
+void CWservEventReceiver::Queue()
+    {
+    iWs.EventReady(&iStatus);
+    SetActive();
+    }
+
+void CWservEventReceiver::DoCancel()
+    {
+    iWs.EventReadyCancel();
+    }
+
+void CWservEventReceiver::RunL()
+    {
+    TWsEvent event;
+    iWs.GetEvent(event);
+    if (event.Type() == EEventFocusGroupChanged)
+        iMonitor.HandleFocusedWgChangeL();
+    Queue();
+    }
+
+
+
+CMemoryMonitorServer* CMemoryMonitorServer::NewL(CMemoryMonitor& aMonitor)
+    {
+    CMemoryMonitorServer* self=new(ELeave) CMemoryMonitorServer(aMonitor);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+CMemoryMonitorServer::~CMemoryMonitorServer()
+    {
+    }
+
+CMemoryMonitorServer::CMemoryMonitorServer(CMemoryMonitor& aMonitor)
+:CServer2(CActive::EPriorityStandard), iMonitor(aMonitor)
+    {
+    }
+
+void CMemoryMonitorServer::ConstructL()
+    {
+    StartL(KMemoryMonitorServerName);
+    }
+
+CSession2* CMemoryMonitorServer::NewSessionL(const TVersion& /*aVersion*/, const RMessage2& /*aMessage*/) const
+    {
+    return new(ELeave) CMemoryMonitorSession();
+    }
+
+TInt CMemoryMonitorServer::RunError(TInt aError)
+    {
+    Message().Complete(aError);
+    //
+    // The leave will result in an early return from CServer::RunL(), skipping
+    // the call to request another message. So do that now in order to keep the
+    // server running.
+    ReStart();
+    return KErrNone;    // handled the error fully
+    }
+
+CMemoryMonitor& CMemoryMonitorServer::Monitor()
+    {
+    return iMonitor;
+    }
+
+void CMemoryMonitorServer::CloseAppsFinished(TBool aMemoryGood)
+    {
+    iSessionIter.SetToFirst();
+    CSession2* s;
+    while ((s = iSessionIter++) != 0)
+        static_cast<CMemoryMonitorSession*>(s)->CloseAppsFinished(aMemoryGood);
+    }
+
+
+CMemoryMonitorSession::CMemoryMonitorSession()
+    {
+    }
+
+void CMemoryMonitorSession::CreateL()
+    {
+    }
+
+CMemoryMonitorSession::~CMemoryMonitorSession()
+    {
+    }
+
+CMemoryMonitorServer& CMemoryMonitorSession::Server()
+    {
+    return *static_cast<CMemoryMonitorServer*>(const_cast<CServer2*>(CSession2::Server()));
+    }
+
+CMemoryMonitor& CMemoryMonitorSession::Monitor()
+    {
+    return Server().Monitor();
+    }
+
+void CMemoryMonitorSession::ServiceL(const RMessage2& aMessage)
+    {
+    aMessage.HasCapabilityL(ECapabilityWriteDeviceData); //Leaves if client has not correct capability
+    switch (aMessage.Function())
+        {
+        case EOomMonitorRequestFreeMemory:
+            if (!iRequestFreeRam.IsNull())
+                PanicClient(aMessage, EPanicRequestActive);
+            // message will be completed when CloseAppsFinished() is called.
+            iRequestFreeRam = aMessage;
+            Monitor().StartFreeSomeRamL(aMessage.Int0());
+            break;
+
+        case EOomMonitorCancelRequestFreeMemory:
+            if (!iRequestFreeRam.IsNull())
+                iRequestFreeRam.Complete(KErrCancel);
+            aMessage.Complete(KErrNone);
+            break;
+
+        case EOomMonitorThisAppIsNotExiting:
+            Monitor().AppNotExiting(aMessage.Int0());
+            aMessage.Complete(KErrNone);
+            break;
+
+        default:
+            PanicClient(aMessage, EPanicIllegalFunction);
+            break;
+        }
+    }
+
+void CMemoryMonitorSession::CloseAppsFinished(TBool aMemoryGood)
+    {
+    if (!iRequestFreeRam.IsNull())
+        iRequestFreeRam.Complete(aMemoryGood ? KErrNone : KErrNoMemory);
+    }
+
+
+void PanicClient(const RMessagePtr2& aMessage,TOomMonitorClientPanic aPanic)
+    {
+    aMessage.Panic(KMemoryMonitorServerName, aPanic);
+    }
+
+// End of file.