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