diff -r 000000000000 -r 15bf7259bb7c uiacceltk/hitchcock/goommonitor/src/goomactionlist.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uiacceltk/hitchcock/goommonitor/src/goomactionlist.cpp Tue Feb 02 07:56:43 2010 +0200 @@ -0,0 +1,683 @@ +/* +* Copyright (c) 2008 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: Classes for executing OOM actions (e.g. closing applications and running plugins). +* +*/ + + +#include +#include + +#include "goommonitorplugin.h" +#include "goommonitorplugin.hrh" +#include "goomactionlist.h" +#include "goomwindowgrouplist.h" +#include "goompanic.h" +#include "goomtraces.h" +#include "goomconstants.hrh" +#include "goommemorymonitor.h" +#include "goommemorymonitorserver.h" +#include "goomrunplugin.h" +#include "goomcloseapp.h" +#include "goomconfig.h" +#include "goomactionref.h" +#include "goomapplicationconfig.h" +#include "goomcloseappconfig.h" +#include "goomrunpluginconfig.h" + +template +CGOomPluginList::CGOomPluginList() + { + FUNC_LOG; + } + +template +CGOomPluginList::~CGOomPluginList() + { + FUNC_LOG; + + TInt count = iPlugins.Count(); + for (TInt ii=0; ii +void CGOomPluginList::ConstructL(TInt aInterfaceUid) + { + FUNC_LOG; + + RImplInfoPtrArray implArray; + CleanupClosePushL(implArray); + REComSession::ListImplementationsL(TUid::Uid(aInterfaceUid), implArray); + + TInt count = implArray.Count(); + iPlugins.ReserveL(count); + + for (TInt ii=0; iiImplementationUid()); + plugin.iImpl = static_cast(REComSession::CreateImplementationL(uid, plugin.iDtorUid, NULL)); + plugin.iUid = uid.iUid; + } + + CleanupStack::PopAndDestroy(&implArray); + } + +template +CGOomPluginList::TPlugin::TPlugin() +: iImpl(0) + { + FUNC_LOG; + } + +template +CGOomPluginList* CGOomPluginList::NewL(TInt aInterfaceUid) + { + FUNC_LOG; + + CGOomPluginList* self = new (ELeave) CGOomPluginList(); + CleanupStack::PushL(self); + self->ConstructL(aInterfaceUid); + CleanupStack::Pop(self); + return self; + } + +CGOomActionList* CGOomActionList::NewL(CMemoryMonitor& aMonitor, CMemoryMonitorServer& aServer, RWsSession& aWs, CGOomConfig& aConfig) + { + FUNC_LOG; + + CGOomActionList* self = new (ELeave) CGOomActionList(aMonitor, aServer, aWs); + CleanupStack::PushL(self); + self->ConstructL(aConfig); + CleanupStack::Pop(); + return self; + } + +CGOomActionList::~CGOomActionList() + { + FUNC_LOG; + + iCloseAppActions.ResetAndDestroy(); + iCloseAppActions.Close(); + iRunPluginActions.ResetAndDestroy(); + iRunPluginActions.Close(); + + iActionRefs.Close(); + + delete iPluginList; + + } + +// Creates a list of actions based on the contents of the config and the current window group list +// Any old actions will be replaced. +// This function may leave, however enough memory should be reserved for this process so that at least +// some actions have been created for freeing memory, these can then be run by calling FreeMemory. +// Note that this function will only leave in extreme circumstances, in normal usage we should have +// enough reserved memory to build the complete action list. +void CGOomActionList::BuildPluginActionListL(CGOomWindowGroupList& aWindowGroupList, CGOomConfig& aConfig) + { + FUNC_LOG; + + if (iFreeingMemory) + { + TRACES("OOMWATCHER:CGOomActionList::BuildActionListL Memory is currently being freed, do not build action list"); + TRACES2("OOMWATCHER:CGOomActionList::BuildActionListL iCurrentActionIndex = %d out of %d actions in progress", iCurrentActionIndex, iActionRefs.Count()); + return; + } + + iActionRefs.Reset(); + iCurrentActionIndex = 0; + + TInt actionsIndex = 0; + + iAppsProtectedByPlugins.Reset(); + + iRunningKillAppActions = EFalse; + + // + // There is only one kind of plugin, the graphics plugin which is based on the oom plugin + // references to v2 plugin types removed as these are not yet used by GOOM + // we rely on the pluginlist not having changed since construction + + // Go through each plugin in the plugin list, create a run-plugin action for each one + TInt pluginIndex = iPluginList->Count(); + while (pluginIndex--) + { + // Get the config for this plugin + CGOomRunPluginConfig& pluginConfig = aConfig.GetPluginConfig(iPluginList->Uid(pluginIndex)); + TInt priority = pluginConfig.CalculatePluginPriority(aWindowGroupList); + + TGOomSyncMode syncMode = pluginConfig.iSyncMode; + TInt ramEstimate = pluginConfig.iRamEstimate; + + TActionRef::TActionType actionType; + + if (pluginConfig.PluginType() == EGOomAppPlugin) + { + actionType = TActionRef::EAppPlugin; + } + else + actionType = TActionRef::ESystemPlugin; + + TActionRef ref = TActionRef(actionType, priority, syncMode, ramEstimate, *(iRunPluginActions[actionsIndex]), aWindowGroupList.GetIndexFromAppId(pluginConfig.TargetApp())); + iAppsProtectedByPlugins.Append(pluginConfig.TargetApp()); + TRACES1("Creating Plugin Action Item TargetAppId %x", pluginConfig.TargetApp()); + //It is valid to have plugins with equal priority + User::LeaveIfError(iActionRefs.InsertInOrderAllowRepeats(ref, ComparePriorities)); + + actionsIndex++; + } + + TRACES1("BuildActionListL: Action list built with %d Plugin items",iActionRefs.Count()); + } + +void CGOomActionList::BuildKillAppActionListL(CGOomWindowGroupList& aWindowGroupList, CGOomConfig& aConfig) + { + FUNC_LOG; + + iActionRefs.Reset(); + iCurrentActionIndex = 0; + + aWindowGroupList.RefreshL(); + + iRunningKillAppActions = ETrue; + + if (aWindowGroupList.Count()) + { + // Go through each item in the wglist, create an app close action for this application + TInt wgIndex = aWindowGroupList.Count() - 1; + + TRACES1("BuildActionListL: Windowgroup list count %d ",aWindowGroupList.Count()); + + // Fix for when fast swap has focus, or pen input server has put itself into the foreground: + // the wg at index 1 should be considered as the foreground app. + // stopIndex is calculated to take this into account. + TUid foregroundUid = TUid::Uid(iMonitor.ForegroundAppUid()); + + TRACES1("BuildActionListL: Foreground App %x ", foregroundUid); + + while (wgIndex >= 0) + { + CGOomCloseAppConfig* appCloseConfig = NULL; + + CApaWindowGroupName* wgName = aWindowGroupList.WgName(); + __ASSERT_DEBUG(wgName, GOomMonitorPanic(KInvalidWgName)); + + // Get the app ID for the wglist item + // This sets the window group name + TInt32 appId = aWindowGroupList.AppId(wgIndex, ETrue); + + if ( !appId || foregroundUid.iUid ==appId || wgName->IsSystem() || wgName->Hidden() || (iAppsProtectedByPlugins.Find(appId) != KErrNotFound)) + { + //If the UID is NULL at this point, we assume the process is not an application + //and therefore is not a suitable candidate for closure. + //We also do not close system or hidden apps. + TRACES3("BuildActionListL: Not adding process to action list; UID = %x, wgIndex = %d, wgid = %d", appId, wgIndex, aWindowGroupList.WgId(wgIndex).iId); + TRACES3("BuildActionListL: IsSystem = %d, Hidden = %d, Foregroundapp %x", wgName->IsSystem() ? 1 : 0, wgName->Hidden() ? 1 : 0, foregroundUid); + } + + else if (aWindowGroupList.IsBusy(wgIndex) || wgName->IsBusy()) + // If the application has been flagged as busy then look up the configuration for busy apps in the config file + { + // Find the app close config for this app ID + appCloseConfig = aConfig.GetApplicationConfig(KGOomBusyAppId).GetAppCloseConfig(); + } + else if (aWindowGroupList.IsDynamicHighPriority(wgIndex)) + // If the application has been flagged as busy then look up the configuration for busy apps in the config file + { + // Find the app close config for this app ID + appCloseConfig = aConfig.GetApplicationConfig(KGOomHighPriorityAppId).GetAppCloseConfig(); + } + else + // If the application hasn't been flagged as busy or high priority then look up the priority according to the config + { + // Find the app close config for this app ID + appCloseConfig = aConfig.GetApplicationConfig(appId).GetAppCloseConfig(); + } + + // Create the app close action and add it to the action list + if (appCloseConfig) + { + TUint priority = appCloseConfig->CalculateCloseAppPriority(aWindowGroupList, wgIndex); + TInt wgId = aWindowGroupList.WgId(wgIndex).iId; + TGOomSyncMode syncMode = appCloseConfig->iSyncMode; + TInt ramEstimate = appCloseConfig->iRamEstimate; + TActionRef ref = TActionRef(TActionRef::EAppClose, priority, syncMode, ramEstimate, wgId, wgIndex); + + //AppClose Actions should always have a unique prioirity determined by the application's z order. + TInt err = iActionRefs.InsertInOrder(ref, ComparePriorities); + if ((err != KErrNone) && (err != KErrAlreadyExists)) + { + User::Leave(err); + } + TRACES3("BuildActionListL: Adding app to action list, Uid = %x, wgId = %d, wgIndex = %d", appId, wgId, wgIndex); + } + + wgIndex--; + } + } + + TRACES1("BuildActionListL: Action list built with %d items",iActionRefs.Count()); + } + + +// Execute the OOM actions according to their priority +// Run batches of OOM actions according to their sync mode +void CGOomActionList::FreeMemory(TInt aMaxPriority) + { + FUNC_LOG; + + if (iFreeingMemory) + { + TRACES("OOMWATCHER:CGOomActionList::FreeMemory Memory is currently being freed, do not start any more actions"); + return; + } + + iMaxPriority = aMaxPriority; + + TBool memoryFreeingActionRun = EFalse; + + // Get the configured maximum number of applications that can be closed at one time + const CGOomGlobalConfig& globalConfig = CMemoryMonitor::GlobalConfig(); + TInt maxBatchSize = globalConfig.iMaxCloseAppBatch; + TInt numberOfRunningActions = 0; + + TInt memoryEstimate = iMonitor.GetFreeMemory(); // The amount of free memory we expect to be free after the currently initiated operations + + while (iCurrentActionIndex < iActionRefs.Count()) + { + if(iActionRefs[iCurrentActionIndex].Priority() > aMaxPriority) + { + TRACES1("Busy App wgid %d, spared by GOOM", iActionRefs[iCurrentActionIndex].WgId()); + iCurrentActionIndex++; + continue; + } + + TActionRef ref = iActionRefs[iCurrentActionIndex]; + CGOomAction* action = NULL; + if (ref.Type() == TActionRef::EAppClose) + { + action = iCloseAppActions[numberOfRunningActions]; + static_cast(action)->Reconfigure(ref); + + //Double checking again if this app is now in foreground, if yes then we dont kill + CApaWindowGroupName* wgName = CApaWindowGroupName::NewLC(iWs, iWs.GetFocusWindowGroup()); + RDebug::Print(wgName->WindowGroupName()); + TInt32 fgApp = wgName->AppUid().iUid; + TInt32 appId = iMonitor.GetWindowGroupList()->AppIdfromWgId(ref.WgId(), ETrue); + if(appId == fgApp) + { + TRACES1("Foreground App wgid %x, spared by GOOM", appId); + + iCurrentActionIndex++; + continue; + } + } + else + { + action = &(ref.RunPlugin()); + } + + iFreeingMemory = ETrue; + TRACES2("CGOomActionList::FreeMemory: Running action %d which has priority %d", iCurrentActionIndex,ref.Priority()); + action->FreeMemory(iCurrentTarget - memoryEstimate); + memoryFreeingActionRun = ETrue; + + // Actions with EContinueIgnoreMaxBatchSize don't add to the tally of running actions + if (ref.SyncMode() != EContinueIgnoreMaxBatchSize) + numberOfRunningActions++; + + // Update our estimate of how much RAM we think we'll have after this operation + memoryEstimate += ref.RamEstimate(); + + // Do we estimate that we are freeing enough memory (only applies if the sync mode is "esimtate" for this action) + TBool estimatedEnoughMemoryFreed = EFalse; + if ((ref.SyncMode() == EEstimate) + && (memoryEstimate >= iCurrentTarget)) + { + estimatedEnoughMemoryFreed = ETrue; + } + + if ((ref.SyncMode() == ECheckRam) + || (numberOfRunningActions >= maxBatchSize) + || estimatedEnoughMemoryFreed + || globalConfig.ForceCheckAtPriority(iActionRefs[iCurrentActionIndex].Priority())) + // If this actions requires a RAM check then wait for it to complete + // Also force a check if we've reached the maximum number of concurrent operations + // Also check if we estimate that we have already freed enough memory (assuming that the sync mode is "estimate" + { + // Return from the loop - we will be called back (in CGOomActionList::StateChanged()) when the running actions complete + TRACES("CGOomActionList::FreeMemory: Exiting run action loop"); + return; + } + // ... otherwise continue running actions, don't wait for any existing ones to complete + iCurrentActionIndex++; + } + + + if (!memoryFreeingActionRun) + { + // No usable memory freeing action has been found, so we give up + TRACES("CGOomActionList::FreeMemory: No usable memory freeing action has been found"); + iMonitor.ResetTargets(); + TInt freeMemory; + if (FreeMemoryAboveTarget(freeMemory) && !iMonitor.NeedToPostponeMemGood()) + { + MemoryGood(); + } + iServer.CloseAppsFinished(freeMemory, EFalse); + } + } + +// Should be called when the memory situation is good +// It results in notifications of the good memory state to all plugins with an outstanding FreeMemory request +void CGOomActionList::MemoryGood() + { + FUNC_LOG; + + TInt actionRefIndex = iActionRefs.Count(); + + // Go through each of the action references, if it's a plugin action then call MemoryGood on it + // Note that this only results in a call to the plugin if FreeMemory has been called on this plugin since that last call to MemoryGood + while (actionRefIndex--) + { + if ((iActionRefs[actionRefIndex].Type() == TActionRef::EAppPlugin) + || (iActionRefs[actionRefIndex].Type() == TActionRef::ESystemPlugin)) + { + iActionRefs[actionRefIndex].RunPlugin().MemoryGood(); + } + } + } + +TBool CGOomActionList::FreeMemoryAboveTarget(TInt& aFreeMemory) + { + FUNC_LOG; + + aFreeMemory = iMonitor.GetFreeMemory(); + + TRACES1("CGOomActionList::FreeMemoryAboveTarget: Free RAM now %d",aFreeMemory); + + return (aFreeMemory >= iCurrentTarget); + } + +TInt CGOomActionList::ComparePriorities(const TActionRef& aPos1, const TActionRef& aPos2 ) + { + FUNC_LOG; + + if (aPos1.Priority()< aPos2.Priority()) + { + return -1; + } + if (aPos1.Priority() > aPos2.Priority()) + { + return 1; + } + + // If priorities are equal then we use hardcoded rules to determine which one is run... + + // All other actions are run in preference to application closures + if ((aPos1.Type() != TActionRef::EAppClose) + && ((aPos2.Type() == TActionRef::EAppClose))) + { + return -1; + } + if ((aPos1.Type() == TActionRef::EAppClose) + && ((aPos2.Type() != TActionRef::EAppClose))) + { + return 1; + } + // If both actions are application closures then the Z order is used to determine which one to run (furthest back application will be closed first) + if ((aPos1.Type() == TActionRef::EAppClose) + && ((aPos2.Type() == TActionRef::EAppClose))) + { + if (aPos1.WgIndex() < aPos2.WgIndex()) + { + return 1; + } + else + { + return -1; + } + //Two Apps should not have equal window group indexes, we panic below if this is the case. + } + + // Application plugins will be run in preference to system plugins + if ((aPos1.Type() == TActionRef::EAppPlugin) + && ((aPos2.Type() == TActionRef::ESystemPlugin))) + { + return -1; + } + if ((aPos1.Type() == TActionRef::ESystemPlugin) + && ((aPos2.Type() == TActionRef::EAppPlugin))) + { + return 1; + } + + // If both actions are application plugins then the Z order of the target app is used to determine which one to run (the one with the furthest back target app will be closed first) + // If the target app is not running then the plugin is run in preference to target apps where the target app is running + if ((aPos1.Type() == TActionRef::EAppPlugin) + && ((aPos2.Type() == TActionRef::EAppPlugin))) + { + // When the target app is not running then the plugin will be run ahead of plugins where the target app is running + if ((aPos1.WgIndex() == KAppNotInWindowGroupList) && (aPos2.WgIndex() != KAppNotInWindowGroupList)) + { + return -1; + } + if ((aPos1.WgIndex() != KAppNotInWindowGroupList) && (aPos2.WgIndex() == KAppNotInWindowGroupList)) + { + return 1; + } + // If the target apps for both plugins are running then compare the Z order + if ((aPos1.WgIndex() != KAppNotInWindowGroupList) && (aPos2.WgIndex() != KAppNotInWindowGroupList)) + { + if (aPos1.WgIndex() < aPos2.WgIndex()) + { + return 1; + } + else + { + return -1; + } + } + // If the target app for neither plugin is running then they are of equal priority + } + //App Close Actions must have a unique priority. + __ASSERT_DEBUG((aPos1.Type() != TActionRef::EAppClose) && (aPos2.Type() != TActionRef::EAppClose), GOomMonitorPanic(KAppCloseActionEqualPriorities)); + return 0; + } + +void CGOomActionList::AppNotExiting(TInt aWgId) + { + FUNC_LOG; + + TInt index = iCloseAppActions.Count(); + while (index--) + { + CGOomCloseApp* action = iCloseAppActions[index]; + if ( (action->WgId() == aWgId) && (action->IsRunning()) ) + { + TRACES1("CGOomCloseApp::AppNotExiting: App with window group id %d has not responded to the close event", aWgId); + action->CloseAppEvent(); + } + } + } + +// From MGOomActionObserver +// An action has changed state (possibly it has completed freeing memory) +void CGOomActionList::StateChanged() + { + FUNC_LOG; + + TBool allActionsComplete = ETrue; + + // Note that the actions themselves are responsible for timing out if necessary. + TInt index = iCloseAppActions.Count(); + while ((index--) && (allActionsComplete)) + { + if (iCloseAppActions[index]->IsRunning()) + { + TRACES1("CGOomActionList::StateChanged. CloseAppAction %d STILL RUNNING. PROBLEM !!! YOU SHOULD NEVER SEE THIS", index); + allActionsComplete = EFalse; + } + } + + index = iRunPluginActions.Count(); + while ((index--) && (allActionsComplete)) + { + if (iRunPluginActions[index]->IsRunning()) + { + TRACES1("CGOomActionList::StateChanged. PluginAction %d STILL RUNNING. PROBLEM !!! YOU SHOULD NEVER SEE THIS", index); + allActionsComplete = EFalse; + } + } + + TRACES1("CGOomActionList::StateChanged. Current Target = %d", iCurrentTarget); + + if (allActionsComplete) + { + iFreeingMemory = EFalse; + // If all of the actions are complete then check memory and run more if necessary + TInt freeMemory; + TBool freeMemoryAboveTarget = FreeMemoryAboveTarget(freeMemory); + TRACES1("CGOomActionList::StateChanged. Free Memory = %d", freeMemory); + if (!freeMemoryAboveTarget) + // If we are still below the good-memory-threshold then continue running actions + { + TRACES2("CGOomActionList::StateChanged: Finished Action %d out of %d",iCurrentActionIndex, iActionRefs.Count()); + + iCurrentActionIndex++; + + if (iCurrentActionIndex >= iActionRefs.Count()) + { + if(iRunningKillAppActions) + { + iRunningKillAppActions = EFalse; + // There are no more actions to try, so we give up + TRACES1("CGOomActionList::StateChanged: All current actions complete, below good threshold with no more actions available. freeMemory=%d", freeMemory); + iMonitor.ResetTargets(); + /* Do not call memory good immidiately after freeing memory for some app + if (freeMemory >= iCurrentTarget && !iMonitor.NeedToPostponeMemGood()) + { + MemoryGood(); + } + */ + iServer.CloseAppsFinished(freeMemory, EFalse); + } + else + { + TRACES1("CGOomActionList::StateChanged: All current Plugin actions complete, below good threshold, Time to kill bad guys. freeMemory=%d", freeMemory); + iRunningKillAppActions = ETrue; + iMonitor.RunCloseAppActions(iMaxPriority); + } + } + else + { + // There are still more actions to try, so we continue + TRACES1("CGOomActionList::StateChanged: All current actions complete, running more actions. freeMemory=%d", freeMemory); + FreeMemory(iMaxPriority); + } + } + else + { + TRACES1("CGOomActionList::StateChanged: All current actions complete, memory good. freeMemory=%d", freeMemory); + /* Do not call memory good immidiately after freeing memory for some app + if(freeMemory > iMonitor.GetGoodThreshold() && !iMonitor.NeedToPostponeMemGood()) + { + MemoryGood(); + } + */ + iRunningKillAppActions = EFalse; + iMonitor.ResetTargets(); + iServer.CloseAppsFinished(freeMemory, ETrue); + } + } + + // If some actions are not yet in the idle state then we must continue to wait for them (they will notify us of a state change via a callback) + } + +CGOomActionList::CGOomActionList(CMemoryMonitor& aMonitor, CMemoryMonitorServer& aServer, RWsSession& aWs) + : iWs(aWs), iMonitor(aMonitor), iServer(aServer) + { + FUNC_LOG; + } + +void CGOomActionList::ConstructL(CGOomConfig& aConfig) + { + FUNC_LOG; + + iCurrentActionIndex = 0; + iFreeingMemory = EFalse; + + // Get the list of plugins available to the system + + iPluginList = CGOomPluginList::NewL(KGOomPluginInterfaceUidValue); + // Go through each plugin in the plugin list, create a run-plugin action for each one + TInt pluginIndex = iPluginList->Count(); + while (pluginIndex--) + { + // Get the config for this plugin + CGOomRunPluginConfig& pluginConfig = aConfig.GetPluginConfig(iPluginList->Uid(pluginIndex)); + + // Create an action acording to the config + CGOomRunPlugin* action = CGOomRunPlugin::NewL(iPluginList->Uid(pluginIndex), pluginConfig, *this, iPluginList->Implementation(pluginIndex)); + + iRunPluginActions.AppendL(action); + } + + //references to v2 plugin types removed as these are not yet used by GOOM + + //allocate empty CGOomCloseApp objects + TInt appCloseIndex = aConfig.GlobalConfig().iMaxCloseAppBatch; + while (appCloseIndex--) + { + CGOomCloseApp* action = CGOomCloseApp::NewL(*this, iWs); + iCloseAppActions.AppendL(action); + } + } + +void CGOomActionList::SetPriority(TInt aWgIndex, TInt aPriority) + { + FUNC_LOG; + + TInt idx = iActionRefs.Count()-1; + while(idx >= 0) + { + if(iActionRefs[idx].WgIndex() == aWgIndex) + { + break; + } + idx--; + } + + if(idx >= 0) + { + TRACES2("CGOomActionList::SetPriority Setting app wgindex %d, index %d as busy", aWgIndex, idx); + iActionRefs[idx].SetPriority(aPriority); + if (!iFreeingMemory) + { + iActionRefs.Sort(ComparePriorities); + } + } + else + { + TRACES1("CGOomActionList::SetPriority wgindex %d not in the hitlist", aWgIndex); + } + }