Commit a0b9be91 authored by Abhishek Bhardwaj's avatar Abhishek Bhardwaj Committed by Commit Bot

arc: Add wake lock based logic to re-suspend after dark resume

This change adds functionality to support lock screen notifications for
Android. The wake lock module now listens to dark resume notifications
from the power manager. It -

1. Starts a timer to check for partial wake locks acquired by Android.

2. After the timer in 1 expires, if no partial wake lock is acquired the
power manager is requested to re-suspend the system. If a partial wake
lock is acquired, then another hard timeout timer is set and also an
observer to wake lock release is set.

3. If wake lock is released, the system is re-suspended immediately.

4. If a wake lock is still acquired but the hard timeout expires then
the system is re-suspended immediately.

5. If the system transitions to a full resume all dark resume related
state and timers are cleared.

BUG=chromium:898297
TEST=Unit tests and end to end test with Android applications.

Change-Id: I6dbae0c5f63af67637032b62d0c56cae27d08bad
Reviewed-on: https://chromium-review.googlesource.com/c/1297474
Commit-Queue: Abhishek Bhardwaj <abhishekbh@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602980}
parent 45ec05e7
...@@ -66,6 +66,7 @@ static_library("arc") { ...@@ -66,6 +66,7 @@ static_library("arc") {
"volume_mounter/arc_volume_mounter_bridge.h", "volume_mounter/arc_volume_mounter_bridge.h",
"wake_lock/arc_wake_lock_bridge.cc", "wake_lock/arc_wake_lock_bridge.cc",
"wake_lock/arc_wake_lock_bridge.h", "wake_lock/arc_wake_lock_bridge.h",
"wake_lock/wake_lock_observer.h",
] ]
public_deps = [ public_deps = [
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/observer_list.h"
#include "base/task/post_task.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_policy_controller.h" #include "chromeos/dbus/power_policy_controller.h"
#include "components/arc/arc_bridge_service.h" #include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
...@@ -46,7 +49,8 @@ class ArcWakeLockBridgeFactory ...@@ -46,7 +49,8 @@ class ArcWakeLockBridgeFactory
// WakeLockRequester requests a wake lock from the device service in response // WakeLockRequester requests a wake lock from the device service in response
// to wake lock requests of a given type from Android. A count is kept of // to wake lock requests of a given type from Android. A count is kept of
// outstanding Android requests so that only a single actual wake lock is used. // outstanding Android requests so that only a single actual wake lock is
// used.
class ArcWakeLockBridge::WakeLockRequester { class ArcWakeLockBridge::WakeLockRequester {
public: public:
WakeLockRequester(device::mojom::WakeLockType type, WakeLockRequester(device::mojom::WakeLockType type,
...@@ -76,6 +80,9 @@ class ArcWakeLockBridge::WakeLockRequester { ...@@ -76,6 +80,9 @@ class ArcWakeLockBridge::WakeLockRequester {
} }
wake_lock_->RequestWakeLock(); wake_lock_->RequestWakeLock();
for (auto& observer : observers_)
observer.OnWakeLockAcquire();
} }
// Decrements the number of outstanding Android requests. Cancels the device // Decrements the number of outstanding Android requests. Cancels the device
...@@ -94,10 +101,27 @@ class ArcWakeLockBridge::WakeLockRequester { ...@@ -94,10 +101,27 @@ class ArcWakeLockBridge::WakeLockRequester {
} }
DCHECK(wake_lock_); DCHECK(wake_lock_);
DVLOG(1) << "Partial wake force release. Count: " << wake_lock_count_; DVLOG(1) << "Partial wake lock force release. Count: " << wake_lock_count_;
wake_lock_->CancelWakeLock(); wake_lock_->CancelWakeLock();
for (auto& observer : observers_)
observer.OnWakeLockRelease();
}
bool IsWakeLockHeld() const { return wake_lock_count_ > 0; }
void AddObserver(WakeLockObserver* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
} }
void RemoveObserver(WakeLockObserver* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
bool HasObservers() const { return observers_.might_have_observers(); }
// Runs the message loop until replies have been received for all pending // Runs the message loop until replies have been received for all pending
// requests on |wake_lock_|. // requests on |wake_lock_|.
void FlushForTesting() { void FlushForTesting() {
...@@ -118,6 +142,8 @@ class ArcWakeLockBridge::WakeLockRequester { ...@@ -118,6 +142,8 @@ class ArcWakeLockBridge::WakeLockRequester {
// Lazily initialized in response to first request. // Lazily initialized in response to first request.
device::mojom::WakeLockPtr wake_lock_; device::mojom::WakeLockPtr wake_lock_;
base::ObserverList<WakeLockObserver>::Unchecked observers_;
DISALLOW_COPY_AND_ASSIGN(WakeLockRequester); DISALLOW_COPY_AND_ASSIGN(WakeLockRequester);
}; };
...@@ -145,11 +171,16 @@ ArcWakeLockBridge::ArcWakeLockBridge(content::BrowserContext* context, ...@@ -145,11 +171,16 @@ ArcWakeLockBridge::ArcWakeLockBridge(content::BrowserContext* context,
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
arc_bridge_service_->wake_lock()->SetHost(this); arc_bridge_service_->wake_lock()->SetHost(this);
arc_bridge_service_->wake_lock()->AddObserver(this); arc_bridge_service_->wake_lock()->AddObserver(this);
chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
this);
} }
ArcWakeLockBridge::~ArcWakeLockBridge() { ArcWakeLockBridge::~ArcWakeLockBridge() {
arc_bridge_service_->wake_lock()->RemoveObserver(this); arc_bridge_service_->wake_lock()->RemoveObserver(this);
arc_bridge_service_->wake_lock()->SetHost(nullptr); arc_bridge_service_->wake_lock()->SetHost(nullptr);
// In case some this wasn't cleared while handling a dark resume.
GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
->RemoveObserver(this);
} }
void ArcWakeLockBridge::OnConnectionClosed() { void ArcWakeLockBridge::OnConnectionClosed() {
...@@ -157,11 +188,6 @@ void ArcWakeLockBridge::OnConnectionClosed() { ...@@ -157,11 +188,6 @@ void ArcWakeLockBridge::OnConnectionClosed() {
wake_lock_requesters_.clear(); wake_lock_requesters_.clear();
} }
void ArcWakeLockBridge::FlushWakeLocksForTesting() {
for (const auto& it : wake_lock_requesters_)
it.second->FlushForTesting();
}
void ArcWakeLockBridge::AcquirePartialWakeLock( void ArcWakeLockBridge::AcquirePartialWakeLock(
AcquirePartialWakeLockCallback callback) { AcquirePartialWakeLockCallback callback) {
GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension) GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
...@@ -176,6 +202,106 @@ void ArcWakeLockBridge::ReleasePartialWakeLock( ...@@ -176,6 +202,106 @@ void ArcWakeLockBridge::ReleasePartialWakeLock(
std::move(callback).Run(true); std::move(callback).Run(true);
} }
void ArcWakeLockBridge::DarkSuspendImminent() {
DVLOG(1) << __func__;
suspend_readiness_cb_ = chromeos::DBusThreadManager::Get()
->GetPowerManagerClient()
->GetSuspendReadinessCallback(FROM_HERE);
// Post task that will check for any wake locks acquired in dark resume.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ArcWakeLockBridge::HandleDarkResumeWakeLockCheckTimeout,
dark_resume_weak_ptr_factory_.GetWeakPtr()),
kDarkResumeWakeLockCheckTimeout);
}
void ArcWakeLockBridge::SuspendDone(const base::TimeDelta& sleep_duration) {
DVLOG(1) << __func__;
// Clear any dark resume state when the device resumes.
ClearDarkResumeState();
}
void ArcWakeLockBridge::OnWakeLockRelease() {
// This observer is only registered once dark resume starts.
DCHECK(suspend_readiness_cb_);
DVLOG(1) << __func__;
// At this point the instance has done it's work, tell the power daemon to
// re-suspend.
std::move(suspend_readiness_cb_).Run();
ClearDarkResumeState();
}
void ArcWakeLockBridge::FlushWakeLocksForTesting() {
for (const auto& it : wake_lock_requesters_)
it.second->FlushForTesting();
}
bool ArcWakeLockBridge::IsSuspendReadinessStateSetForTesting() const {
if (suspend_readiness_cb_)
return true;
return false;
}
bool ArcWakeLockBridge::WakeLockHasObserversForTesting(
device::mojom::WakeLockType type) {
return GetWakeLockRequester(
device::mojom::WakeLockType::kPreventAppSuspension)
->HasObservers();
}
void ArcWakeLockBridge::HandleDarkResumeWakeLockCheckTimeout() {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(dark_resume_tasks_sequence_checker_);
// Check if any wake locks are held at this point. If not, then it's assumed
// the instance either acquired and released one or had no reason to acquire
// one in the first place. If it wants to after this then too bad, tell the
// power daemon to re-suspend and invalidate any other state associated with
// dark resume.
if (!GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
->IsWakeLockHeld()) {
DVLOG(1) << "Wake lock not held during check";
std::move(suspend_readiness_cb_).Run();
ClearDarkResumeState();
return;
}
DVLOG(1) << "Wake lock held during check";
// If a wake lock is held then register for a wake lock release
// notification. As soon as it's released tell power daemon to re-suspend.
// If the instance takes a long time then tell powerd daemon to re-suspend
// after a hard timeout irrespective of wake locks held.
GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
->AddObserver(this);
// Post task that will tell the power daemon to re-suspend after a dark
// resume irrespective of any state. This is a last resort timeout to ensure
// the device doesn't stay up indefinitely in dark resume.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ArcWakeLockBridge::HandleDarkResumeHardTimeout,
dark_resume_weak_ptr_factory_.GetWeakPtr()),
kDarkResumeHardTimeout);
}
void ArcWakeLockBridge::HandleDarkResumeHardTimeout() {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(dark_resume_tasks_sequence_checker_);
// Enough is enough. Tell power daemon it's okay to suspend.
DCHECK(suspend_readiness_cb_);
std::move(suspend_readiness_cb_).Run();
ClearDarkResumeState();
}
void ArcWakeLockBridge::ClearDarkResumeState() {
DVLOG(1) << __func__;
// Invalidate all other state associated with dark resume.
suspend_readiness_cb_.Reset();
GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
->RemoveObserver(this);
dark_resume_weak_ptr_factory_.InvalidateWeakPtrs();
}
ArcWakeLockBridge::WakeLockRequester* ArcWakeLockBridge::GetWakeLockRequester( ArcWakeLockBridge::WakeLockRequester* ArcWakeLockBridge::GetWakeLockRequester(
device::mojom::WakeLockType type) { device::mojom::WakeLockType type) {
auto it = wake_lock_requesters_.find(type); auto it = wake_lock_requesters_.find(type);
......
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chromeos/dbus/power_manager_client.h"
#include "components/arc/common/wake_lock.mojom.h" #include "components/arc/common/wake_lock.mojom.h"
#include "components/arc/connection_observer.h" #include "components/arc/connection_observer.h"
#include "components/arc/wake_lock/wake_lock_observer.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/common/service_manager_connection.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
...@@ -30,7 +33,9 @@ class ArcBridgeService; ...@@ -30,7 +33,9 @@ class ArcBridgeService;
// Sets wake up timers / alarms based on calls from the instance. // Sets wake up timers / alarms based on calls from the instance.
class ArcWakeLockBridge : public KeyedService, class ArcWakeLockBridge : public KeyedService,
public ConnectionObserver<mojom::WakeLockInstance>, public ConnectionObserver<mojom::WakeLockInstance>,
public mojom::WakeLockHost { public mojom::WakeLockHost,
public chromeos::PowerManagerClient::Observer,
public WakeLockObserver {
public: public:
// Returns the factory instance for this class. // Returns the factory instance for this class.
static BrowserContextKeyedServiceFactory* GetFactory(); static BrowserContextKeyedServiceFactory* GetFactory();
...@@ -54,13 +59,36 @@ class ArcWakeLockBridge : public KeyedService, ...@@ -54,13 +59,36 @@ class ArcWakeLockBridge : public KeyedService,
// ConnectionObserver<mojom::WakeLockInstance>::Observer overrides. // ConnectionObserver<mojom::WakeLockInstance>::Observer overrides.
void OnConnectionClosed() override; void OnConnectionClosed() override;
// mojom::WakeLockHost overrides.
void AcquirePartialWakeLock(AcquirePartialWakeLockCallback callback) override;
void ReleasePartialWakeLock(ReleasePartialWakeLockCallback callback) override;
// chromeos::PowerManagerClient::Observer overrides.
void DarkSuspendImminent() override;
void SuspendDone(const base::TimeDelta& sleep_duration) override;
// WakeLockObserver override.
void OnWakeLockRelease() override;
// Runs the message loop until replies have been received for all pending // Runs the message loop until replies have been received for all pending
// device service requests in |wake_lock_requesters_|. // device service requests in |wake_lock_requesters_|.
void FlushWakeLocksForTesting(); void FlushWakeLocksForTesting();
// mojom::WakeLockHost overrides. // Checks if |suspend_readiness_cb_| is set.
void AcquirePartialWakeLock(AcquirePartialWakeLockCallback callback) override; bool IsSuspendReadinessStateSetForTesting() const;
void ReleasePartialWakeLock(ReleasePartialWakeLockCallback callback) override;
// Returns true iff wake lock of |type| has observers.
bool WakeLockHasObserversForTesting(device::mojom::WakeLockType type);
// Time after a dark resume when wake lock count is checked and a decision is
// made to re-suspend or wait for wake lock release.
static constexpr base::TimeDelta kDarkResumeWakeLockCheckTimeout =
base::TimeDelta::FromSeconds(3);
// Max time to wait for wake lock release after a wake lock check after a dark
// resume. After this time the system is asked to re-suspend.
static constexpr base::TimeDelta kDarkResumeHardTimeout =
base::TimeDelta::FromSeconds(10);
private: private:
class WakeLockRequester; class WakeLockRequester;
...@@ -68,6 +96,20 @@ class ArcWakeLockBridge : public KeyedService, ...@@ -68,6 +96,20 @@ class ArcWakeLockBridge : public KeyedService,
// Returns the WakeLockRequester for |type|, creating one if needed. // Returns the WakeLockRequester for |type|, creating one if needed.
WakeLockRequester* GetWakeLockRequester(device::mojom::WakeLockType type); WakeLockRequester* GetWakeLockRequester(device::mojom::WakeLockType type);
// Runs |kDarkResumeWakeLockCheckTimeout| time delta after a dark resume.
// Checks if app suspension wake locks (partial wake locks for Android) are
// held after |kDarkResumeWakeLockCheckTimeout|. If no wake locks are held
// then re-suspend the device else schedule |HandleDarkResumeHardTimeout|.
void HandleDarkResumeWakeLockCheckTimeout();
// Runs |kDarkResumeHardTimeout| time delta after a
// |HandleDarkResumeWakeLockCheckTimeout|. Clears all dark resume state and
// re-suspends the device.
void HandleDarkResumeHardTimeout();
// Clears all state associated with dark resume.
void ClearDarkResumeState();
ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
// If non-null, used instead of the process-wide connector to fetch services. // If non-null, used instead of the process-wide connector to fetch services.
...@@ -78,8 +120,21 @@ class ArcWakeLockBridge : public KeyedService, ...@@ -78,8 +120,21 @@ class ArcWakeLockBridge : public KeyedService,
std::map<device::mojom::WakeLockType, std::unique_ptr<WakeLockRequester>> std::map<device::mojom::WakeLockType, std::unique_ptr<WakeLockRequester>>
wake_lock_requesters_; wake_lock_requesters_;
// Called when system is ready to supend after a |DarkSupendImminent| i.e.
// after a dark resume.
base::OnceClosure suspend_readiness_cb_;
mojo::Binding<mojom::WakeLockHost> binding_; mojo::Binding<mojom::WakeLockHost> binding_;
// Used for checking if |DarkResumeWakeLockCheckTimeout| and
// |DarkResumeHardTimeout| run on the same sequence.
SEQUENCE_CHECKER(dark_resume_tasks_sequence_checker_);
// Factory used to schedule and cancel
// |HandleDarkResumeWakeLockCheckTimeout| and |HandleDarkResumeHardTimeout|.
// At any point either none or one of these tasks is in flight.
base::WeakPtrFactory<ArcWakeLockBridge> dark_resume_weak_ptr_factory_{this};
base::WeakPtrFactory<ArcWakeLockBridge> weak_ptr_factory_; base::WeakPtrFactory<ArcWakeLockBridge> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ArcWakeLockBridge); DISALLOW_COPY_AND_ASSIGN(ArcWakeLockBridge);
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_power_manager_client.h"
#include "components/arc/arc_bridge_service.h" #include "components/arc/arc_bridge_service.h"
#include "components/arc/common/power.mojom.h" #include "components/arc/common/power.mojom.h"
#include "components/arc/test/connection_holder_util.h" #include "components/arc/test/connection_holder_util.h"
...@@ -24,7 +26,9 @@ using device::mojom::WakeLockType; ...@@ -24,7 +26,9 @@ using device::mojom::WakeLockType;
class ArcWakeLockBridgeTest : public testing::Test { class ArcWakeLockBridgeTest : public testing::Test {
public: public:
ArcWakeLockBridgeTest() { ArcWakeLockBridgeTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
auto wake_lock_provider_ptr = auto wake_lock_provider_ptr =
std::make_unique<device::TestWakeLockProvider>(); std::make_unique<device::TestWakeLockProvider>();
wake_lock_provider_ = wake_lock_provider_ptr.get(); wake_lock_provider_ = wake_lock_provider_ptr.get();
...@@ -34,6 +38,10 @@ class ArcWakeLockBridgeTest : public testing::Test { ...@@ -34,6 +38,10 @@ class ArcWakeLockBridgeTest : public testing::Test {
std::move(wake_lock_provider_ptr)); std::move(wake_lock_provider_ptr));
connector_ = connector_factory_->CreateConnector(); connector_ = connector_factory_->CreateConnector();
fake_power_manager_client_ = new chromeos::FakePowerManagerClient;
chromeos::DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
base::WrapUnique(fake_power_manager_client_));
bridge_service_ = std::make_unique<ArcBridgeService>(); bridge_service_ = std::make_unique<ArcBridgeService>();
wake_lock_bridge_ = wake_lock_bridge_ =
std::make_unique<ArcWakeLockBridge>(nullptr, bridge_service_.get()); std::make_unique<ArcWakeLockBridge>(nullptr, bridge_service_.get());
...@@ -44,28 +52,6 @@ class ArcWakeLockBridgeTest : public testing::Test { ...@@ -44,28 +52,6 @@ class ArcWakeLockBridgeTest : public testing::Test {
~ArcWakeLockBridgeTest() override { DestroyWakeLockInstance(); } ~ArcWakeLockBridgeTest() override { DestroyWakeLockInstance(); }
protected: protected:
// Returns true iff there is no failure acquiring a system wake lock.
bool AcquirePartialWakeLock() {
base::RunLoop loop;
bool result = false;
wake_lock_bridge_->AcquirePartialWakeLock(base::BindOnce(
[](bool* result_out, bool result) { *result_out = result; }, &result));
loop.RunUntilIdle();
wake_lock_bridge_->FlushWakeLocksForTesting();
return result;
}
// Returns true iff there is no failure releasing a system wake lock.
bool ReleasePartialWakeLock() {
base::RunLoop loop;
bool result = false;
wake_lock_bridge_->ReleasePartialWakeLock(base::BindOnce(
[](bool* result_out, bool result) { *result_out = result; }, &result));
loop.RunUntilIdle();
wake_lock_bridge_->FlushWakeLocksForTesting();
return result;
}
// Creates a FakeWakeLockInstance for |bridge_service_|. This results in // Creates a FakeWakeLockInstance for |bridge_service_|. This results in
// ArcWakeLockBridge::OnInstanceReady() being called. // ArcWakeLockBridge::OnInstanceReady() being called.
void CreateWakeLockInstance() { void CreateWakeLockInstance() {
...@@ -83,13 +69,18 @@ class ArcWakeLockBridgeTest : public testing::Test { ...@@ -83,13 +69,18 @@ class ArcWakeLockBridgeTest : public testing::Test {
instance_.reset(); instance_.reset();
} }
ArcWakeLockBridge* GetWakeLockBridge() { return wake_lock_bridge_.get(); }
device::TestWakeLockProvider* GetWakeLockProvider() { device::TestWakeLockProvider* GetWakeLockProvider() {
return wake_lock_provider_; return wake_lock_provider_;
} }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
// Owned by chromeos::DBusThreadManager.
chromeos::FakePowerManagerClient* fake_power_manager_client_;
private:
std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_; std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
std::unique_ptr<service_manager::Connector> connector_; std::unique_ptr<service_manager::Connector> connector_;
device::TestWakeLockProvider* wake_lock_provider_; device::TestWakeLockProvider* wake_lock_provider_;
...@@ -101,40 +92,83 @@ class ArcWakeLockBridgeTest : public testing::Test { ...@@ -101,40 +92,83 @@ class ArcWakeLockBridgeTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(ArcWakeLockBridgeTest); DISALLOW_COPY_AND_ASSIGN(ArcWakeLockBridgeTest);
}; };
namespace {
// Returns true iff there is no failure acquiring a system wake lock.
bool AcquirePartialWakeLock(ArcWakeLockBridge* wake_lock_bridge) {
base::RunLoop loop;
bool result = false;
wake_lock_bridge->AcquirePartialWakeLock(base::BindOnce(
[](bool* result_out, bool result) { *result_out = result; }, &result));
loop.RunUntilIdle();
wake_lock_bridge->FlushWakeLocksForTesting();
return result;
}
// Returns true iff there is no failure releasing a system wake lock.
bool ReleasePartialWakeLock(ArcWakeLockBridge* wake_lock_bridge) {
base::RunLoop loop;
bool result = false;
wake_lock_bridge->ReleasePartialWakeLock(base::BindOnce(
[](bool* result_out, bool result) { *result_out = result; }, &result));
loop.RunUntilIdle();
wake_lock_bridge->FlushWakeLocksForTesting();
return result;
}
// Return true iff all dark resume related state is set i.e the suspend
// readiness callback is set and wake lock release event has observers.
bool IsDarkResumeStateSet(ArcWakeLockBridge* wake_lock_bridge) {
return wake_lock_bridge->IsSuspendReadinessStateSetForTesting() &&
wake_lock_bridge->WakeLockHasObserversForTesting(
WakeLockType::kPreventAppSuspension);
}
// Return true iff all dark resume related state is reset. This should be true
// when device exits dark resume either by re-suspending or transitioning to
// full resume.
bool IsDarkResumeStateReset(ArcWakeLockBridge* wake_lock_bridge) {
return !wake_lock_bridge->WakeLockHasObserversForTesting(
WakeLockType::kPreventAppSuspension) &&
!wake_lock_bridge->IsSuspendReadinessStateSetForTesting();
}
} // namespace
TEST_F(ArcWakeLockBridgeTest, AcquireAndReleaseSinglePartialWakeLock) { TEST_F(ArcWakeLockBridgeTest, AcquireAndReleaseSinglePartialWakeLock) {
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
EXPECT_TRUE(ReleasePartialWakeLock()); EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(0, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(0, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
} }
TEST_F(ArcWakeLockBridgeTest, AcquireAndReleaseMultiplePartialWakeLocks) { TEST_F(ArcWakeLockBridgeTest, AcquireAndReleaseMultiplePartialWakeLocks) {
// Taking multiple wake locks should result in only one active wake lock. // Taking multiple wake locks should result in only one active wake lock.
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
// Releasing two wake locks after acquiring three should not result in // Releasing two wake locks after acquiring three should not result in
// releasing a wake lock. // releasing a wake lock.
EXPECT_TRUE(ReleasePartialWakeLock()); EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
EXPECT_TRUE(ReleasePartialWakeLock()); EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
// Releasing the remaining wake lock should result in the release of the wake // Releasing the remaining wake lock should result in the release of the wake
// lock. // lock.
EXPECT_TRUE(ReleasePartialWakeLock()); EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(0, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(0, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
} }
TEST_F(ArcWakeLockBridgeTest, ReleaseWakeLockOnInstanceClosed) { TEST_F(ArcWakeLockBridgeTest, ReleaseWakeLockOnInstanceClosed) {
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
ASSERT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType( ASSERT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
...@@ -150,9 +184,99 @@ TEST_F(ArcWakeLockBridgeTest, ReleaseWakeLockOnInstanceClosed) { ...@@ -150,9 +184,99 @@ TEST_F(ArcWakeLockBridgeTest, ReleaseWakeLockOnInstanceClosed) {
// Check that wake locks can be requested after the instance becomes ready // Check that wake locks can be requested after the instance becomes ready
// again. // again.
CreateWakeLockInstance(); CreateWakeLockInstance();
EXPECT_TRUE(AcquirePartialWakeLock()); EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType( EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension)); WakeLockType::kPreventAppSuspension));
} }
TEST_F(ArcWakeLockBridgeTest, CheckSuspendAfterDarkResumeNoWakeLocksHeld) {
// Trigger a dark resume event, move time forward to trigger a wake lock check
// and check if a re-suspend happened if no wake locks were acquired.
fake_power_manager_client_->SendDarkSuspendImminent();
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeWakeLockCheckTimeout);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateReset(GetWakeLockBridge()));
// Trigger a dark resume event, acquire and release a wake lock and move time
// forward to trigger a wake lock check. The device should re-suspend in this
// case since no wake locks were held at the time of the wake lock check.
fake_power_manager_client_->SendDarkSuspendImminent();
EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeWakeLockCheckTimeout);
base::RunLoop run_loop2;
run_loop2.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateReset(GetWakeLockBridge()));
}
TEST_F(ArcWakeLockBridgeTest, CheckSuspendAfterDarkResumeWakeLocksHeld) {
// Trigger a dark resume event, acquire a wake lock and move time forward to a
// wake lock check. At this point the system shouldn't re-suspend i.e. the
// suspend readiness callback should be set and wake lock release should have
// observers.
fake_power_manager_client_->SendDarkSuspendImminent();
EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeWakeLockCheckTimeout);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateSet(GetWakeLockBridge()));
// Move time forward by < |kDarkResumeHardTimeout| and release the
// partial wake lock.This should instantaneously re-suspend the device.
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeHardTimeout -
base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(ReleasePartialWakeLock(GetWakeLockBridge()));
base::RunLoop run_loop2;
run_loop2.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateReset(GetWakeLockBridge()));
}
TEST_F(ArcWakeLockBridgeTest, CheckSuspendAfterDarkResumeHardTimeout) {
// Trigger a dark resume event, acquire a wake lock and move time forward to a
// wake lock check. At this point the system shouldn't re-suspend i.e. the
// suspend readiness callback should be set and wake lock release should have
// observers.
fake_power_manager_client_->SendDarkSuspendImminent();
EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeWakeLockCheckTimeout);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateSet(GetWakeLockBridge()));
// Move time forward by |kDarkResumeHardTimeout|. At this point the
// device should re-suspend even though the wake lock is acquired.
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeHardTimeout);
EXPECT_EQ(1, GetWakeLockProvider()->GetActiveWakeLocksOfType(
WakeLockType::kPreventAppSuspension));
base::RunLoop run_loop2;
run_loop2.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateReset(GetWakeLockBridge()));
}
TEST_F(ArcWakeLockBridgeTest, CheckStateResetAfterSuspendDone) {
// Trigger a dark resume event, acquire a wake lock and move time forward to a
// wake lock check. At this point the system shouldn't re-suspend i.e. the
// suspend readiness callback should be set and wake lock release should have
// observers.
fake_power_manager_client_->SendDarkSuspendImminent();
EXPECT_TRUE(AcquirePartialWakeLock(GetWakeLockBridge()));
scoped_task_environment_.FastForwardBy(
ArcWakeLockBridge::kDarkResumeWakeLockCheckTimeout);
base::RunLoop run_loop;
run_loop.RunUntilIdle();
EXPECT_TRUE(IsDarkResumeStateSet(GetWakeLockBridge()));
// Trigger suspend done event. Check if state is reset as dark resume would be
// exited.
fake_power_manager_client_->SendSuspendDone();
EXPECT_TRUE(IsDarkResumeStateReset(GetWakeLockBridge()));
}
} // namespace arc } // namespace arc
// Copyright 2018 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 COMPONENTS_ARC_WAKE_LOCK_WAKE_LOCK_OBSERVER_H_
#define COMPONENTS_ARC_WAKE_LOCK_WAKE_LOCK_OBSERVER_H_
namespace arc {
// This is an interface for classes that want to learn when Android wake locks
// are acquired or released. Observer should register themselves by calling the
// overriding class's AddObserver() method.
class WakeLockObserver {
public:
virtual ~WakeLockObserver() = default;
// Called when the tracked wake lock is acquired the first time i.e.
// number of holders increases to 1.
virtual void OnWakeLockAcquire() {}
// Called when the tracked wake lock is released the last time i.e. the number
// of holders goes to 0.
virtual void OnWakeLockRelease() {}
};
} // namespace arc
#endif // COMPONENTS_ARC_WAKE_LOCK_WAKE_LOCK_OBSERVER_H_
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