blob: 80c656d953ae3416bd5a8da5c2bbd026850455a8 [file] [log] [blame]
// Copyright 2017 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 "services/device/public/cpp/test/test_wake_lock_provider.h"
#include <memory>
#include <utility>
#include "base/callback.h"
#include "base/logging.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/device/public/mojom/constants.mojom.h"
namespace device {
// TestWakeLock implements mojom::WakeLock on behalf of TestWakeLockProvider.
class TestWakeLockProvider::TestWakeLock : public mojom::WakeLock,
public service_manager::Service {
public:
TestWakeLock(mojom::WakeLockRequest request,
mojom::WakeLockType type,
TestWakeLockProvider* provider)
: type_(type), provider_(provider) {
AddClient(std::move(request));
bindings_.set_connection_error_handler(base::BindRepeating(
&TestWakeLock::OnConnectionError, base::Unretained(this)));
}
~TestWakeLock() override = default;
mojom::WakeLockType type() const { return type_; }
// mojom::WakeLock:
void RequestWakeLock() override {
DCHECK(bindings_.dispatch_context());
DCHECK_GE(num_lock_requests_, 0);
// Coalesce consecutive requests from the same client.
if (*bindings_.dispatch_context())
return;
*bindings_.dispatch_context() = true;
num_lock_requests_++;
CheckAndNotifyProvider();
}
void CancelWakeLock() override {
DCHECK(bindings_.dispatch_context());
// Coalesce consecutive cancel requests from the same client. Also ignore a
// CancelWakeLock call without a RequestWakeLock call.
if (!(*bindings_.dispatch_context()))
return;
DCHECK_GT(num_lock_requests_, 0);
*bindings_.dispatch_context() = false;
num_lock_requests_--;
CheckAndNotifyProvider();
}
void AddClient(mojom::WakeLockRequest request) override {
bindings_.AddBinding(this, std::move(request),
std::make_unique<bool>(false));
}
void ChangeType(mojom::WakeLockType type,
ChangeTypeCallback callback) override {
NOTIMPLEMENTED();
}
void HasWakeLockForTests(HasWakeLockForTestsCallback callback) override {
NOTIMPLEMENTED();
}
void OnConnectionError() {
// If there is an outstanding request by this client then decrement its
// request and check if the wake lock is deactivated.
DCHECK(bindings_.dispatch_context());
if (*bindings_.dispatch_context() && num_lock_requests_ > 0) {
num_lock_requests_--;
CheckAndNotifyProvider();
}
// TestWakeLockProvider will take care of deleting this object as it owns
// it.
if (bindings_.empty())
provider_->OnConnectionError(type_, this);
}
private:
void CheckAndNotifyProvider() {
if (num_lock_requests_ == 1) {
provider_->OnWakeLockActivated(type_);
return;
}
if (num_lock_requests_ == 0) {
provider_->OnWakeLockDeactivated(type_);
return;
}
}
mojom::WakeLockType type_;
// Not owned.
TestWakeLockProvider* provider_;
mojo::BindingSet<mojom::WakeLock, std::unique_ptr<bool>> bindings_;
int num_lock_requests_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestWakeLock);
};
// Holds the state associated with wake locks of a single type across the
// system i.e. if 3 |kAppSuspension| wake locks are currently held the |count|
// would be 3.
struct TestWakeLockProvider::WakeLockDataPerType {
WakeLockDataPerType() = default;
WakeLockDataPerType(WakeLockDataPerType&&) = default;
~WakeLockDataPerType() = default;
// Currently held count of this wake lock type.
int64_t count = 0;
// Map of all wake locks of this type created by this provider. An entry is
// removed from this map when an |OnConnectionError| is received.
std::map<TestWakeLock*, std::unique_ptr<TestWakeLock>> wake_locks;
// Observers for this wake lock type.
mojo::InterfacePtrSet<mojom::WakeLockObserver> observers;
DISALLOW_COPY_AND_ASSIGN(WakeLockDataPerType);
};
TestWakeLockProvider::TestWakeLockProvider(
service_manager::mojom::ServiceRequest request)
: service_binding_(this, std::move(request)) {
// Populates |wake_lock_store_| with entries for all types of wake locks.
wake_lock_store_[mojom::WakeLockType::kPreventAppSuspension] =
std::make_unique<WakeLockDataPerType>();
wake_lock_store_[mojom::WakeLockType::kPreventDisplaySleep] =
std::make_unique<WakeLockDataPerType>();
wake_lock_store_[mojom::WakeLockType::kPreventDisplaySleepAllowDimming] =
std::make_unique<WakeLockDataPerType>();
}
TestWakeLockProvider::~TestWakeLockProvider() = default;
void TestWakeLockProvider::GetWakeLockContextForID(
int context_id,
mojo::InterfaceRequest<mojom::WakeLockContext> request) {
// This method is only used on Android.
NOTIMPLEMENTED();
}
void TestWakeLockProvider::GetWakeLockWithoutContext(
mojom::WakeLockType type,
mojom::WakeLockReason reason,
const std::string& description,
mojom::WakeLockRequest request) {
// Create a wake lock and store it to manage it's lifetime.
auto wake_lock =
std::make_unique<TestWakeLock>(std::move(request), type, this);
GetWakeLockDataPerType(type).wake_locks[wake_lock.get()] =
std::move(wake_lock);
}
void TestWakeLockProvider::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
DCHECK_EQ(interface_name, mojom::WakeLockProvider::Name_);
bindings_.AddBinding(
this, mojom::WakeLockProviderRequest(std::move(interface_pipe)));
}
void TestWakeLockProvider::OnConnectionError(mojom::WakeLockType type,
TestWakeLock* wake_lock) {
size_t result = GetWakeLockDataPerType(type).wake_locks.erase(wake_lock);
DCHECK_GT(result, 0UL);
}
TestWakeLockProvider::WakeLockDataPerType&
TestWakeLockProvider::GetWakeLockDataPerType(mojom::WakeLockType type) const {
auto it = wake_lock_store_.find(type);
// An entry for |type| should always be created in the constructor.
DCHECK(it != wake_lock_store_.end());
return *(it->second);
}
void TestWakeLockProvider::OnWakeLockActivated(mojom::WakeLockType type) {
// Increment the currently activated wake locks of type |type|.
const int64_t old_count = GetWakeLockDataPerType(type).count;
DCHECK_GE(old_count, 0);
GetWakeLockDataPerType(type).count = old_count + 1;
}
void TestWakeLockProvider::OnWakeLockDeactivated(mojom::WakeLockType type) {
// Decrement the currently activated wake locks of type |type|.
const int64_t old_count = GetWakeLockDataPerType(type).count;
DCHECK_GT(old_count, 0);
const int64_t new_count = old_count - 1;
GetWakeLockDataPerType(type).count = new_count;
// Notify observers of the last cancelation i.e. deactivation of wake lock
// type |type|.
if (new_count == 0) {
GetWakeLockDataPerType(type).observers.ForAllPtrs(
[type](mojom::WakeLockObserver* wake_lock_observer) {
wake_lock_observer->OnWakeLockDeactivated(type);
});
}
}
void TestWakeLockProvider::NotifyOnWakeLockDeactivation(
mojom::WakeLockType type,
mojom::WakeLockObserverPtr observer) {
// Notify observer immediately if wake lock is deactivated. Add it to the
// observers list for future deactivation notifications.
if (GetWakeLockDataPerType(type).count == 0) {
observer->OnWakeLockDeactivated(type);
}
GetWakeLockDataPerType(type).observers.AddPtr(std::move(observer));
}
void TestWakeLockProvider::GetActiveWakeLocksForTests(
mojom::WakeLockType type,
GetActiveWakeLocksForTestsCallback callback) {
std::move(callback).Run(GetWakeLockDataPerType(type).count);
}
} // namespace device