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