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);
......
// 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