1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "device/gamepad/gamepad_provider.h"
6
7 #include <stddef.h>
8 #include <string.h>
9 #include <cmath>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/check.h"
15 #include "base/location.h"
16 #include "base/message_loop/message_pump_type.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "build/build_config.h"
24 #include "device/gamepad/gamepad_data_fetcher.h"
25 #include "device/gamepad/gamepad_data_fetcher_manager.h"
26 #include "device/gamepad/gamepad_user_gesture.h"
27 #include "device/gamepad/public/cpp/gamepad_features.h"
28 #include "mojo/public/cpp/system/platform_handle.h"
29
30 namespace device {
31
32 constexpr int64_t kPollingIntervalMilliseconds = 4; // ~250 Hz
33
GamepadProvider(GamepadConnectionChangeClient * connection_change_client)34 GamepadProvider::GamepadProvider(
35 GamepadConnectionChangeClient* connection_change_client)
36 : gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()),
37 connection_change_client_(connection_change_client) {
38 Initialize(std::unique_ptr<GamepadDataFetcher>());
39 }
40
GamepadProvider(GamepadConnectionChangeClient * connection_change_client,std::unique_ptr<GamepadDataFetcher> fetcher,std::unique_ptr<base::Thread> polling_thread)41 GamepadProvider::GamepadProvider(
42 GamepadConnectionChangeClient* connection_change_client,
43 std::unique_ptr<GamepadDataFetcher> fetcher,
44 std::unique_ptr<base::Thread> polling_thread)
45 : gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()),
46 polling_thread_(std::move(polling_thread)),
47 connection_change_client_(connection_change_client) {
48 Initialize(std::move(fetcher));
49 }
50
~GamepadProvider()51 GamepadProvider::~GamepadProvider() {
52 GamepadDataFetcherManager::GetInstance()->ClearProvider();
53
54 base::SystemMonitor* monitor = base::SystemMonitor::Get();
55 if (monitor)
56 monitor->RemoveDevicesChangedObserver(this);
57
58 // Delete GamepadDataFetchers on |polling_thread_|. This is important because
59 // some of them require their destructor to be called on the same sequence as
60 // their other methods.
61 polling_thread_->task_runner()->PostTask(
62 FROM_HERE, base::BindOnce(&GamepadFetcherVector::clear,
63 base::Unretained(&data_fetchers_)));
64
65 // Use Stop() to join the polling thread, as there may be pending callbacks
66 // which dereference |polling_thread_|.
67 polling_thread_->Stop();
68
69 DCHECK(data_fetchers_.empty());
70 }
71
72 base::ReadOnlySharedMemoryRegion
DuplicateSharedMemoryRegion()73 GamepadProvider::DuplicateSharedMemoryRegion() {
74 return gamepad_shared_buffer_->DuplicateSharedMemoryRegion();
75 }
76
GetCurrentGamepadData(Gamepads * data)77 void GamepadProvider::GetCurrentGamepadData(Gamepads* data) {
78 const Gamepads* pads = gamepad_shared_buffer_->buffer();
79 base::AutoLock lock(shared_memory_lock_);
80 *data = *pads;
81 }
82
PlayVibrationEffectOnce(uint32_t pad_index,mojom::GamepadHapticEffectType type,mojom::GamepadEffectParametersPtr params,mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback)83 void GamepadProvider::PlayVibrationEffectOnce(
84 uint32_t pad_index,
85 mojom::GamepadHapticEffectType type,
86 mojom::GamepadEffectParametersPtr params,
87 mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) {
88 polling_thread_->task_runner()->PostTask(
89 FROM_HERE,
90 base::BindOnce(&GamepadProvider::PlayEffectOnPollingThread,
91 Unretained(this), pad_index, type, std::move(params),
92 std::move(callback), base::ThreadTaskRunnerHandle::Get()));
93 }
94
ResetVibrationActuator(uint32_t pad_index,mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback)95 void GamepadProvider::ResetVibrationActuator(
96 uint32_t pad_index,
97 mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) {
98 polling_thread_->task_runner()->PostTask(
99 FROM_HERE,
100 base::BindOnce(&GamepadProvider::ResetVibrationOnPollingThread,
101 Unretained(this), pad_index, std::move(callback),
102 base::ThreadTaskRunnerHandle::Get()));
103 }
104
Pause()105 void GamepadProvider::Pause() {
106 {
107 base::AutoLock lock(is_paused_lock_);
108 is_paused_ = true;
109 }
110 polling_thread_->task_runner()->PostTask(
111 FROM_HERE,
112 base::BindOnce(&GamepadProvider::SendPauseHint, Unretained(this), true));
113 }
114
Resume()115 void GamepadProvider::Resume() {
116 {
117 base::AutoLock lock(is_paused_lock_);
118 if (!is_paused_)
119 return;
120 is_paused_ = false;
121 }
122
123 polling_thread_->task_runner()->PostTask(
124 FROM_HERE,
125 base::BindOnce(&GamepadProvider::SendPauseHint, Unretained(this), false));
126 polling_thread_->task_runner()->PostTask(
127 FROM_HERE,
128 base::BindOnce(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
129 }
130
RegisterForUserGesture(base::OnceClosure closure)131 void GamepadProvider::RegisterForUserGesture(base::OnceClosure closure) {
132 base::AutoLock lock(user_gesture_lock_);
133 user_gesture_observers_.emplace_back(std::move(closure),
134 base::ThreadTaskRunnerHandle::Get());
135 }
136
OnDevicesChanged(base::SystemMonitor::DeviceType type)137 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
138 base::AutoLock lock(devices_changed_lock_);
139 devices_changed_ = true;
140 }
141
Initialize(std::unique_ptr<GamepadDataFetcher> fetcher)142 void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
143 sampling_interval_delta_ =
144 base::TimeDelta::FromMilliseconds(kPollingIntervalMilliseconds);
145
146 base::SystemMonitor* monitor = base::SystemMonitor::Get();
147 if (monitor)
148 monitor->AddDevicesChangedObserver(this);
149
150 if (!polling_thread_)
151 polling_thread_.reset(new base::Thread("Gamepad polling thread"));
152 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
153 // On Linux, the data fetcher needs to watch file descriptors, so the message
154 // loop needs to be a libevent loop.
155 const base::MessagePumpType kMessageLoopType = base::MessagePumpType::IO;
156 #elif defined(OS_ANDROID)
157 // On Android, keeping a message loop of default type.
158 const base::MessagePumpType kMessageLoopType = base::MessagePumpType::DEFAULT;
159 #else
160 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
161 // message loop needs to be a UI-type loop. On Windows it must be a UI loop
162 // to properly pump the MessageWindow that captures device state.
163 const base::MessagePumpType kMessageLoopType = base::MessagePumpType::UI;
164 #endif
165 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
166
167 if (fetcher) {
168 AddGamepadDataFetcher(std::move(fetcher));
169 } else {
170 GamepadDataFetcherManager::GetInstance()->InitializeProvider(this);
171 }
172 }
173
AddGamepadDataFetcher(std::unique_ptr<GamepadDataFetcher> fetcher)174 void GamepadProvider::AddGamepadDataFetcher(
175 std::unique_ptr<GamepadDataFetcher> fetcher) {
176 polling_thread_->task_runner()->PostTask(
177 FROM_HERE, base::BindOnce(&GamepadProvider::DoAddGamepadDataFetcher,
178 base::Unretained(this), std::move(fetcher)));
179 }
180
RemoveSourceGamepadDataFetcher(GamepadSource source)181 void GamepadProvider::RemoveSourceGamepadDataFetcher(GamepadSource source) {
182 polling_thread_->task_runner()->PostTask(
183 FROM_HERE,
184 base::BindOnce(&GamepadProvider::DoRemoveSourceGamepadDataFetcher,
185 base::Unretained(this), source));
186 }
187
PlayEffectOnPollingThread(uint32_t pad_index,mojom::GamepadHapticEffectType type,mojom::GamepadEffectParametersPtr params,mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_runner)188 void GamepadProvider::PlayEffectOnPollingThread(
189 uint32_t pad_index,
190 mojom::GamepadHapticEffectType type,
191 mojom::GamepadEffectParametersPtr params,
192 mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
193 scoped_refptr<base::SequencedTaskRunner> callback_runner) {
194 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
195 PadState* pad_state = GetConnectedPadState(pad_index);
196 if (!pad_state) {
197 GamepadDataFetcher::RunVibrationCallback(
198 std::move(callback), std::move(callback_runner),
199 mojom::GamepadHapticsResult::GamepadHapticsResultError);
200 return;
201 }
202
203 GamepadDataFetcher* fetcher = GetSourceGamepadDataFetcher(pad_state->source);
204 if (!fetcher) {
205 GamepadDataFetcher::RunVibrationCallback(
206 std::move(callback), std::move(callback_runner),
207 mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
208 return;
209 }
210
211 fetcher->PlayEffect(pad_state->source_id, type, std::move(params),
212 std::move(callback), std::move(callback_runner));
213 }
214
ResetVibrationOnPollingThread(uint32_t pad_index,mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_runner)215 void GamepadProvider::ResetVibrationOnPollingThread(
216 uint32_t pad_index,
217 mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
218 scoped_refptr<base::SequencedTaskRunner> callback_runner) {
219 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
220 PadState* pad_state = GetConnectedPadState(pad_index);
221 if (!pad_state) {
222 GamepadDataFetcher::RunVibrationCallback(
223 std::move(callback), std::move(callback_runner),
224 mojom::GamepadHapticsResult::GamepadHapticsResultError);
225 return;
226 }
227
228 GamepadDataFetcher* fetcher = GetSourceGamepadDataFetcher(pad_state->source);
229 if (!fetcher) {
230 GamepadDataFetcher::RunVibrationCallback(
231 std::move(callback), std::move(callback_runner),
232 mojom::GamepadHapticsResult::GamepadHapticsResultNotSupported);
233 return;
234 }
235
236 fetcher->ResetVibration(pad_state->source_id, std::move(callback),
237 std::move(callback_runner));
238 }
239
GetSourceGamepadDataFetcher(GamepadSource source)240 GamepadDataFetcher* GamepadProvider::GetSourceGamepadDataFetcher(
241 GamepadSource source) {
242 for (auto it = data_fetchers_.begin(); it != data_fetchers_.end();) {
243 if ((*it)->source() == source) {
244 return it->get();
245 } else {
246 ++it;
247 }
248 }
249 return nullptr;
250 }
251
DoAddGamepadDataFetcher(std::unique_ptr<GamepadDataFetcher> fetcher)252 void GamepadProvider::DoAddGamepadDataFetcher(
253 std::unique_ptr<GamepadDataFetcher> fetcher) {
254 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
255
256 if (!fetcher)
257 return;
258
259 InitializeDataFetcher(fetcher.get());
260 data_fetchers_.push_back(std::move(fetcher));
261 }
262
DoRemoveSourceGamepadDataFetcher(GamepadSource source)263 void GamepadProvider::DoRemoveSourceGamepadDataFetcher(GamepadSource source) {
264 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
265
266 for (auto it = data_fetchers_.begin(); it != data_fetchers_.end();) {
267 if ((*it)->source() == source) {
268 it = data_fetchers_.erase(it);
269 } else {
270 ++it;
271 }
272 }
273 }
274
SendPauseHint(bool paused)275 void GamepadProvider::SendPauseHint(bool paused) {
276 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
277 for (const auto& it : data_fetchers_) {
278 it->PauseHint(paused);
279 }
280 }
281
DoPoll()282 void GamepadProvider::DoPoll() {
283 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
284 DCHECK(have_scheduled_do_poll_);
285 have_scheduled_do_poll_ = false;
286
287 bool changed;
288
289 ANNOTATE_BENIGN_RACE_SIZED(gamepad_shared_buffer_->buffer(), sizeof(Gamepads),
290 "Racey reads are discarded");
291
292 {
293 base::AutoLock lock(devices_changed_lock_);
294 changed = devices_changed_;
295 devices_changed_ = false;
296 }
297
298 for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
299 pad_states_.get()[i].is_active = false;
300
301 // Loop through each registered data fetcher and poll its gamepad data.
302 // It's expected that GetGamepadData will mark each gamepad as active (via
303 // GetPadState). If a gamepad is not marked as active during the calls to
304 // GetGamepadData then it's assumed to be disconnected.
305 for (const auto& it : data_fetchers_) {
306 it->GetGamepadData(changed);
307 }
308
309 Gamepads* buffer = gamepad_shared_buffer_->buffer();
310
311 // Send out disconnect events using the last polled data before we wipe it out
312 // in the mapping step.
313 if (ever_had_user_gesture_) {
314 for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
315 PadState& state = pad_states_.get()[i];
316
317 if (!state.is_newly_active && !state.is_active &&
318 state.source != GAMEPAD_SOURCE_NONE) {
319 auto pad = buffer->items[i];
320 pad.connected = false;
321 OnGamepadConnectionChange(false, i, pad);
322 ClearPadState(state);
323 }
324 }
325 }
326
327 {
328 base::AutoLock lock(shared_memory_lock_);
329
330 // Acquire the SeqLock. There is only ever one writer to this data.
331 // See gamepad_shared_buffer.h.
332 gamepad_shared_buffer_->WriteBegin();
333 for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
334 PadState& state = pad_states_.get()[i];
335 // Must run through the map+sanitize here or CheckForUserGesture may fail.
336 MapAndSanitizeGamepadData(&state, &buffer->items[i], sanitize_);
337 }
338 gamepad_shared_buffer_->WriteEnd();
339 }
340
341 if (ever_had_user_gesture_) {
342 for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
343 PadState& state = pad_states_.get()[i];
344
345 if (state.is_newly_active && buffer->items[i].connected) {
346 state.is_newly_active = false;
347 OnGamepadConnectionChange(true, i, buffer->items[i]);
348 }
349 }
350 }
351
352 bool did_notify = CheckForUserGesture();
353
354 // Avoid double-notifying gamepad connection observers when a gamepad is
355 // connected in the same polling cycle as the initial user gesture.
356 //
357 // If a gamepad is connected in the same polling cycle as the initial user
358 // gesture, the user gesture will trigger a gamepadconnected event during the
359 // CheckForUserGesture call above. If we don't clear |is_newly_active| here,
360 // we will notify again for the same gamepad on the next polling cycle.
361 if (did_notify) {
362 for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i)
363 pad_states_.get()[i].is_newly_active = false;
364 }
365
366 // Schedule our next interval of polling.
367 ScheduleDoPoll();
368 }
369
DisconnectUnrecognizedGamepad(GamepadSource source,int source_id)370 void GamepadProvider::DisconnectUnrecognizedGamepad(GamepadSource source,
371 int source_id) {
372 for (auto& fetcher : data_fetchers_) {
373 if (fetcher->source() == source) {
374 bool disconnected = fetcher->DisconnectUnrecognizedGamepad(source_id);
375 DCHECK(disconnected);
376 return;
377 }
378 }
379 }
380
ScheduleDoPoll()381 void GamepadProvider::ScheduleDoPoll() {
382 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
383 if (have_scheduled_do_poll_)
384 return;
385
386 {
387 base::AutoLock lock(is_paused_lock_);
388 if (is_paused_)
389 return;
390 }
391
392 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
393 FROM_HERE, base::BindOnce(&GamepadProvider::DoPoll, Unretained(this)),
394 sampling_interval_delta_);
395 have_scheduled_do_poll_ = true;
396 }
397
OnGamepadConnectionChange(bool connected,uint32_t index,const Gamepad & pad)398 void GamepadProvider::OnGamepadConnectionChange(bool connected,
399 uint32_t index,
400 const Gamepad& pad) {
401 if (connection_change_client_)
402 connection_change_client_->OnGamepadConnectionChange(connected, index, pad);
403 }
404
CheckForUserGesture()405 bool GamepadProvider::CheckForUserGesture() {
406 base::AutoLock lock(user_gesture_lock_);
407 if (user_gesture_observers_.empty() && ever_had_user_gesture_)
408 return false;
409
410 const Gamepads* pads = gamepad_shared_buffer_->buffer();
411 if (GamepadsHaveUserGesture(*pads)) {
412 ever_had_user_gesture_ = true;
413 for (auto& closure_and_thread : user_gesture_observers_) {
414 closure_and_thread.second->PostTask(FROM_HERE,
415 std::move(closure_and_thread.first));
416 }
417 user_gesture_observers_.clear();
418 return true;
419 }
420 return false;
421 }
422
423 } // namespace device
424