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 <cmath>
6#include <set>
7#include <vector>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/threading/thread.h"
14#include "base/threading/thread_restrictions.h"
15#include "content/browser/gamepad/gamepad_data_fetcher.h"
16#include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
17#include "content/browser/gamepad/gamepad_provider.h"
18#include "content/browser/gamepad/gamepad_service.h"
19#include "content/common/gamepad_hardware_buffer.h"
20#include "content/common/gamepad_messages.h"
21#include "content/common/gamepad_user_gesture.h"
22#include "content/public/browser/browser_thread.h"
23
24using blink::WebGamepad;
25using blink::WebGamepads;
26
27namespace content {
28
29GamepadProvider::ClosureAndThread::ClosureAndThread(
30    const base::Closure& c,
31    const scoped_refptr<base::MessageLoopProxy>& m)
32    : closure(c),
33      message_loop(m) {
34}
35
36GamepadProvider::ClosureAndThread::~ClosureAndThread() {
37}
38
39GamepadProvider::GamepadProvider()
40    : is_paused_(true),
41      have_scheduled_do_poll_(false),
42      devices_changed_(true),
43      ever_had_user_gesture_(false) {
44  Initialize(scoped_ptr<GamepadDataFetcher>());
45}
46
47GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
48    : is_paused_(true),
49      have_scheduled_do_poll_(false),
50      devices_changed_(true),
51      ever_had_user_gesture_(false) {
52  Initialize(fetcher.Pass());
53}
54
55GamepadProvider::~GamepadProvider() {
56  base::SystemMonitor* monitor = base::SystemMonitor::Get();
57  if (monitor)
58    monitor->RemoveDevicesChangedObserver(this);
59
60  // Use Stop() to join the polling thread, as there may be pending callbacks
61  // which dereference |polling_thread_|.
62  polling_thread_->Stop();
63  data_fetcher_.reset();
64}
65
66base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
67    base::ProcessHandle process) {
68  base::SharedMemoryHandle renderer_handle;
69  gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
70  return renderer_handle;
71}
72
73void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
74  const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
75  base::AutoLock lock(shared_memory_lock_);
76  *data = pads;
77}
78
79void GamepadProvider::Pause() {
80  {
81    base::AutoLock lock(is_paused_lock_);
82    is_paused_ = true;
83  }
84  base::MessageLoop* polling_loop = polling_thread_->message_loop();
85  polling_loop->PostTask(
86      FROM_HERE,
87      base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
88}
89
90void GamepadProvider::Resume() {
91  {
92    base::AutoLock lock(is_paused_lock_);
93    if (!is_paused_)
94        return;
95    is_paused_ = false;
96  }
97
98  base::MessageLoop* polling_loop = polling_thread_->message_loop();
99  polling_loop->PostTask(
100      FROM_HERE,
101      base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
102  polling_loop->PostTask(
103      FROM_HERE,
104      base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
105}
106
107void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
108  base::AutoLock lock(user_gesture_lock_);
109  user_gesture_observers_.push_back(ClosureAndThread(
110      closure, base::MessageLoop::current()->message_loop_proxy()));
111}
112
113void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
114  base::AutoLock lock(devices_changed_lock_);
115  devices_changed_ = true;
116}
117
118void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
119  size_t data_size = sizeof(GamepadHardwareBuffer);
120  base::SystemMonitor* monitor = base::SystemMonitor::Get();
121  if (monitor)
122    monitor->AddDevicesChangedObserver(this);
123  bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
124  CHECK(res);
125  GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
126  memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
127  pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
128
129  polling_thread_.reset(new base::Thread("Gamepad polling thread"));
130#if defined(OS_LINUX)
131  // On Linux, the data fetcher needs to watch file descriptors, so the message
132  // loop needs to be a libevent loop.
133  const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
134#elif defined(OS_ANDROID)
135  // On Android, keeping a message loop of default type.
136  const base::MessageLoop::Type kMessageLoopType =
137      base::MessageLoop::TYPE_DEFAULT;
138#else
139  // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
140  // message loop needs to be a UI-type loop. On Windows it must be a UI loop
141  // to properly pump the MessageWindow that captures device state.
142  const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
143#endif
144  polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
145
146  polling_thread_->message_loop()->PostTask(
147      FROM_HERE,
148      base::Bind(&GamepadProvider::DoInitializePollingThread,
149                 base::Unretained(this),
150                 base::Passed(&fetcher)));
151}
152
153void GamepadProvider::DoInitializePollingThread(
154    scoped_ptr<GamepadDataFetcher> fetcher) {
155  DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
156  DCHECK(!data_fetcher_.get());  // Should only initialize once.
157
158  if (!fetcher)
159    fetcher.reset(new GamepadPlatformDataFetcher);
160  data_fetcher_ = fetcher.Pass();
161}
162
163void GamepadProvider::SendPauseHint(bool paused) {
164  DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
165  if (data_fetcher_)
166    data_fetcher_->PauseHint(paused);
167}
168
169bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
170  return connected_ == pad.connected &&
171         axes_length_ == pad.axesLength &&
172         buttons_length_ == pad.buttonsLength &&
173         memcmp(id_, pad.id, arraysize(id_)) == 0 &&
174         memcmp(mapping_, pad.mapping, arraysize(mapping_)) == 0;
175}
176
177void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
178  connected_ = pad.connected;
179  axes_length_ = pad.axesLength;
180  buttons_length_ = pad.buttonsLength;
181  memcpy(id_, pad.id, arraysize(id_));
182  memcpy(mapping_, pad.mapping, arraysize(mapping_));
183}
184
185void GamepadProvider::PadState::SetDisconnected() {
186  connected_ = false;
187  axes_length_ = 0;
188  buttons_length_ = 0;
189  memset(id_, 0, arraysize(id_));
190  memset(mapping_, 0, arraysize(mapping_));
191}
192
193void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
194  pad->connected = connected_;
195  pad->axesLength = axes_length_;
196  pad->buttonsLength = buttons_length_;
197  memcpy(pad->id, id_, arraysize(id_));
198  memcpy(pad->mapping, mapping_, arraysize(mapping_));
199  memset(pad->axes, 0, arraysize(pad->axes));
200  memset(pad->buttons, 0, arraysize(pad->buttons));
201}
202
203void GamepadProvider::DoPoll() {
204  DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
205  DCHECK(have_scheduled_do_poll_);
206  have_scheduled_do_poll_ = false;
207
208  bool changed;
209  GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
210
211  ANNOTATE_BENIGN_RACE_SIZED(
212      &hwbuf->buffer,
213      sizeof(WebGamepads),
214      "Racey reads are discarded");
215
216  {
217    base::AutoLock lock(devices_changed_lock_);
218    changed = devices_changed_;
219    devices_changed_ = false;
220  }
221
222  {
223    base::AutoLock lock(shared_memory_lock_);
224
225    // Acquire the SeqLock. There is only ever one writer to this data.
226    // See gamepad_hardware_buffer.h.
227    hwbuf->sequence.WriteBegin();
228    data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
229    hwbuf->sequence.WriteEnd();
230  }
231
232  if (ever_had_user_gesture_) {
233    for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
234      WebGamepad& pad = hwbuf->buffer.items[i];
235      PadState& state = pad_states_.get()[i];
236      if (pad.connected && !state.connected()) {
237        OnGamepadConnectionChange(true, i, pad);
238      } else if (!pad.connected && state.connected()) {
239        OnGamepadConnectionChange(false, i, pad);
240      } else if (pad.connected && state.connected() && !state.Match(pad)) {
241        WebGamepad old_pad;
242        state.AsWebGamepad(&old_pad);
243        OnGamepadConnectionChange(false, i, old_pad);
244        OnGamepadConnectionChange(true, i, pad);
245      }
246    }
247  }
248
249  CheckForUserGesture();
250
251  // Schedule our next interval of polling.
252  ScheduleDoPoll();
253}
254
255void GamepadProvider::ScheduleDoPoll() {
256  DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
257  if (have_scheduled_do_poll_)
258    return;
259
260  {
261    base::AutoLock lock(is_paused_lock_);
262    if (is_paused_)
263      return;
264  }
265
266  base::MessageLoop::current()->PostDelayedTask(
267      FROM_HERE,
268      base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
269      base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
270  have_scheduled_do_poll_ = true;
271}
272
273void GamepadProvider::OnGamepadConnectionChange(
274    bool connected, int index, const WebGamepad& pad) {
275  PadState& state = pad_states_.get()[index];
276  if (connected)
277    state.SetPad(pad);
278  else
279    state.SetDisconnected();
280
281  BrowserThread::PostTask(
282      BrowserThread::IO,
283      FROM_HERE,
284      base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
285                 base::Unretained(this),
286                 connected,
287                 index,
288                 pad));
289}
290
291void GamepadProvider::DispatchGamepadConnectionChange(
292    bool connected, int index, const WebGamepad& pad) {
293  if (connected)
294    GamepadService::GetInstance()->OnGamepadConnected(index, pad);
295  else
296    GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
297}
298
299GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
300  void* mem = gamepad_shared_memory_.memory();
301  CHECK(mem);
302  return static_cast<GamepadHardwareBuffer*>(mem);
303}
304
305void GamepadProvider::CheckForUserGesture() {
306  base::AutoLock lock(user_gesture_lock_);
307  if (user_gesture_observers_.empty() && ever_had_user_gesture_)
308    return;
309
310  bool had_gesture_before = ever_had_user_gesture_;
311  const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
312  if (GamepadsHaveUserGesture(pads)) {
313    ever_had_user_gesture_ = true;
314    for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
315      user_gesture_observers_[i].message_loop->PostTask(FROM_HERE,
316          user_gesture_observers_[i].closure);
317    }
318    user_gesture_observers_.clear();
319  }
320  if (!had_gesture_before && ever_had_user_gesture_) {
321    // Initialize pad_states_ for the first time.
322    for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
323      pad_states_.get()[i].SetPad(pads.items[i]);
324    }
325  }
326}
327
328}  // namespace content
329