sysresmonitoring/oommonitor/src/oommemorymonitor.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:43:04 +0300
branchRCL_3
changeset 82 4610cd70c542
parent 63 c2c61fdca848
permissions -rw-r--r--
Revision: 201039 Kit: 201041

/*
* 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:  Main classes for Out of Memory Monitor.
*
*/


#include <hal.h>

#include <UikonInternalPSKeys.h>

#include "oommemorymonitor.h"
#include "oommonitorplugin.h"
#include "oomsubscribehelper.h"
#include "oomconfig.h"
#include "oommemorymonitorserver.h"
#include "oomconfigparser.h"
#include "oomactionlist.h"
#include "oomlog.h"
#include "OomTraces.h"
#include "oomoutofmemorywatcher.h"
#include "oomwserveventreceiver.h"
#include "oomconstants.hrh"
#include "oomrunpluginconfig.h"
#include "oomapplicationconfig.h"
#include "oomclientrequestqueue.h"

#ifndef CLIENT_REQUEST_QUEUE
const TInt KOomWatchDogStatusIdle = -1;
#endif

// ======================================================================
// class CMemoryMonitor
// ======================================================================

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
CMemoryMonitor* CMemoryMonitor::NewL()
    { // static
    FUNC_LOG;

    CMemoryMonitor* self = new(ELeave) CMemoryMonitor();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

CMemoryMonitor::CMemoryMonitor()
    {
    FUNC_LOG;

    SetMemoryMonitorTls(this);
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
CMemoryMonitor::~CMemoryMonitor()
    {
    FUNC_LOG;

#ifndef CLIENT_REQUEST_QUEUE
    if (iWatchdogStatusSubscriber)
        {
        iWatchdogStatusSubscriber->StopSubscribe();
        }
    iWatchdogStatusProperty.Close();
    delete iWatchdogStatusSubscriber;
#endif
    
    delete iServer;
    delete iWservEventReceiver;
    delete iOOMWatcher;
    iFs.Close();
    iWs.Close();
    
    delete iOomWindowGroupList;
    delete iOomActionList;
    delete iConfig;
#ifdef CLIENT_REQUEST_QUEUE
    delete iQueue;
#endif
    
#ifdef _DEBUG    
    delete iLogger;
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMemoryMonitor::ConstructL()
    {
    FUNC_LOG;

    User::LeaveIfError(iWs.Connect());
    
    iOomWindowGroupList = COomWindowGroupList::NewL(iWs);
    
    iConfig = COomConfig::NewL(); 
    
#ifdef CLIENT_REQUEST_QUEUE
    iQueue = COomClientRequestQueue::NewL(*this);
    
    iServer = CMemoryMonitorServer::NewL(*iQueue);
#else
    iServer = CMemoryMonitorServer::NewL(*this);    
#endif
    
   // Load up threshold & OOM app lists from resource.
    User::LeaveIfError(iFs.Connect());    
    
    COomConfigParser* oomConfigParser = new (ELeave) COomConfigParser(*iConfig, iFs);
    CleanupStack::PushL(oomConfigParser);
    oomConfigParser->ParseL();
    CleanupStack::PopAndDestroy(oomConfigParser);

    iOomActionList = COomActionList::NewL(*this, *iServer, iWs, *iConfig);
    
#ifdef _DEBUG    
    iLogger = COomLogger::NewL(iWs, iFs);
#endif
    
    // Get the thresholds based on the current foreground app and the config
    RefreshThresholds();
    
    _LIT_SECURITY_POLICY_S0(KOomMemoryMonitorPolicyWrite, KOomMemoryMonitorStatusPropertyCategory.iUid);
    _LIT_SECURITY_POLICY_PASS(KOomMemoryMonitorPolicyRead);

    // Define MemoryMonitorStatusProperty. set to "above treshhold".
    TInt err = RProperty::Define(KOomMemoryMonitorStatusPropertyCategory, KOomMemoryMonitorStatusPropertyKey, RProperty::EInt, KOomMemoryMonitorPolicyRead, KOomMemoryMonitorPolicyWrite);
    TRACES1("CMemoryMonitor::ConstructL: KOomMemoryMonitorStatusProperty: Define err = %d", err);
    
    err = RProperty::Set(KOomMemoryMonitorStatusPropertyCategory, KOomMemoryMonitorStatusPropertyKey, EAboveTreshHold);
    TRACES1("CMemoryMonitor::ConstructL:  KOomMemoryMonitorStatusProperty: Set err = %d", err);            

#ifndef CLIENT_REQUEST_QUEUE
    err = iWatchdogStatusProperty.Attach(KPSUidUikon, KUikOOMWatchdogStatus);
    
    TRACES1("CMemoryMonitor::ConstructL: KUikOOMWatchdogStatus err = %d", err);
        
    err = iWatchdogStatusProperty.Set(KOomWatchDogStatusIdle);
    
    iWatchdogStatusSubscriber = new (ELeave) CSubscribeHelper(TCallBack(WatchdogStatusStatusChanged, this), iWatchdogStatusProperty);
    iWatchdogStatusSubscriber->Subscribe();
#endif
    
    
    iOOMWatcher = COutOfMemoryWatcher::NewL(*this, iLowThreshold, iGoodThreshold);
    iOOMWatcher->Start();

    iWservEventReceiver = new(ELeave) CWservEventReceiver(*this, iWs);
    iWservEventReceiver->ConstructL();
    }

const COomGlobalConfig& CMemoryMonitor::GlobalConfig()
    {
    CMemoryMonitor* globalMemoryMonitor = static_cast<CMemoryMonitor*>(Dll::Tls());
    return globalMemoryMonitor->iConfig->GlobalConfig();
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMemoryMonitor::FreeMemThresholdCrossedL()
    {
    FUNC_LOG;

    iActionTrigger = ERamRotation;
    StartFreeSomeRamL(iGoodThreshold);
    }

void CMemoryMonitor::HandleFocusedWgChangeL()
    {
    FUNC_LOG;

    TInt oldGoodThreshold = iGoodThreshold;
    TInt oldLowThreshold = iLowThreshold;
    
    // Refresh the low and good memory thresholds as they may have changed due to the new foreground application
    RefreshThresholds();
       
    if ((oldGoodThreshold != iGoodThreshold)
            || (oldLowThreshold != iLowThreshold))
        // If the thresholds have changed then update the memory watched
        {
        iOOMWatcher->UpdateThresholds(iLowThreshold, iGoodThreshold);
        }
    
    // If the available memory is less than the low memory threshold then free some RAM
    User::CompressAllHeaps();
    TInt current = 0;
    HAL::Get( HALData::EMemoryRAMFree, current );
    
    if (current < iLowThreshold)
        {
        iActionTrigger = ERamRotation;
        StartFreeSomeRamL(iGoodThreshold);
        }
    }

void CMemoryMonitor::StartFreeSomeRamL(TInt aTargetFree)
    {
    StartFreeSomeRamL(aTargetFree, KOomPriorityInfinate - 1);
    }

void CMemoryMonitor::StartFreeSomeRamL(TInt aTargetFree, TInt aMaxPriority) // The maximum priority of action to run
    {
    FUNC_LOG;

    TRACES2("MemoryMonitor::StartFreeSomeRamL: aTargetFree = %d, iCurrentTarget = %d", aTargetFree, iCurrentTarget);
    
    // Update the target if new target is higher. If the target is lower than the current target and memory 
    // is currently being freed then we do not want to reduce the amount of memory this operation frees.
    if (aTargetFree > iCurrentTarget)
        iCurrentTarget = aTargetFree;

    // check if there is enough free memory already.
    TInt freeMemory;
    GetFreeMemory(freeMemory);

    TRACES1("MemoryMonitor::StartFreeSomeRamL, freeMemory = %d", freeMemory);
    
    if (freeMemory >= iCurrentTarget)
        {
        if (iLastMemoryMonitorStatusProperty != EFreeingMemory)
            {
        ResetTargets();
        iOomActionList->SwitchOffPlugins();
        SetMemoryMonitorStatusProperty(EAboveTreshHold);
#ifdef CLIENT_REQUEST_QUEUE        
        iQueue->ActionsCompleted(freeMemory, ETrue);
#else        
        iServer->CloseAppsFinished(freeMemory, ETrue);
#endif        
            }
        return;
        }

#ifdef _DEBUG    
    iLogger->StartL();
#endif
	
    // Build the list of memory freeing actions
    iOomActionList->BuildActionListL(*iOomWindowGroupList, *iConfig);
    
    iOomActionList->SetCurrentTarget(iCurrentTarget);
    
    // Run the memory freeing actions
    iOomActionList->FreeMemory(aMaxPriority);
    }

void CMemoryMonitor::RequestFreeMemoryPandSL(TInt aBytesRequested)
    {
    FUNC_LOG;
    
    iActionTrigger = EPublishAndSubscribe;
    StartFreeSomeRamL(aBytesRequested + iLowThreshold);
    }

void CMemoryMonitor::RequestFreeMemoryL(TInt aBytesRequested)
    {
    FUNC_LOG;
    
    iActionTrigger = EClientServerRequestFreeMemory;
    StartFreeSomeRamL(aBytesRequested + iLowThreshold);
    }

void CMemoryMonitor::FreeOptionalRamL(TInt aBytesRequested, TInt aPluginId) // The ID of the plugin that will clear up the allocation, used to determine the priority of the allocation
    {
    FUNC_LOG;
    
    iActionTrigger = EClientServerRequestOptionalRam;

    // Calculate the priority of the allocation (the priority of the plugin that will clear it up - 1)
    TInt priorityOfAllocation = iConfig->GetPluginConfig(aPluginId).CalculatePluginPriority(*iOomWindowGroupList) - 1;
   
    StartFreeSomeRamL(aBytesRequested + iGoodThreshold, priorityOfAllocation);
    }

void CMemoryMonitor::GetFreeMemory(TInt& aCurrentFreeMemory)
    {
    FUNC_LOG;

    // may cause some extra load but allows more precise action
    User::CompressAllHeaps();

    HAL::Get( HALData::EMemoryRAMFree, aCurrentFreeMemory );

    TRACES1("CMemoryMonitor::GetFreeMemory: Free RAM now %d", aCurrentFreeMemory);
    }

#ifndef CLIENT_REQUEST_QUEUE 
TInt CMemoryMonitor::WatchdogStatusStatusChanged(TAny* aPtr)
    {
    FUNC_LOG;

    CMemoryMonitor* self = STATIC_CAST(CMemoryMonitor*,aPtr);
    if (self)
        self->HandleWatchdogStatusCallBack();
    return KErrNone;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMemoryMonitor::HandleWatchdogStatusCallBack()
    {
    FUNC_LOG;

    // Someone has set the key to request some free memory.
    TInt memoryRequested = 0;
    iWatchdogStatusProperty.Get(memoryRequested);

    // Try to free the RAM.
    if (memoryRequested >= 1)
        {
        iOOMWatcher->Cancel();              // Pause memory notifys.
        TRAP_IGNORE(RequestFreeMemoryPandSL(memoryRequested + iLowThreshold)); // This call could take a few seconds to do its stuff.
        iOOMWatcher->Start();               // Restarts memory monitoring.
        }
    // Set the key back to KOomWatchDogStatusIdle to indicate we're done.
    iWatchdogStatusProperty.Set(KOomWatchDogStatusIdle);
    }
#endif //CLIENT_REQUEST_QUEUE

void CMemoryMonitor::AppNotExiting(TInt aWgId)
    {
    FUNC_LOG;

    iOomActionList->AppNotExiting(aWgId);
    }


void CMemoryMonitor::RefreshThresholds()
    {
    FUNC_LOG;

    iOomWindowGroupList->Refresh();
    
    // Calculate the desired good threshold, this could be the globally configured value...
    iGoodThreshold = CMemoryMonitor::GlobalConfig().iGoodRamThreshold;
    iLowThreshold = CMemoryMonitor::GlobalConfig().iLowRamThreshold;
    TRACES2("CMemoryMonitor::RefreshThresholds: Global Good Threshold = %d, Global Low Threshold = %d", iGoodThreshold, iLowThreshold);

#ifdef _DEBUG
    TRACES("CMemoryMonitor::RefreshThresholds: Dumping Window Group List");
    TInt wgIndex = iOomWindowGroupList->Count() - 1;
    while (wgIndex >= 0)        
        {
        TInt32 appId = iOomWindowGroupList->AppId(wgIndex, ETrue);    
        TInt wgId = iOomWindowGroupList->WgId(wgIndex).iId;
        TInt parentId = iOomWindowGroupList->WgId(wgIndex).iId;        
        TRACES4("CMemoryMonitor::RefreshThresholds: wgIndex=%d, oom uid=%x, wgId(child)=%d, parentId=%d", wgIndex, appId, wgId, parentId);    
        wgIndex--;
        }
#endif
    
    // The global value can be overridden by an app specific value
    // Find the application config entry for the foreground application
    if (iOomWindowGroupList->Count())
        {
        TUint foregroundAppId = iOomWindowGroupList->AppId(0, ETrue);
        TUid foregroundAppUid = TUid::Uid(foregroundAppId);
        
        if ( (foregroundAppUid == KUidPenInputServer || foregroundAppUid == KUidFastSwap) &&
             iOomWindowGroupList->Count() > 1 )
            {
            // pen input server puts itself to the foreground when the web browser is active
            // fast swap should not reset the thresholds for the app behind it
            foregroundAppId = iOomWindowGroupList->AppId(1, ETrue);
            }

        // If this application configuration overrides the good_ram_threshold then set it
        if (iConfig->GetApplicationConfig(foregroundAppId).iGoodRamThreshold != KOomThresholdUnset)
            {
            iGoodThreshold = iConfig->GetApplicationConfig(foregroundAppId).iGoodRamThreshold;
            TRACES2("CMemoryMonitor::RefreshThresholds: For foreground app %x, Good Threshold = %d", foregroundAppId, iGoodThreshold);
            }
        // If this application configuration overrides the low_ram_threshold then set it
        if (iConfig->GetApplicationConfig(foregroundAppId).iLowRamThreshold != KOomThresholdUnset)
            {
            iLowThreshold = iConfig->GetApplicationConfig(foregroundAppId).iLowRamThreshold;
            TRACES2("CMemoryMonitor::RefreshThresholds: For foreground app %x, Low Threshold = %d", foregroundAppId, iLowThreshold);
            }

        }
    }

// SetMemoryMonitorStatusProperty - updates the property value only if it has changed
void CMemoryMonitor::SetMemoryMonitorStatusProperty(const TMemoryMonitorStatusPropertyValues aValue)
    {
    if (iLastMemoryMonitorStatusProperty != aValue)
        {
        TInt err = RProperty::Set(KOomMemoryMonitorStatusPropertyCategory, KOomMemoryMonitorStatusPropertyKey, aValue);
        TRACES1("CMemoryMonitor::SetMemoryMonitorStatusProperty: err = %d", err);
        iLastMemoryMonitorStatusProperty = aValue;
        }
    }

void CMemoryMonitor::ResetTargets()
    {
    FUNC_LOG;

    //we reset the target when a memory free operation completes, to deal with the case 
    //where the operation was initiated with a target larger than the current good threshold
    iCurrentTarget = iGoodThreshold;
    iOomActionList->SetCurrentTarget(iCurrentTarget);   
    }

void CMemoryMonitor::SetPriorityBusy(TInt aWgId)
    {
    FUNC_LOG;

    iOomWindowGroupList->SetPriorityBusy(aWgId);
    }

void CMemoryMonitor::SetPriorityNormal(TInt aWgId)
    {
    FUNC_LOG;

    iOomWindowGroupList->SetPriorityNormal(aWgId);
    }

void CMemoryMonitor::SetPriorityHigh(TInt aWgId)
    {
    iOomWindowGroupList->SetPriorityHigh(aWgId);
    }

TActionTriggerType CMemoryMonitor::ActionTrigger() const
    {
    return iActionTrigger;
    }

#ifdef CLIENT_REQUEST_QUEUE 
TInt CMemoryMonitor::GoodThreshold() const
    {
    return iGoodThreshold;
    }

TInt CMemoryMonitor::LowThreshold() const
    {
    return iLowThreshold;
    }

void CMemoryMonitor::ActionsCompleted(TInt aBytesFree, TBool aMemoryGood)
    {
    iQueue->ActionsCompleted(aBytesFree, aMemoryGood);
    }
#endif //CLIENT_REQUEST_QUEUE