Commit c8c38693 authored by skuhne's avatar skuhne Committed by Commit bot

Adding Baseframework of the ResourceManager (all hooks and observers)

are being put in place with a very basic management scheme

The current ash implementation of the LowMemoryObserver polls
"/dev/chromeos-low-mem" to find if we are running out of memory.
After discussion with semenzato@ we decided that that is not precise
enough and we should rather poll the system memory information. Going
forward this has multiple benefits:
 1. it does not require special kernel handling.
 2. it also gives us information about GPU memory on chromeOS which
    will come handy once we are adding more sophisticated management.

Beside that, this CL adds the ResourceManager which hooks itself into
several components to detect changes in Activity's and their ordering
as well as memory pressure.

Note: The current management implementation itself is really primitive
and needs to be improved.

TBR for the DEPS file

BUG=381212, 403782
TEST=MemoryPressureTest.*, ResourceManagerTest.*
TBR=oshima@chromium.org

Review URL: https://codereview.chromium.org/513523002

Cr-Commit-Position: refs/heads/master@{#292286}
parent c99c87ac
......@@ -7,8 +7,10 @@
#include <algorithm>
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_manager_observer.h"
#include "athena/activity/public/activity_view_manager.h"
#include "base/logging.h"
#include "base/observer_list.h"
namespace athena {
......@@ -37,11 +39,17 @@ void ActivityManagerImpl::AddActivity(Activity* activity) {
activities_.push_back(activity);
ActivityViewManager* manager = ActivityViewManager::Get();
manager->AddActivity(activity);
FOR_EACH_OBSERVER(ActivityManagerObserver,
observers_,
OnActivityStarted(activity));
}
void ActivityManagerImpl::RemoveActivity(Activity* activity) {
std::vector<Activity*>::iterator find =
std::find(activities_.begin(), activities_.end(), activity);
FOR_EACH_OBSERVER(ActivityManagerObserver,
observers_,
OnActivityEnding(activity));
if (find != activities_.end()) {
activities_.erase(find);
ActivityViewManager* manager = ActivityViewManager::Get();
......@@ -54,6 +62,14 @@ void ActivityManagerImpl::UpdateActivity(Activity* activity) {
manager->UpdateActivity(activity);
}
void ActivityManagerImpl::AddObserver(ActivityManagerObserver* observer) {
observers_.AddObserver(observer);
}
void ActivityManagerImpl::RemoveObserver(ActivityManagerObserver* observer) {
observers_.RemoveObserver(observer);
}
// static
ActivityManager* ActivityManager::Create() {
ActivityViewManager::Create();
......@@ -73,4 +89,5 @@ void ActivityManager::Shutdown() {
ActivityViewManager::Shutdown();
}
} // namespace athena
......@@ -7,9 +7,12 @@
#include <vector>
#include "base/macros.h"
#include "base/observer_list.h"
namespace athena {
class ActivityManagerObserver;
class ActivityManagerImpl : public ActivityManager {
public:
ActivityManagerImpl();
......@@ -21,10 +24,14 @@ class ActivityManagerImpl : public ActivityManager {
virtual void AddActivity(Activity* activity) OVERRIDE;
virtual void RemoveActivity(Activity* activity) OVERRIDE;
virtual void UpdateActivity(Activity* activity) OVERRIDE;
virtual void AddObserver(ActivityManagerObserver* observer) OVERRIDE;
virtual void RemoveObserver(ActivityManagerObserver* observer) OVERRIDE;
private:
std::vector<Activity*> activities_;
ObserverList<ActivityManagerObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(ActivityManagerImpl);
};
......
......@@ -10,6 +10,7 @@
namespace athena {
class Activity;
class ActivityManagerObserver;
// Manages a set of activities.
class ATHENA_EXPORT ActivityManager {
......@@ -26,6 +27,9 @@ class ATHENA_EXPORT ActivityManager {
// Updates the UI when the task color/title changes.
virtual void UpdateActivity(Activity* activity) = 0;
virtual void AddObserver(ActivityManagerObserver* observer) = 0;
virtual void RemoveObserver(ActivityManagerObserver* observer) = 0;
};
} // namespace athena
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATHENA_ACTIVITY_PUBLIC_ACTIVITY_MANAGER_OBSERVER_H_
#define ATHENA_ACTIVITY_PUBLIC_ACTIVITY_MANAGER_OBSERVER_H_
#include "athena/athena_export.h"
namespace athena {
class Activity;
class ATHENA_EXPORT ActivityManagerObserver {
public:
virtual ~ActivityManagerObserver() {}
// Called after an |activity| got created.
virtual void OnActivityStarted(Activity* activity) = 0;
// Called before an |activity| gets destroyed.
virtual void OnActivityEnding(Activity* activity) = 0;
};
} // namespace athena
#endif // ATHENA_ACTIVITY_PUBLIC_ACTIVITY_MANAGER_OBSERVER_H_
......@@ -41,6 +41,7 @@
'activity/public/activity.h',
'activity/public/activity_factory.h',
'activity/public/activity_manager.h',
'activity/public/activity_manager_observer.h',
'activity/public/activity_view_manager.h',
'activity/public/activity_view_model.h',
'athena_export.h',
......@@ -141,7 +142,13 @@
'content/web_contents_view_delegate_factory_impl.cc',
'extensions/public/extensions_delegate.h',
'extensions/extensions_delegate.cc',
'resource_manager/memory_pressure_notifier.cc',
'resource_manager/memory_pressure_notifier.h',
'resource_manager/resource_manager_impl.cc',
'resource_manager/public/resource_manager.h',
'resource_manager/public/resource_manager_delegate.h',
'virtual_keyboard/public/virtual_keyboard_manager.h',
'resource_manager/resource_manager_impl.cc',
'virtual_keyboard/virtual_keyboard_manager_impl.cc',
],
},
......@@ -156,6 +163,7 @@
'content/shell/shell_app_activity.cc',
'content/shell/shell_app_activity.h',
'extensions/shell/extensions_delegate_impl.cc',
'resource_manager/delegate/resource_manager_delegate.cc',
],
},
{
......@@ -190,6 +198,7 @@
'test/sample_activity_factory.h',
'test/test_app_model_builder.cc',
'test/test_app_model_builder.h',
'test/test_resource_manager_delegate.cc',
'wm/test/window_manager_impl_test_api.cc',
'wm/test/window_manager_impl_test_api.h',
],
......@@ -213,6 +222,8 @@
'home/home_card_gesture_manager_unittest.cc',
'home/home_card_unittest.cc',
'input/accelerator_manager_unittest.cc',
'resource_manager/memory_pressure_notifier_unittest.cc',
'resource_manager/resource_manager_unittest.cc',
'screen/screen_manager_unittest.cc',
'test/athena_unittests.cc',
'wm/split_view_controller_unittest.cc',
......
......@@ -8,6 +8,7 @@
#include "athena/content/app_activity_registry.h"
#include "athena/content/public/app_registry.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/window.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/widget/widget.h"
......@@ -81,7 +82,11 @@ Activity::ActivityState AppActivity::GetCurrentState() {
}
bool AppActivity::IsVisible() {
return web_view_ && web_view_->IsDrawn();
return web_view_ &&
web_view_->IsDrawn() &&
current_state_ != ACTIVITY_UNLOADED &&
GetWindow() &&
GetWindow()->IsVisible();
}
Activity::ActivityMediaState AppActivity::GetMediaState() {
......
......@@ -42,7 +42,7 @@ Activity::ActivityState AppActivityProxy::GetCurrentState() {
}
bool AppActivityProxy::IsVisible() {
return true;
return false;
}
Activity::ActivityMediaState AppActivityProxy::GetMediaState() {
......
......@@ -9,7 +9,6 @@
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_view_model.h"
#include "athena/content/app_activity_proxy.h"
#include "ui/gfx/image/image_skia.h"
namespace athena {
......
......@@ -110,8 +110,7 @@ void AppActivityRegistry::RestartApplication(AppActivityProxy* proxy) {
DCHECK_EQ(unloaded_activity_proxy_, proxy);
// Restart the application.
ExtensionsDelegate::Get(browser_context_)->LaunchApp(app_id_);
// Remove the activity from the Activity manager.
ActivityManager::Get()->RemoveActivity(unloaded_activity_proxy_);
// Delete the activity which will also remove the it from the ActivityManager.
delete unloaded_activity_proxy_; // Will call ProxyDestroyed.
// After this call |this| might be gone if the app did not open a window yet.
}
......
......@@ -12,6 +12,7 @@
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "ui/aura/window.h"
#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/focus/focus_manager.h"
......@@ -346,7 +347,11 @@ Activity::ActivityState WebActivity::GetCurrentState() {
}
bool WebActivity::IsVisible() {
return web_view_ && web_view_->IsDrawn();
return web_view_ &&
web_view_->IsDrawn() &&
current_state_ != ACTIVITY_UNLOADED &&
GetWindow() &&
GetWindow()->IsVisible();
}
Activity::ActivityMediaState WebActivity::GetMediaState() {
......
......@@ -265,6 +265,7 @@ class HomeCardImpl : public HomeCard,
// WindowManagerObserver:
virtual void OnOverviewModeEnter() OVERRIDE;
virtual void OnOverviewModeExit() OVERRIDE;
virtual void OnActivityOrderHasChanged() OVERRIDE;
// aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active,
......@@ -479,6 +480,9 @@ void HomeCardImpl::OnOverviewModeExit() {
SetState(VISIBLE_MINIMIZED);
}
void HomeCardImpl::OnActivityOrderHasChanged() {
}
void HomeCardImpl::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
if (state_ != HIDDEN &&
......
......@@ -5,6 +5,7 @@ include_rules = [
"+athena/extensions/public",
"+athena/home/public",
"+athena/input/public",
"+athena/resource_manager/public",
"+athena/screen/public",
"+athena/system/public",
"+athena/task/public",
......
......@@ -17,6 +17,7 @@
#include "athena/main/placeholder.h"
#include "athena/main/placeholder.h"
#include "athena/main/url_search_provider.h"
#include "athena/resource_manager/public/resource_manager.h"
#include "athena/screen/public/screen_manager.h"
#include "athena/screen/public/screen_manager.h"
#include "athena/system/public/system_ui.h"
......@@ -148,11 +149,13 @@ void StartAthenaSession(athena::ActivityFactory* activity_factory,
athena::AppModelBuilder* app_model_builder) {
athena::HomeCard::Create(app_model_builder);
athena::ActivityManager::Create();
athena::ResourceManager::Create();
athena::ActivityFactory::RegisterActivityFactory(activity_factory);
}
void ShutdownAthena() {
athena::ActivityFactory::Shutdown();
athena::ResourceManager::Shutdown();
athena::ActivityManager::Shutdown();
athena::HomeCard::Shutdown();
athena::AppRegistry::ShutDown();
......
include_rules = [
"+athena/activity/public",
"+athena/content/public",
"+athena/wm/public",
"+ui/aura",
"+ui/base",
]
specific_include_rules = {
".*_unittest.cc": [
"+ui/gfx",
"+ui/views",
]
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include <string>
#include "base/logging.h"
#include "base/macros.h"
#include "base/process/process_metrics.h"
namespace athena {
namespace {
// This is the minimum amount of time in milliseconds between checks for
// memory pressure.
const int kMemoryPressureIntervalMs = 750;
} // namespace
class ResourceManagerDelegateImpl : public ResourceManagerDelegate {
public:
ResourceManagerDelegateImpl() {}
virtual ~ResourceManagerDelegateImpl() {}
private:
virtual int GetUsedMemoryInPercent() OVERRIDE {
base::SystemMemoryInfoKB memory;
// TODO(skuhne): According to semenzato this calculation has to change.
if (base::GetSystemMemoryInfo(&memory) &&
memory.total > 0 && memory.free >= 0) {
return ((memory.total - memory.free) * 100) / memory.total;
}
LOG(WARNING) << "Cannot determine the free memory of the system.";
return 0;
}
virtual int MemoryPressureIntervalInMS() OVERRIDE {
return kMemoryPressureIntervalMs;
}
DISALLOW_COPY_AND_ASSIGN(ResourceManagerDelegateImpl);
};
// static
ResourceManagerDelegate*
ResourceManagerDelegate::CreateResourceManagerDelegate() {
return new ResourceManagerDelegateImpl;
}
} // namespace athena
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include "base/time/time.h"
namespace athena {
MemoryPressureNotifier::MemoryPressureNotifier(
MemoryPressureObserver* listener)
: listener_(listener),
current_pressure_(MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN) {
StartObserving();
}
MemoryPressureNotifier::~MemoryPressureNotifier() {
StopObserving();
}
void MemoryPressureNotifier::StartObserving() {
int time_in_ms = listener_->GetDelegate()->MemoryPressureIntervalInMS();
timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(time_in_ms),
base::Bind(&MemoryPressureNotifier::CheckMemoryPressure,
base::Unretained(this)));
}
void MemoryPressureNotifier::StopObserving() {
// If StartObserving failed, StopObserving will still get called.
timer_.Stop();
}
void MemoryPressureNotifier::CheckMemoryPressure() {
MemoryPressureObserver::MemoryPressure pressure =
GetMemoryPressureLevelFromFillLevel(
listener_->GetDelegate()->GetUsedMemoryInPercent());
if (current_pressure_ != pressure ||
(pressure != MemoryPressureObserver::MEMORY_PRESSURE_LOW &&
pressure != MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN)) {
// If we are anything worse than |MEMORY_PRESSURE_LOW|, we notify the
// listener.
current_pressure_ = pressure;
listener_->OnMemoryPressure(current_pressure_);
}
}
MemoryPressureObserver::MemoryPressure
MemoryPressureNotifier::GetMemoryPressureLevelFromFillLevel(
int memory_fill_level) {
if (memory_fill_level == 0)
return MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN;
if (memory_fill_level < 50)
return MemoryPressureObserver::MEMORY_PRESSURE_LOW;
if (memory_fill_level < 75)
return MemoryPressureObserver::MEMORY_PRESSURE_MODERATE;
if (memory_fill_level < 90)
return MemoryPressureObserver::MEMORY_PRESSURE_HIGH;
return MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL;
}
} // namespace athena
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATHENA_RESOURCE_MANAGER_MEMORY_PRESSURE_NOTIFIER_H_
#define ATHENA_RESOURCE_MANAGER_MEMORY_PRESSURE_NOTIFIER_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer/timer.h"
namespace athena {
class MemoryPressureNotifierImpl;
class ResourceManagerDelegate;
////////////////////////////////////////////////////////////////////////////////
// MemoryPressureObserver
//
// This observer gets informed once about a |MEMORY_PRESSURE_LOW|. When the
// pressure exceeds, the observer will get polled until |MEMORY_PRESSURE_LOW| is
// reached again to counter memory consumption.
class MemoryPressureObserver {
public:
MemoryPressureObserver() {}
virtual ~MemoryPressureObserver() {}
// The reported memory pressure. Note: The value is intentionally abstracted
// since the real amount of free memory is only estimated (due to e.g. zram).
enum MemoryPressure {
MEMORY_PRESSURE_UNKNOWN, // The memory pressure cannot be determined.
MEMORY_PRESSURE_LOW, // Single call if memory fill level is below 50%.
MEMORY_PRESSURE_MODERATE, // Polled for memory fill level of ~50 .. 75%.
MEMORY_PRESSURE_HIGH, // Polled for memory fill level of ~75% .. 90%.
MEMORY_PRESSURE_CRITICAL, // Polled for memory fill level of above ~90%.
};
// The observer.
virtual void OnMemoryPressure(MemoryPressure pressure) = 0;
// OS system interface functions. The delegate remains owned by the Observer.
virtual ResourceManagerDelegate* GetDelegate() = 0;
};
////////////////////////////////////////////////////////////////////////////////
// MemoryPressureNotifier
//
// Class to handle the observation of our free memory. It notifies the owner of
// memory fill level changes, so that it can take action to reduce memory by
// reducing active activities.
//
// The observer will use 3 different fill levels: 50% full, 75% full and 90%
// full.
class MemoryPressureNotifier {
public:
// The creator gets the |listener| object. Note that the ownership of the
// listener object remains with the creator.
explicit MemoryPressureNotifier(MemoryPressureObserver* listener);
~MemoryPressureNotifier();
// Stop observing the memory fill level.
// May be safely called if StartObserving has not been called.
void StopObserving();
private:
// Starts observing the memory fill level.
// Calls to StartObserving should always be matched with calls to
// StopObserving.
void StartObserving();
// The function which gets periodically be called to check any changes in the
// memory pressure.
void CheckMemoryPressure();
// Converts free percent of memory into a memory pressure value.
MemoryPressureObserver::MemoryPressure GetMemoryPressureLevelFromFillLevel(
int memory_fill_level);
base::RepeatingTimer<MemoryPressureNotifier> timer_;
// The listener which needs to be informed about memory pressure.
MemoryPressureObserver* listener_;
// Our current memory pressure.
MemoryPressureObserver::MemoryPressure current_pressure_;
DISALLOW_COPY_AND_ASSIGN(MemoryPressureNotifier);
};
} // namespace athena
#endif // ATHENA_RESOURCE_MANAGER_MEMORY_PRESSURE_NOTIFIER_H_
/// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include "athena/test/athena_test_base.h"
namespace athena {
namespace test {
namespace {
// Our OS delegate abstraction class to override the memory fill level.
class TestResourceManagerDelegate : public ResourceManagerDelegate {
public:
TestResourceManagerDelegate() : memory_fill_level_percent_(0) {}
virtual ~TestResourceManagerDelegate() {}
virtual int GetUsedMemoryInPercent() OVERRIDE {
timer_called_++;
return memory_fill_level_percent_;
}
virtual int MemoryPressureIntervalInMS() OVERRIDE {
return 5;
}
void set_memory_fill_level_percent(int memory_fill_level_percent) {
memory_fill_level_percent_ = memory_fill_level_percent;
}
// Returns the number of timer calls to the GetMemoryInPercent() calls.
int timer_called() { return timer_called_; }
private:
// The to be returned memory fill level value in percent.
int memory_fill_level_percent_;
// How often was the timer calling the GetUsedMemoryInPercent() function.
int timer_called_;
DISALLOW_COPY_AND_ASSIGN(TestResourceManagerDelegate);
};
// Our memory pressure observer class.
class TestMemoryPressureObserver : public MemoryPressureObserver {
public:
TestMemoryPressureObserver(ResourceManagerDelegate* delegate)
: delegate_(delegate),
number_of_calls_(0),
pressure_(MEMORY_PRESSURE_UNKNOWN) {}
virtual ~TestMemoryPressureObserver() {}
// The observer.
virtual void OnMemoryPressure(MemoryPressure pressure) OVERRIDE {
number_of_calls_++;
pressure_ = pressure;
}
virtual ResourceManagerDelegate* GetDelegate() OVERRIDE {
return delegate_.get();
}
int number_of_calls() { return number_of_calls_; }
MemoryPressureObserver::MemoryPressure pressure() { return pressure_; }
private:
scoped_ptr<ResourceManagerDelegate> delegate_;
// Number of calls received.
int number_of_calls_;
// Last posted memory pressure.
MemoryPressureObserver::MemoryPressure pressure_;
DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureObserver);
};
} // namespace
// Our testing base.
class MemoryPressureTest : public AthenaTestBase {
public:
MemoryPressureTest() : test_resource_manager_delegate_(NULL) {}
virtual ~MemoryPressureTest() {}
// AthenaTestBase:
virtual void SetUp() OVERRIDE {
AthenaTestBase::SetUp();
// Create and install our TestAppContentDelegate with instrumentation.
test_resource_manager_delegate_ =
new TestResourceManagerDelegate();
test_memory_pressure_observer_.reset(new TestMemoryPressureObserver(
test_resource_manager_delegate_));
memory_pressure_notifier_.reset(
new MemoryPressureNotifier(test_memory_pressure_observer_.get()));
}
virtual void TearDown() OVERRIDE {
memory_pressure_notifier_.reset();
RunAllPendingInMessageLoop();
test_memory_pressure_observer_.reset();
AthenaTestBase::TearDown();
}
protected:
TestResourceManagerDelegate* test_resource_manager_delegate() {
return test_resource_manager_delegate_;
}
TestMemoryPressureObserver* test_memory_pressure_observer() {
return test_memory_pressure_observer_.get();
}
// Waits until a timer interrupt occurs. Returns false if no timer is
// registered.
bool WaitForTimer() {
int first_counter = test_resource_manager_delegate()->timer_called();
// Wait up to 500ms for any poll on our memory status function from the
// MemoryPressureNotifier.
for (int i = 0; i < 500; ++i) {
if (test_resource_manager_delegate()->timer_called() != first_counter)
return true;
usleep(1);
RunAllPendingInMessageLoop();
}
return false;
}
private:
// Not owned: the resource manager delegate.
TestResourceManagerDelegate* test_resource_manager_delegate_;
scoped_ptr<TestMemoryPressureObserver> test_memory_pressure_observer_;
scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_;
DISALLOW_COPY_AND_ASSIGN(MemoryPressureTest);
};
// Only creates and destroys it to see that the system gets properly shut down.
TEST_F(MemoryPressureTest, SimpleTest) {
}
// Test that we get only a single call while the memory pressure is low.
TEST_F(MemoryPressureTest, OneEventOnLowPressure) {
ASSERT_TRUE(WaitForTimer());
// No call should have happened at this time to the
EXPECT_FALSE(test_memory_pressure_observer()->number_of_calls());
// Set to something below 50% and check that we still get no call.
test_resource_manager_delegate()->set_memory_fill_level_percent(49);
ASSERT_TRUE(WaitForTimer());
EXPECT_EQ(1, test_memory_pressure_observer()->number_of_calls());
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_LOW,
test_memory_pressure_observer()->pressure());
ASSERT_TRUE(WaitForTimer());
EXPECT_EQ(1, test_memory_pressure_observer()->number_of_calls());
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_LOW,
test_memory_pressure_observer()->pressure());
}
// Test that we get a |MEMORY_PRESSURE_UNKNOWN| if it cannot be determined.
TEST_F(MemoryPressureTest, TestNoCallsOnMemoryPressureUnknown) {
test_resource_manager_delegate()->set_memory_fill_level_percent(0);
ASSERT_TRUE(WaitForTimer());
// We shouldn't have gotten a single call.
EXPECT_FALSE(test_memory_pressure_observer()->number_of_calls());
// And the memory pressure should be unknown.
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN,
test_memory_pressure_observer()->pressure());
}
// Test that we get a change to MODERATE if the memory pressure is at 60%.
TEST_F(MemoryPressureTest, TestModeratePressure) {
test_resource_manager_delegate()->set_memory_fill_level_percent(60);
ASSERT_TRUE(WaitForTimer());
// At least one call should have happened.
int calls = test_memory_pressure_observer()->number_of_calls();
EXPECT_TRUE(calls);
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_MODERATE,
test_memory_pressure_observer()->pressure());
// Even if the value does not change, we should get more calls.
ASSERT_TRUE(WaitForTimer());
EXPECT_LT(calls, test_memory_pressure_observer()->number_of_calls());
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_MODERATE,
test_memory_pressure_observer()->pressure());
}
// Test that increasing and decreasing the memory pressure does the right thing.
TEST_F(MemoryPressureTest, TestPressureUpAndDown) {
test_resource_manager_delegate()->set_memory_fill_level_percent(60);
ASSERT_TRUE(WaitForTimer());
// At least one call should have happened.
int calls1 = test_memory_pressure_observer()->number_of_calls();
EXPECT_TRUE(calls1);
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_MODERATE,
test_memory_pressure_observer()->pressure());
// Check to the next level.
test_resource_manager_delegate()->set_memory_fill_level_percent(80);
ASSERT_TRUE(WaitForTimer());
int calls2 = test_memory_pressure_observer()->number_of_calls();
EXPECT_LT(calls1, calls2);
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_HIGH,
test_memory_pressure_observer()->pressure());
// Check to no pressure again.
test_resource_manager_delegate()->set_memory_fill_level_percent(20);
ASSERT_TRUE(WaitForTimer());
int calls3 = test_memory_pressure_observer()->number_of_calls();
EXPECT_LT(calls2, calls3);
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_LOW,
test_memory_pressure_observer()->pressure());
// Even if the value does not change, we should not get any more calls.
ASSERT_TRUE(WaitForTimer());
EXPECT_EQ(calls3, test_memory_pressure_observer()->number_of_calls());
EXPECT_EQ(MemoryPressureObserver::MEMORY_PRESSURE_LOW,
test_memory_pressure_observer()->pressure());
}
} // namespace test
} // namespace athena
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_H_
#define ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_H_
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "base/basictypes.h"
namespace athena {
// The resource manager is monitoring activity changes, low memory conditions
// and other events to control the activity state (pre-/un-/re-/loading them)
// to keep enough memory free that no jank/lag will show when new applications
// are loaded and / or a navigation between applications takes place.
class ResourceManager {
public:
// Creates the instance handling the resources.
static void Create();
static ResourceManager* Get();
static void Shutdown();
ResourceManager();
virtual ~ResourceManager();
// Unit tests can simulate MemoryPressure changes with this call.
// Note: Even though the default unit test ResourceManagerDelegte
// implementation ensures that the MemoryPressure event will not go off,
// this call will also explicitly stop the MemoryPressureNotifier.
virtual void SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MemoryPressure pressure) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(ResourceManager);
};
} // namespace athena
#endif // ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_DLEGATE_H_
#define ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_DLEGATE_H_
#include <string>
#include "base/macros.h"
namespace athena {
// The resource manager delegate which abstracts system function calls to allow
// unit tests to override them.
class ResourceManagerDelegate {
public:
static ResourceManagerDelegate* CreateResourceManagerDelegate();
ResourceManagerDelegate() {}
virtual ~ResourceManagerDelegate() {}
// Returns the percentage of memory used in the system.
virtual int GetUsedMemoryInPercent() = 0;
// Returns the time memory pressure interval time in ms to be used by the
// memory pressure monitoring system.
virtual int MemoryPressureIntervalInMS() = 0;
};
} // namespace athena
#endif // ATHENA_RESOURCE_MANAGER_PUBLIC_RESOURCE_MANAGER_DLEGATE_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/public/resource_manager.h"
#include <algorithm>
#include <vector>
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_manager.h"
#include "athena/activity/public/activity_manager_observer.h"
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include "athena/wm/public/window_manager.h"
#include "athena/wm/public/window_manager_observer.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/window.h"
namespace athena {
class ResourceManagerImpl : public ResourceManager,
public WindowManagerObserver,
public ActivityManagerObserver,
public MemoryPressureObserver {
public:
ResourceManagerImpl(ResourceManagerDelegate* delegate);
virtual ~ResourceManagerImpl();
// ResourceManager:
virtual void SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MemoryPressure pressure) OVERRIDE;
// ActivityManagerObserver:
virtual void OnActivityStarted(Activity* activity) OVERRIDE;
virtual void OnActivityEnding(Activity* activity) OVERRIDE;
// WindowManagerObserver:
virtual void OnOverviewModeEnter() OVERRIDE;
virtual void OnOverviewModeExit() OVERRIDE;
virtual void OnActivityOrderHasChanged() OVERRIDE;
// MemoryPressureObserver:
virtual void OnMemoryPressure(
MemoryPressureObserver::MemoryPressure pressure) OVERRIDE;
virtual ResourceManagerDelegate* GetDelegate() OVERRIDE;
private:
// Manage the resources for our activities.
void ManageResource();
// Order our activity list to the order of activities of the stream.
// TODO(skuhne): Once the ActivityManager is responsible to create this list
// for us, we can remove this code here.
void UpdateActivityOrder();
// The sorted (new(front) -> old(back)) activity list.
// TODO(skuhne): Once the ActivityManager is responsible to create this list
// for us, we can remove this code here.
std::vector<Activity*> activity_list_;
// The resource manager delegate.
scoped_ptr<ResourceManagerDelegate> delegate_;
// Keeping a reference to the current memory pressure.
MemoryPressureObserver::MemoryPressure current_memory_pressure_;
// The memory pressure notifier.
scoped_ptr<MemoryPressureNotifier> memory_pressure_notifier_;
DISALLOW_COPY_AND_ASSIGN(ResourceManagerImpl);
};
namespace {
ResourceManagerImpl* instance = NULL;
} // namespace
ResourceManagerImpl::ResourceManagerImpl(ResourceManagerDelegate* delegate)
: delegate_(delegate),
current_memory_pressure_(MemoryPressureObserver::MEMORY_PRESSURE_UNKNOWN),
memory_pressure_notifier_(new MemoryPressureNotifier(this)) {
WindowManager::GetInstance()->AddObserver(this);
ActivityManager::Get()->AddObserver(this);
}
ResourceManagerImpl::~ResourceManagerImpl() {
ActivityManager::Get()->RemoveObserver(this);
WindowManager::GetInstance()->RemoveObserver(this);
}
void ResourceManagerImpl::SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MemoryPressure pressure) {
memory_pressure_notifier_->StopObserving();
OnMemoryPressure(pressure);
}
void ResourceManagerImpl::OnActivityStarted(Activity* activity) {
// As long as we have to manage the list of activities ourselves, we need to
// order it here.
activity_list_.push_back(activity);
UpdateActivityOrder();
// Update the activity states.
ManageResource();
}
void ResourceManagerImpl::OnActivityEnding(Activity* activity) {
// Remove the activity from the list again.
std::vector<Activity*>::iterator it =
std::find(activity_list_.begin(), activity_list_.end(), activity);
DCHECK(it != activity_list_.end());
activity_list_.erase(it);
}
void ResourceManagerImpl::OnOverviewModeEnter() {
// Nothing to do here.
}
void ResourceManagerImpl::OnOverviewModeExit() {
// Nothing to do here.
}
void ResourceManagerImpl::OnActivityOrderHasChanged() {
// As long as we have to manage the list of activities ourselves, we need to
// order it here.
UpdateActivityOrder();
// Manage the resources of each activity.
ManageResource();
}
void ResourceManagerImpl::OnMemoryPressure(
MemoryPressureObserver::MemoryPressure pressure) {
current_memory_pressure_ = pressure;
ManageResource();
}
ResourceManagerDelegate* ResourceManagerImpl::GetDelegate() {
return delegate_.get();
}
void ResourceManagerImpl::ManageResource() {
// If there is none or only one app running we cannot do anything.
if (activity_list_.size() <= 1U)
return;
// TODO(skuhne): This algorithm needs to take all kinds of predictive analysis
// and running applications into account. For this first patch we only do a
// very simple "floating window" algorithm which is surely not good enough.
size_t max_running_activities = 5;
switch (current_memory_pressure_) {
case MEMORY_PRESSURE_UNKNOWN:
// If we do not know how much memory we have we assume that it must be a
// high consumption.
// Fallthrough.
case MEMORY_PRESSURE_HIGH:
max_running_activities = 5;
break;
case MEMORY_PRESSURE_CRITICAL:
max_running_activities = 0;
break;
case MEMORY_PRESSURE_MODERATE:
max_running_activities = 7;
break;
case MEMORY_PRESSURE_LOW:
// No need to do anything yet.
return;
}
Activity* oldest_media_activity = NULL;
std::vector<Activity*> unloadable_activities;
for (std::vector<Activity*>::iterator it = activity_list_.begin();
it != activity_list_.end(); ++it) {
// The activity should not be unloaded or visible.
if ((*it)->GetCurrentState() != Activity::ACTIVITY_UNLOADED &&
!(*it)->IsVisible()) {
if ((*it)->GetMediaState() == Activity::ACTIVITY_MEDIA_STATE_NONE) {
// Does not play media - so we can unload this immediately.
unloadable_activities.push_back(*it);
} else {
oldest_media_activity = *it;
}
}
}
if (unloadable_activities.size() > max_running_activities) {
unloadable_activities.back()->SetCurrentState(Activity::ACTIVITY_UNLOADED);
return;
} else if (current_memory_pressure_ == MEMORY_PRESSURE_CRITICAL) {
if (oldest_media_activity) {
oldest_media_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED);
return;
}
LOG(ERROR) << "[ResourceManager]: Single activity uses too much memory.";
return;
}
if (current_memory_pressure_ != MEMORY_PRESSURE_UNKNOWN) {
// Only show this warning when the memory pressure is actually known. This
// will suppress warnings in e.g. unit tests.
LOG(WARNING) << "[ResourceManager]: No way to release memory pressure (" <<
current_memory_pressure_ <<
"), Activities (running, allowed, unloadable)=(" <<
activity_list_.size() << ", " <<
max_running_activities << ", " <<
unloadable_activities.size() << ")";
}
}
void ResourceManagerImpl::UpdateActivityOrder() {
if (activity_list_.empty())
return;
std::vector<Activity*> new_activity_list;
const aura::Window::Windows children =
activity_list_[0]->GetWindow()->parent()->children();
// Find the first window in the container which is part of the application.
for (aura::Window::Windows::const_iterator child_iterator = children.begin();
child_iterator != children.end(); ++child_iterator) {
for (std::vector<Activity*>::iterator activity_iterator =
activity_list_.begin();
activity_iterator != activity_list_.end(); ++activity_iterator) {
if (*child_iterator == (*activity_iterator)->GetWindow()) {
new_activity_list.push_back(*activity_iterator);
activity_list_.erase(activity_iterator);
break;
}
}
}
// At this point the old list should be empty and we can swap the lists.
DCHECK(!activity_list_.size());
activity_list_ = new_activity_list;
}
// static
void ResourceManager::Create() {
DCHECK(!instance);
instance = new ResourceManagerImpl(
ResourceManagerDelegate::CreateResourceManagerDelegate());
}
// static
ResourceManager* ResourceManager::Get() {
return instance;
}
// static
void ResourceManager::Shutdown() {
DCHECK(instance);
delete instance;
instance = NULL;
}
ResourceManager::ResourceManager() {}
ResourceManager::~ResourceManager() {
DCHECK(instance);
instance = NULL;
}
} // namespace athena
/// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/activity/public/activity.h"
#include "athena/activity/public/activity_manager.h"
#include "athena/activity/public/activity_view_model.h"
#include "athena/resource_manager/memory_pressure_notifier.h"
#include "athena/resource_manager/public/resource_manager.h"
#include "athena/test/athena_test_base.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace athena {
namespace test {
namespace {
// A dummy test app activity which works without content / ShellAppWindow.
class TestActivity : public Activity,
public ActivityViewModel {
public:
TestActivity(std::string title) : title_(base::UTF8ToUTF16(title)),
view_(new views::View()),
current_state_(ACTIVITY_UNLOADED),
media_state_(ACTIVITY_MEDIA_STATE_NONE),
is_visible_(false) {};
virtual ~TestActivity() {}
void set_media_state(ActivityMediaState media_state) {
media_state_ = media_state;
}
void set_visible(bool visible) { is_visible_ = visible; }
// Activity overrides:
virtual ActivityViewModel* GetActivityViewModel() OVERRIDE { return this; }
virtual void SetCurrentState(ActivityState state) OVERRIDE {
current_state_ = state;
}
virtual ActivityState GetCurrentState() OVERRIDE { return current_state_; }
virtual bool IsVisible() OVERRIDE { return is_visible_; }
virtual ActivityMediaState GetMediaState() OVERRIDE { return media_state_; }
virtual aura::Window* GetWindow() OVERRIDE {
return view_->GetWidget()->GetNativeWindow();
}
// ActivityViewModel overrides:
virtual void Init() OVERRIDE {}
virtual SkColor GetRepresentativeColor() const OVERRIDE { return 0; }
virtual base::string16 GetTitle() const OVERRIDE { return title_; }
virtual bool UsesFrame() const OVERRIDE { return true; }
virtual views::View* GetContentsView() OVERRIDE { return view_; }
virtual void CreateOverviewModeImage() OVERRIDE {}
virtual gfx::ImageSkia GetOverviewModeImage() OVERRIDE { return image_; }
private:
// The presentation values.
const base::string16 title_;
gfx::ImageSkia image_;
// The associated view.
views::View* view_;
// The current state.
ActivityState current_state_;
// The current media state.
ActivityMediaState media_state_;
// Returns if it is visible or not.
bool is_visible_;
DISALLOW_COPY_AND_ASSIGN(TestActivity);
};
} // namespace
// Our testing base.
class ResourceManagerTest : public AthenaTestBase {
public:
ResourceManagerTest() {}
virtual ~ResourceManagerTest() {}
virtual void TearDown() OVERRIDE {
while (!activity_list_.empty())
CloseActivity(activity_list_[0]);
AthenaTestBase::TearDown();
}
TestActivity* CreateActivity(const std::string& title) {
TestActivity* activity = new TestActivity(title);
ActivityManager::Get()->AddActivity(activity);
activity->SetCurrentState(Activity::ACTIVITY_INVISIBLE);
activity_list_.push_back(activity);
return activity;
}
void CloseActivity(Activity* activity) {
delete activity;
RunAllPendingInMessageLoop();
std::vector<TestActivity*>::iterator it = std::find(activity_list_.begin(),
activity_list_.end(),
activity);
DCHECK(it != activity_list_.end());
activity_list_.erase(it);
}
private:
std::vector<TestActivity*> activity_list_;
DISALLOW_COPY_AND_ASSIGN(ResourceManagerTest);
};
// Only creates and destroys it to see that the system gets properly shut down.
TEST_F(ResourceManagerTest, SimpleTest) {
}
// Test that we release an activity when the memory pressure goes critical.
TEST_F(ResourceManagerTest, OnCriticalWillUnloadOneActivity) {
// create a few dummy activities.
TestActivity* app_visible = CreateActivity("visible");
TestActivity* app_unloadable1 = CreateActivity("unloadable1");
TestActivity* app_unloadable2 = CreateActivity("unloadable2");
app_visible->set_visible(true);
app_unloadable1->set_visible(false);
app_unloadable2->set_visible(false);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_unloadable1->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_unloadable2->GetCurrentState());
// Call the resource manager and say we are in a critical memory condition.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_unloadable1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable2->GetCurrentState());
// Calling it a second time will release the second app.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable2->GetCurrentState());
// Calling it once more will change nothing.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable2->GetCurrentState());
}
// Test that media playing activities only get unloaded if there is no other
// way.
TEST_F(ResourceManagerTest, OnCriticalMediaHandling) {
// create a few dummy activities.
TestActivity* app_visible = CreateActivity("visible");
TestActivity* app_media_locked1 = CreateActivity("medialocked1");
TestActivity* app_unloadable = CreateActivity("unloadable2");
TestActivity* app_media_locked2 = CreateActivity("medialocked2");
app_visible->set_visible(true);
app_unloadable->set_visible(false);
app_media_locked1->set_visible(false);
app_media_locked2->set_visible(false);
app_media_locked1->set_media_state(
Activity::ACTIVITY_MEDIA_STATE_AUDIO_PLAYING);
app_media_locked2->set_media_state(Activity::ACTIVITY_MEDIA_STATE_RECORDING);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_media_locked1->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_unloadable->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_media_locked2->GetCurrentState());
// Calling it with a critical situation first, it will release the non media
// locked app.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_media_locked1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_media_locked2->GetCurrentState());
// Calling it the second time, the oldest media playing activity will get
// unloaded.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_media_locked1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_media_locked2->GetCurrentState());
// Calling it the third time, the oldest media playing activity will get
// unloaded.
ResourceManager::Get()->SetMemoryPressureAndStopMonitoring(
MemoryPressureObserver::MEMORY_PRESSURE_CRITICAL);
DCHECK_NE(Activity::ACTIVITY_UNLOADED, app_visible->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_media_locked1->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_unloadable->GetCurrentState());
DCHECK_EQ(Activity::ACTIVITY_UNLOADED, app_media_locked2->GetCurrentState());
}
} // namespace test
} // namespace athena
......@@ -6,6 +6,7 @@ include_rules = [
"+athena/home/public",
"+athena/screen/public",
"+athena/main",
"+athena/resource_manager/public",
"+chromeos/dbus",
"+third_party/skia/include",
"+ui/app_list",
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/resource_manager/public/resource_manager_delegate.h"
#include <string>
#include "base/macros.h"
namespace athena {
namespace test {
namespace {
// This is the minimum amount of time in milliseconds between checks for
// low memory. Since we report no memory use (and the timeout is extremely long)
// the memory pressure event will never go off.
const int kMemoryPressureIntervalMs = 60000;
} // namespace
class ResourceManagerDelegateImpl : public ResourceManagerDelegate {
public:
ResourceManagerDelegateImpl() {}
virtual ~ResourceManagerDelegateImpl() {}
private:
virtual int GetUsedMemoryInPercent() OVERRIDE {
return 0;
}
virtual int MemoryPressureIntervalInMS() OVERRIDE {
return kMemoryPressureIntervalMs;
}
DISALLOW_COPY_AND_ASSIGN(ResourceManagerDelegateImpl);
};
} // namespace test
// static
ResourceManagerDelegate*
ResourceManagerDelegate::CreateResourceManagerDelegate() {
return new test::ResourceManagerDelegateImpl;
}
} // namespace athena
......@@ -18,6 +18,9 @@ class ATHENA_EXPORT WindowManagerObserver {
// Called immediately after going out of the overview mode.
virtual void OnOverviewModeExit() = 0;
// Called after the activity order has changed.
virtual void OnActivityOrderHasChanged() = 0;
};
} // namespace athena
......
......@@ -241,6 +241,15 @@ void WindowManagerImpl::OnWindowDestroying(aura::Window* window) {
container_.reset();
}
void WindowManagerImpl::OnWindowStackingChanged(aura::Window* window) {
// TODO(skuhne): Use |window_list_provider_->IsValidWindow(window)| instead.
if (window->type() == ui::wm::WINDOW_TYPE_NORMAL) {
FOR_EACH_OBSERVER(WindowManagerObserver,
observers_,
OnActivityOrderHasChanged());
}
}
bool WindowManagerImpl::IsCommandEnabled(int command_id) const {
return true;
}
......
......@@ -70,6 +70,7 @@ class WindowManagerImpl : public WindowManager,
// aura::WindowObserver:
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
// AcceleratorHandler:
virtual bool IsCommandEnabled(int command_id) const OVERRIDE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment