| // 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. |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "chromeos/dbus/power/power_policy_controller.h" |
| #include "components/arc/arc_browser_context_keyed_service_factory_base.h" |
| #include "components/arc/arc_service_manager.h" |
| #include "components/arc/session/arc_bridge_service.h" |
| #include "components/arc/wake_lock/arc_wake_lock_bridge.h" |
| #include "content/public/browser/system_connector.h" |
| #include "mojo/public/cpp/bindings/interface_request.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/device/public/mojom/wake_lock_provider.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| |
| namespace arc { |
| |
| namespace { |
| |
| constexpr char kWakeLockReason[] = "ARC"; |
| |
| // Singleton factory for ArcWakeLockBridge. |
| class ArcWakeLockBridgeFactory |
| : public internal::ArcBrowserContextKeyedServiceFactoryBase< |
| ArcWakeLockBridge, |
| ArcWakeLockBridgeFactory> { |
| public: |
| // Factory name used by ArcBrowserContextKeyedServiceFactoryBase. |
| static constexpr const char* kName = "ArcWakeLockBridgeFactory"; |
| |
| static ArcWakeLockBridgeFactory* GetInstance() { |
| return base::Singleton<ArcWakeLockBridgeFactory>::get(); |
| } |
| |
| private: |
| friend base::DefaultSingletonTraits<ArcWakeLockBridgeFactory>; |
| ArcWakeLockBridgeFactory() = default; |
| ~ArcWakeLockBridgeFactory() override = default; |
| }; |
| |
| } // namespace |
| |
| // 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. |
| class ArcWakeLockBridge::WakeLockRequester { |
| public: |
| WakeLockRequester(device::mojom::WakeLockType type, |
| service_manager::Connector* connector) |
| : type_(type), connector_(connector) {} |
| ~WakeLockRequester() = default; |
| |
| // Increments the number of outstanding requests from Android and requests a |
| // wake lock from the device service if this is the only request. |
| void AddRequest() { |
| DCHECK_GE(wake_lock_count_, 0); |
| wake_lock_count_++; |
| if (wake_lock_count_ > 1) { |
| DVLOG(1) << "Partial wake lock acquire. Count: " << wake_lock_count_; |
| return; |
| } |
| |
| // Initialize |wake_lock_| if this is the first time we're using it. |
| DVLOG(1) << "Partial wake lock new acquire. Count: " << wake_lock_count_; |
| if (!wake_lock_) { |
| device::mojom::WakeLockProviderPtr provider; |
| connector_->BindInterface(device::mojom::kServiceName, |
| mojo::MakeRequest(&provider)); |
| provider->GetWakeLockWithoutContext( |
| type_, device::mojom::WakeLockReason::kOther, kWakeLockReason, |
| mojo::MakeRequest(&wake_lock_)); |
| } |
| |
| wake_lock_->RequestWakeLock(); |
| } |
| |
| // Decrements the number of outstanding Android requests. Cancels the device |
| // service wake lock when the request count hits zero. |
| void RemoveRequest() { |
| DCHECK_GE(wake_lock_count_, 0); |
| if (wake_lock_count_ == 0) { |
| LOG(WARNING) << "Release without acquire. Count: " << wake_lock_count_; |
| return; |
| } |
| |
| wake_lock_count_--; |
| if (wake_lock_count_ >= 1) { |
| DVLOG(1) << "Partial wake release. Count: " << wake_lock_count_; |
| return; |
| } |
| |
| DCHECK(wake_lock_); |
| DVLOG(1) << "Partial wake force release. Count: " << wake_lock_count_; |
| wake_lock_->CancelWakeLock(); |
| } |
| |
| // Runs the message loop until replies have been received for all pending |
| // requests on |wake_lock_|. |
| void FlushForTesting() { |
| if (wake_lock_) |
| wake_lock_.FlushForTesting(); |
| } |
| |
| private: |
| // Type of wake lock to request. |
| device::mojom::WakeLockType type_; |
| |
| // Used to get services. Not owned. |
| service_manager::Connector* const connector_ = nullptr; |
| |
| // Number of outstanding Android requests. |
| int64_t wake_lock_count_ = 0; |
| |
| // Lazily initialized in response to first request. |
| device::mojom::WakeLockPtr wake_lock_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WakeLockRequester); |
| }; |
| |
| // static |
| BrowserContextKeyedServiceFactory* ArcWakeLockBridge::GetFactory() { |
| return ArcWakeLockBridgeFactory::GetInstance(); |
| } |
| |
| // static |
| ArcWakeLockBridge* ArcWakeLockBridge::GetForBrowserContext( |
| content::BrowserContext* context) { |
| return ArcWakeLockBridgeFactory::GetForBrowserContext(context); |
| } |
| |
| // static |
| ArcWakeLockBridge* ArcWakeLockBridge::GetForBrowserContextForTesting( |
| content::BrowserContext* context) { |
| return ArcWakeLockBridgeFactory::GetForBrowserContextForTesting(context); |
| } |
| |
| ArcWakeLockBridge::ArcWakeLockBridge(content::BrowserContext* context, |
| ArcBridgeService* bridge_service) |
| : arc_bridge_service_(bridge_service), |
| binding_(this), |
| weak_ptr_factory_(this) { |
| arc_bridge_service_->wake_lock()->SetHost(this); |
| arc_bridge_service_->wake_lock()->AddObserver(this); |
| } |
| |
| ArcWakeLockBridge::~ArcWakeLockBridge() { |
| arc_bridge_service_->wake_lock()->RemoveObserver(this); |
| arc_bridge_service_->wake_lock()->SetHost(nullptr); |
| } |
| |
| 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) |
| ->AddRequest(); |
| std::move(callback).Run(true); |
| } |
| |
| void ArcWakeLockBridge::ReleasePartialWakeLock( |
| ReleasePartialWakeLockCallback callback) { |
| GetWakeLockRequester(device::mojom::WakeLockType::kPreventAppSuspension) |
| ->RemoveRequest(); |
| std::move(callback).Run(true); |
| } |
| |
| ArcWakeLockBridge::WakeLockRequester* ArcWakeLockBridge::GetWakeLockRequester( |
| device::mojom::WakeLockType type) { |
| auto it = wake_lock_requesters_.find(type); |
| if (it != wake_lock_requesters_.end()) |
| return it->second.get(); |
| |
| service_manager::Connector* connector = |
| connector_for_test_ ? connector_for_test_ : content::GetSystemConnector(); |
| DCHECK(connector); |
| |
| it = wake_lock_requesters_ |
| .emplace(type, std::make_unique<WakeLockRequester>(type, connector)) |
| .first; |
| return it->second.get(); |
| } |
| |
| } // namespace arc |