1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
ash / components / arc / timer / arc_timer_bridge.cc [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <set>
#include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/components/arc/timer/arc_timer_bridge.h"
#include "ash/components/arc/timer/arc_timer_mojom_traits.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/strings/stringprintf.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/dbus/upstart/upstart_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace arc {
namespace {
// Tag to be used with the powerd timer API.
constexpr char kTag[] = "ARC";
mojom::ArcTimerResult ConvertBoolResultToMojo(bool result) {
return result ? mojom::ArcTimerResult::SUCCESS
: mojom::ArcTimerResult::FAILURE;
}
// Callback for powerd API called in |StartTimer|.
void OnStartTimer(mojom::TimerHost::StartTimerCallback callback, bool result) {
std::move(callback).Run(ConvertBoolResultToMojo(result));
}
// Unwraps a mojo handle to a file descriptor on the system.
base::ScopedFD UnwrapScopedHandle(mojo::ScopedHandle handle) {
base::ScopedPlatformFile platform_file;
if (mojo::UnwrapPlatformFile(std::move(handle), &platform_file) !=
MOJO_RESULT_OK) {
LOG(ERROR) << "Failed to unwrap mojo handle";
}
return platform_file;
}
// Returns true iff |arc_timer_requests| contains duplicate clock id values.
bool ContainsDuplicateClocks(
const std::vector<arc::mojom::CreateTimerRequestPtr>& arc_timer_requests) {
std::set<clockid_t> seen_clock_ids;
for (const auto& request : arc_timer_requests) {
if (!seen_clock_ids.emplace(request->clock_id).second)
return true;
}
return false;
}
// 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;
};
} // 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)
: arc_bridge_service_(bridge_service) {
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::OnConnectionClosed() {
DeleteArcTimers();
}
void ArcTimerBridge::CreateTimers(
std::vector<arc::mojom::CreateTimerRequestPtr> arc_timer_requests,
CreateTimersCallback callback) {
// Duplicate clocks are not allowed.
if (ContainsDuplicateClocks(arc_timer_requests)) {
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
// Convert mojo arguments to D-Bus arguments required by powerd to create
// timers.
std::vector<std::pair<clockid_t, base::ScopedFD>> requests;
std::vector<clockid_t> clock_ids;
for (auto& request : arc_timer_requests) {
clockid_t clock_id = request->clock_id;
base::ScopedFD expiration_fd =
UnwrapScopedHandle(std::move(request->expiration_fd));
if (!expiration_fd.is_valid()) {
LOG(ERROR) << "Unwrapped expiration fd is invalid for clock=" << clock_id;
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
requests.emplace_back(clock_id, std::move(expiration_fd));
clock_ids.emplace_back(clock_id);
}
chromeos::PowerManagerClient::Get()->CreateArcTimers(
kTag, std::move(requests),
base::BindOnce(&ArcTimerBridge::OnCreateArcTimers,
weak_ptr_factory_.GetWeakPtr(), std::move(clock_ids),
std::move(callback)));
}
void ArcTimerBridge::StartTimer(clockid_t clock_id,
base::TimeTicks absolute_expiration_time,
StartTimerCallback callback) {
auto timer_id = GetTimerId(clock_id);
if (!timer_id.has_value()) {
LOG(ERROR) << "Timer for clock=" << clock_id << " not created";
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
chromeos::PowerManagerClient::Get()->StartArcTimer(
timer_id.value(), absolute_expiration_time,
base::BindOnce(&OnStartTimer, std::move(callback)));
}
void ArcTimerBridge::SetTime(base::Time time_to_set, SetTimeCallback callback) {
base::Time now = base::Time::Now();
base::TimeDelta delta = time_to_set - now;
if (delta.is_negative()) {
delta = -delta;
}
if (delta > kArcSetTimeMaxTimeDelta) {
LOG(ERROR) << "SetTime rejected. Delta between requested time ("
<< time_to_set << ") and current time (" << now
<< ") is greater than " << kArcSetTimeMaxTimeDelta;
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
DVLOG(1) << "SetTime requested: " << time_to_set;
std::vector<std::string> env = {
base::StringPrintf("UNIXTIME_TO_SET=%ld", time_to_set.ToTimeT())};
ash::UpstartClient::Get()->StartJob(
kArcSetTimeJobName, env,
base::BindOnce(
[](SetTimeCallback callback, bool success) {
DVLOG(1) << "arc-set-time upstart job returned: " << success;
std::move(callback).Run(success ? mojom::ArcTimerResult::SUCCESS
: mojom::ArcTimerResult::FAILURE);
},
std::move(callback)));
}
void ArcTimerBridge::DeleteArcTimers() {
chromeos::PowerManagerClient::Get()->DeleteArcTimers(
kTag, base::BindOnce(&ArcTimerBridge::OnDeleteArcTimers,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcTimerBridge::OnDeleteArcTimers(bool result) {
if (!result) {
LOG(ERROR) << "Delete timers failed";
return;
}
// If the delete call succeeded then delete any timer ids stored and make a
// create timers call.
DVLOG(1) << "Delete timers succeeded";
timer_ids_.clear();
}
void ArcTimerBridge::OnCreateArcTimers(
std::vector<clockid_t> clock_ids,
CreateTimersCallback callback,
std::optional<std::vector<TimerId>> timer_ids) {
// Any old timers associated with the same tag are always cleared by the API
// regardless of the new timers being created successfully or not. Clear the
// cached timer ids in that case.
timer_ids_.clear();
// The API returns a list of timer ids corresponding to each clock in
// |clock_ids|.
if (!timer_ids.has_value()) {
LOG(ERROR) << "Create timers failed";
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
std::vector<TimerId> result = timer_ids.value();
if (result.size() != clock_ids.size()) {
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
// Map clock id values to timer ids.
auto timer_id_iter = result.begin();
for (clockid_t clock_id : clock_ids) {
DVLOG(1) << "Storing clock=" << clock_id << " timer id=" << *timer_id_iter;
if (!timer_ids_.emplace(clock_id, *timer_id_iter).second) {
// This should never happen as any collision should have been detected on
// the powerd side and it should have returned an error.
LOG(ERROR) << "Can't store clock=" << clock_id;
timer_ids_.clear();
std::move(callback).Run(mojom::ArcTimerResult::FAILURE);
return;
}
timer_id_iter++;
}
std::move(callback).Run(mojom::ArcTimerResult::SUCCESS);
}
std::optional<ArcTimerBridge::TimerId> ArcTimerBridge::GetTimerId(
clockid_t clock_id) const {
auto it = timer_ids_.find(clock_id);
return (it == timer_ids_.end()) ? std::nullopt
: std::make_optional<TimerId>(it->second);
}
// static
void ArcTimerBridge::EnsureFactoryBuilt() {
ArcTimerBridgeFactory::GetInstance();
}
} // namespace arc