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