blob: 9206c22e0cfa28877ed17a76bfee36eee18dfd74 [file] [log] [blame]
// 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 <utility>
#include "base/containers/flat_set.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/stl_util.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/timer/arc_timer.h"
#include "components/arc/timer/arc_timer_bridge.h"
#include "components/arc/timer/arc_timer_traits.h"
namespace arc {
namespace {
// Singleton factory for ArcTimerBridge.
class ArcTimerBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcTimerBridge,
ArcTimerBridgeFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcTimerBridgeFactory";
static ArcTimerBridgeFactory* GetInstance() {
return base::Singleton<ArcTimerBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcTimerBridgeFactory>;
ArcTimerBridgeFactory() = default;
~ArcTimerBridgeFactory() override = default;
};
bool IsSupportedClock(int32_t clock_id) {
return clock_id == CLOCK_BOOTTIME_ALARM || clock_id == CLOCK_REALTIME_ALARM;
}
} // namespace
// static
BrowserContextKeyedServiceFactory* ArcTimerBridge::GetFactory() {
return ArcTimerBridgeFactory::GetInstance();
}
// static
ArcTimerBridge* ArcTimerBridge::GetForBrowserContext(
content::BrowserContext* context) {
return ArcTimerBridgeFactory::GetForBrowserContext(context);
}
// static
ArcTimerBridge* ArcTimerBridge::GetForBrowserContextForTesting(
content::BrowserContext* context) {
return ArcTimerBridgeFactory::GetForBrowserContextForTesting(context);
}
ArcTimerBridge::ArcTimerBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: timer_task_runner_(
base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
arc_bridge_service_(bridge_service),
binding_(this),
weak_ptr_factory_(this) {
arc_bridge_service_->timer()->SetHost(this);
arc_bridge_service_->timer()->AddObserver(this);
}
ArcTimerBridge::~ArcTimerBridge() {
arc_bridge_service_->timer()->RemoveObserver(this);
arc_bridge_service_->timer()->SetHost(nullptr);
}
void ArcTimerBridge::CreateTimers(
std::vector<CreateTimerRequest> arc_timer_requests,
CreateTimersCallback callback) {
DVLOG(1) << "Received CreateTimers";
// Alarm timers can't be created on the UI thread because they make syscalls
// in the constructor. Post a task to create |ArcTimer| objects containing
// alarm timer objects.
base::PostTaskAndReplyWithResult(
timer_task_runner_.get(), FROM_HERE,
base::BindOnce(&CreateArcTimers, !arc_timers_.empty(),
base::SequencedTaskRunnerHandle::Get(), timer_task_runner_,
std::move(arc_timer_requests),
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&ArcTimerBridge::OnArcTimersCreated,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ArcTimerBridge::OnConnectionClosed() {
DVLOG(1) << "OnConnectionClosed";
DeleteArcTimers();
}
// Deleter for |ArcTimer|s. Deletes the timer object on |task_runner|.
class ArcTimerBridge::DeleteOnSequence {
public:
explicit DeleteOnSequence(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
~DeleteOnSequence() = default;
DeleteOnSequence(DeleteOnSequence&&) = default;
DeleteOnSequence& operator=(DeleteOnSequence&&) = default;
void operator()(ArcTimer* timer) const {
task_runner_->DeleteSoon(FROM_HERE, timer);
}
private:
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(DeleteOnSequence);
};
void ArcTimerBridge::OnConnectionError(ArcTimer* timer) {
DVLOG(1) << "OnConnectionError";
// At this point the mojo binding for |timer| has an error. Find and delete
// the timer. Since the lifetime of |ArcTimer| objects is managed by this
// class it is safe to compare |timer|.
base::EraseIf(
arc_timers_,
[timer](const std::unique_ptr<ArcTimer, DeleteOnSequence>& element) {
return element.get() == timer;
});
}
// static.
void ArcTimerBridge::OnConnectionErrorOnTimerThread(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::WeakPtr<ArcTimerBridge> weak_self,
ArcTimer* timer) {
// Post task on the main thread since the task will access class members.
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&ArcTimerBridge::OnConnectionError, weak_self, timer));
}
struct ArcTimerBridge::TimersAndProxies {
TimersAndProxies() = default;
~TimersAndProxies() = default;
TimersAndProxies(TimersAndProxies&&) = default;
TimersAndProxies& operator=(TimersAndProxies&&) = default;
std::vector<int32_t> clocks;
std::vector<std::unique_ptr<ArcTimer, DeleteOnSequence>> timers;
std::vector<mojom::TimerPtrInfo> proxies;
};
// static.
base::Optional<ArcTimerBridge::TimersAndProxies>
ArcTimerBridge::CreateArcTimers(
bool timers_already_created,
scoped_refptr<base::SequencedTaskRunner> original_task_runner,
scoped_refptr<base::SequencedTaskRunner> timer_task_runner,
std::vector<arc::CreateTimerRequest> arc_timer_requests,
base::WeakPtr<ArcTimerBridge> weak_self) {
if (timers_already_created) {
LOG(ERROR) << "Double creation not supported";
return base::nullopt;
}
// Iterate over the list of {clock_id, expiration_fd} and create an |ArcTimer|
// and |mojom::TimerPtrInfo| entry for each clock.
base::flat_set<int32_t> seen_clocks;
ArcTimerBridge::TimersAndProxies result;
for (auto& request : arc_timer_requests) {
// Read each entry one by one. Each entry will have an |ArcTimer| entry
// associated with it.
int32_t clock_id = request.clock_id;
if (!IsSupportedClock(clock_id)) {
LOG(ERROR) << "Unsupported clock=" << clock_id;
return base::nullopt;
}
if (!seen_clocks.insert(clock_id).second) {
LOG(ERROR) << "Duplicate clocks not supported";
return base::nullopt;
}
base::ScopedFD expiration_fd = std::move(request.expiration_fd);
if (!expiration_fd.is_valid()) {
LOG(ERROR) << "Bad file descriptor for clock=" << clock_id;
return base::nullopt;
}
mojom::TimerPtrInfo timer_proxy_info;
// TODO(b/69759087): Make |ArcTimer| take |clock_id| to create timers of
// different clock types.
// The instance opens clocks of type CLOCK_BOOTTIME_ALARM and
// CLOCK_REALTIME_ALARM. However, it uses only CLOCK_BOOTTIME_ALARM to set
// wake up alarms. At this point, it's okay to pretend the host supports
// CLOCK_REALTIME_ALARM instead of returning an error.
//
// Mojo guarantees to call all callbacks on the task runner that the
// mojo::Binding i.e. |ArcTimer| was created on.
result.clocks.push_back(clock_id);
result.timers.push_back(std::unique_ptr<ArcTimer, DeleteOnSequence>(
new ArcTimer(
std::move(expiration_fd), mojo::MakeRequest(&timer_proxy_info),
base::BindOnce(&ArcTimerBridge::OnConnectionErrorOnTimerThread,
original_task_runner, weak_self)),
DeleteOnSequence(timer_task_runner)));
result.proxies.push_back(std::move(timer_proxy_info));
}
return result;
}
void ArcTimerBridge::OnArcTimersCreated(
CreateTimersCallback callback,
base::Optional<TimersAndProxies> timers_and_proxies) {
if (timers_and_proxies == base::nullopt) {
std::move(callback).Run(base::nullopt);
return;
}
DCHECK_EQ(timers_and_proxies->clocks.size(),
timers_and_proxies->timers.size());
DCHECK_EQ(timers_and_proxies->clocks.size(),
timers_and_proxies->proxies.size());
arc_timers_ = std::move(timers_and_proxies->timers);
// Respond to instance with timer proxies.
std::vector<mojom::CreateTimerResponsePtr> result;
for (size_t i = 0; i < timers_and_proxies->clocks.size(); i++) {
mojom::CreateTimerResponsePtr response = mojom::CreateTimerResponse::New();
response->clock_id =
mojo::EnumTraits<arc::mojom::ClockId, int32_t>::ToMojom(
timers_and_proxies->clocks[i]);
response->timer = std::move(timers_and_proxies->proxies[i]);
result.push_back(std::move(response));
}
std::move(callback).Run(std::move(result));
}
void ArcTimerBridge::DeleteArcTimers() {
// The timer objects are deleted on |timer_task_runner_|.
arc_timers_.clear();
}
} // namespace arc