1// Copyright 2014 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 "content/browser/gamepad/gamepad_platform_data_fetcher_android.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_array.h"
9#include "base/android/jni_string.h"
10#include "base/debug/trace_event.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_util.h"
13#include "base/strings/utf_string_conversions.h"
14
15#include "jni/GamepadList_jni.h"
16
17#include "third_party/WebKit/public/platform/WebGamepads.h"
18
19using base::android::AttachCurrentThread;
20using base::android::CheckException;
21using base::android::ClearException;
22using base::android::ConvertJavaStringToUTF8;
23using base::android::ScopedJavaLocalRef;
24using blink::WebGamepad;
25using blink::WebGamepads;
26
27namespace content {
28
29bool
30GamepadPlatformDataFetcherAndroid::RegisterGamepadPlatformDataFetcherAndroid(
31    JNIEnv* env) {
32  return RegisterNativesImpl(env);
33}
34
35GamepadPlatformDataFetcherAndroid::GamepadPlatformDataFetcherAndroid() {
36  PauseHint(false);
37}
38
39GamepadPlatformDataFetcherAndroid::~GamepadPlatformDataFetcherAndroid() {
40  PauseHint(true);
41}
42
43void GamepadPlatformDataFetcherAndroid::GetGamepadData(
44    blink::WebGamepads* pads,
45    bool devices_changed_hint) {
46  TRACE_EVENT0("GAMEPAD", "GetGamepadData");
47
48  pads->length = 0;
49
50  JNIEnv* env = AttachCurrentThread();
51  if (!env)
52    return;
53
54  Java_GamepadList_updateGamepadData(env, reinterpret_cast<intptr_t>(pads));
55}
56
57void GamepadPlatformDataFetcherAndroid::PauseHint(bool paused) {
58  JNIEnv* env = AttachCurrentThread();
59  if (!env)
60    return;
61
62  Java_GamepadList_notifyForGamepadsAccess(env, paused);
63}
64
65static void SetGamepadData(JNIEnv* env,
66                           jobject obj,
67                           jlong gamepads,
68                           jint index,
69                           jboolean mapping,
70                           jboolean connected,
71                           jstring devicename,
72                           jlong timestamp,
73                           jfloatArray jaxes,
74                           jfloatArray jbuttons) {
75  DCHECK(gamepads);
76  blink::WebGamepads* pads = reinterpret_cast<WebGamepads*>(gamepads);
77  DCHECK_EQ(pads->length, unsigned(index));
78  DCHECK_LT(index, static_cast<int>(blink::WebGamepads::itemsLengthCap));
79
80  ++pads->length;
81
82  blink::WebGamepad& pad = pads->items[index];
83
84  pad.connected = connected;
85
86  pad.timestamp = timestamp;
87
88  // Do not set gamepad parameters for all the gamepad devices that are not
89  // attached.
90  if (!connected)
91    return;
92
93  // Map the Gamepad DeviceName String to the WebGamepad Id. Ideally it should
94  // be mapped to vendor and product information but it is only available at
95  // kernel level and it can not be queried using class
96  // android.hardware.input.InputManager.
97  // TODO(SaurabhK): Store a cached WebGamePad object in
98  // GamepadPlatformDataFetcherAndroid and only update constant WebGamepad
99  // values when a device has changed.
100  base::string16 device_name;
101  base::android::ConvertJavaStringToUTF16(env, devicename, &device_name);
102  const size_t name_to_copy =
103      std::min(device_name.size(), WebGamepad::idLengthCap - 1);
104  memcpy(pad.id,
105         device_name.data(),
106         name_to_copy * sizeof(base::string16::value_type));
107  pad.id[name_to_copy] = 0;
108
109  base::string16 mapping_name = base::UTF8ToUTF16(mapping ? "standard" : "");
110  const size_t mapping_to_copy =
111      std::min(mapping_name.size(), WebGamepad::mappingLengthCap - 1);
112  memcpy(pad.mapping,
113         mapping_name.data(),
114         mapping_to_copy * sizeof(base::string16::value_type));
115  pad.mapping[mapping_to_copy] = 0;
116
117  pad.timestamp = timestamp;
118
119  std::vector<float> axes;
120  base::android::JavaFloatArrayToFloatVector(env, jaxes, &axes);
121
122  // Set WebGamepad axeslength to total number of axes on the gamepad device.
123  // Only return the first axesLengthCap if axeslength captured by GamepadList
124  // is larger than axesLengthCap.
125  pad.axesLength = std::min(static_cast<int>(axes.size()),
126                            static_cast<int>(WebGamepad::axesLengthCap));
127
128  // Copy axes state to the WebGamepad axes[].
129  for (unsigned int i = 0; i < pad.axesLength; i++) {
130    pad.axes[i] = static_cast<double>(axes[i]);
131  }
132
133  std::vector<float> buttons;
134  base::android::JavaFloatArrayToFloatVector(env, jbuttons, &buttons);
135
136  // Set WebGamepad buttonslength to total number of axes on the gamepad
137  // device. Only return the first buttonsLengthCap if axeslength captured by
138  // GamepadList is larger than buttonsLengthCap.
139  pad.buttonsLength = std::min(static_cast<int>(buttons.size()),
140                               static_cast<int>(WebGamepad::buttonsLengthCap));
141
142  // Copy buttons state to the WebGamepad buttons[].
143  for (unsigned int j = 0; j < pad.buttonsLength; j++) {
144    pad.buttons[j].pressed = buttons[j];
145    pad.buttons[j].value = buttons[j];
146  }
147}
148
149}  // namespace content
150