Skip to content

Commit

Permalink
arc: Add wake lock based logic to re-suspend after dark resume
Browse files Browse the repository at this point in the history
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: Yusuke Sato <yusukes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602980}
  • Loading branch information
Abhishek Bhardwaj authored and Commit Bot committed Oct 26, 2018
1 parent 45ec05e commit a0b9be9
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 45 deletions.
1 change: 1 addition & 0 deletions components/arc/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ static_library("arc") {
"volume_mounter/arc_volume_mounter_bridge.h",
"wake_lock/arc_wake_lock_bridge.cc",
"wake_lock/arc_wake_lock_bridge.h",
"wake_lock/wake_lock_observer.h",
]

public_deps = [
Expand Down
140 changes: 133 additions & 7 deletions components/arc/wake_lock/arc_wake_lock_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

#include "base/logging.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 "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
Expand Down Expand Up @@ -46,7 +49,8 @@ class ArcWakeLockBridgeFactory

// 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
// 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 {
public:
WakeLockRequester(device::mojom::WakeLockType type,
Expand Down Expand Up @@ -76,6 +80,9 @@ class ArcWakeLockBridge::WakeLockRequester {
}

wake_lock_->RequestWakeLock();

for (auto& observer : observers_)
observer.OnWakeLockAcquire();
}

// Decrements the number of outstanding Android requests. Cancels the device
Expand All @@ -94,10 +101,27 @@ class ArcWakeLockBridge::WakeLockRequester {
}

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();

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
// requests on |wake_lock_|.
void FlushForTesting() {
Expand All @@ -118,6 +142,8 @@ class ArcWakeLockBridge::WakeLockRequester {
// Lazily initialized in response to first request.
device::mojom::WakeLockPtr wake_lock_;

base::ObserverList<WakeLockObserver>::Unchecked observers_;

DISALLOW_COPY_AND_ASSIGN(WakeLockRequester);
};

Expand Down Expand Up @@ -145,23 +171,23 @@ ArcWakeLockBridge::ArcWakeLockBridge(content::BrowserContext* context,
weak_ptr_factory_(this) {
arc_bridge_service_->wake_lock()->SetHost(this);
arc_bridge_service_->wake_lock()->AddObserver(this);
chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
this);
}

ArcWakeLockBridge::~ArcWakeLockBridge() {
arc_bridge_service_->wake_lock()->RemoveObserver(this);
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() {
DVLOG(1) << "OnConnectionClosed";
wake_lock_requesters_.clear();
}

void ArcWakeLockBridge::FlushWakeLocksForTesting() {
for (const auto& it : wake_lock_requesters_)
it.second->FlushForTesting();
}

void ArcWakeLockBridge::AcquirePartialWakeLock(
AcquirePartialWakeLockCallback callback) {
GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension)
Expand All @@ -176,6 +202,106 @@ void ArcWakeLockBridge::ReleasePartialWakeLock(
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(
device::mojom::WakeLockType type) {
auto it = wake_lock_requesters_.find(type);
Expand Down
63 changes: 59 additions & 4 deletions components/arc/wake_lock/arc_wake_lock_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@

#include "base/macros.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/connection_observer.h"
#include "components/arc/wake_lock/wake_lock_observer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/common/service_manager_connection.h"
#include "mojo/public/cpp/bindings/binding.h"
Expand All @@ -30,7 +33,9 @@ class ArcBridgeService;
// Sets wake up timers / alarms based on calls from the instance.
class ArcWakeLockBridge : public KeyedService,
public ConnectionObserver<mojom::WakeLockInstance>,
public mojom::WakeLockHost {
public mojom::WakeLockHost,
public chromeos::PowerManagerClient::Observer,
public WakeLockObserver {
public:
// Returns the factory instance for this class.
static BrowserContextKeyedServiceFactory* GetFactory();
Expand All @@ -54,20 +59,57 @@ class ArcWakeLockBridge : public KeyedService,
// ConnectionObserver<mojom::WakeLockInstance>::Observer overrides.
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
// device service requests in |wake_lock_requesters_|.
void FlushWakeLocksForTesting();

// mojom::WakeLockHost overrides.
void AcquirePartialWakeLock(AcquirePartialWakeLockCallback callback) override;
void ReleasePartialWakeLock(ReleasePartialWakeLockCallback callback) override;
// Checks if |suspend_readiness_cb_| is set.
bool IsSuspendReadinessStateSetForTesting() const;

// 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:
class WakeLockRequester;

// Returns the WakeLockRequester for |type|, creating one if needed.
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.

// If non-null, used instead of the process-wide connector to fetch services.
Expand All @@ -78,8 +120,21 @@ class ArcWakeLockBridge : public KeyedService,
std::map<device::mojom::WakeLockType, std::unique_ptr<WakeLockRequester>>
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_;

// 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_;

DISALLOW_COPY_AND_ASSIGN(ArcWakeLockBridge);
Expand Down
Loading

0 comments on commit a0b9be9

Please sign in to comment.