Merge pull request #26126 from stephan49/fix-pipewire-unlock-error
[xbmc.git] / xbmc / platform / android / peripherals / PeripheralBusAndroid.cpp
blob19a3b0607fb32e035f0056309e670f19b142c2cc
1 /*
2 * Copyright (C) 2016-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "PeripheralBusAndroid.h"
11 #include "AndroidJoystickTranslator.h"
12 #include "input/joysticks/JoystickTypes.h"
13 #include "peripherals/addons/PeripheralAddonTranslator.h"
14 #include "peripherals/devices/PeripheralJoystick.h"
15 #include "utils/StringUtils.h"
16 #include "utils/log.h"
18 #include "platform/android/activity/XBMCApp.h"
19 #include "platform/android/peripherals/AndroidJoystickState.h"
21 #include <algorithm>
22 #include <mutex>
23 #include <numeric>
25 #include <android/input.h>
26 #include <android/keycodes.h>
27 #include <androidjni/View.h>
29 using namespace KODI;
30 using namespace PERIPHERALS;
32 #define JOYSTICK_PROVIDER_ANDROID "android"
34 // Set this to the final key code in android/keycodes.h
35 const unsigned int KEY_CODE_FINAL = AKEYCODE_DEMO_APP_4;
37 static const std::string DeviceLocationPrefix = "android/inputdevice/";
39 CPeripheralBusAndroid::CPeripheralBusAndroid(CPeripherals& manager)
40 : CPeripheralBus("PeripBusAndroid", manager, PERIPHERAL_BUS_ANDROID)
42 // we don't need polling as we get notified through the IInputDeviceCallbacks interface
43 m_bNeedsPolling = false;
45 // register for input device callbacks
46 CXBMCApp::Get().RegisterInputDeviceCallbacks(this);
48 // register for input device events
49 CXBMCApp::Get().RegisterInputDeviceEventHandler(this);
51 // get all currently connected input devices
52 m_scanResults = GetInputDevices();
55 CPeripheralBusAndroid::~CPeripheralBusAndroid()
57 // unregister from input device events
58 CXBMCApp::Get().UnregisterInputDeviceEventHandler();
60 // unregister from input device callbacks
61 CXBMCApp::Get().UnregisterInputDeviceCallbacks();
64 bool CPeripheralBusAndroid::InitializeProperties(CPeripheral& peripheral)
66 if (!CPeripheralBus::InitializeProperties(peripheral))
67 return false;
69 if (peripheral.Type() != PERIPHERAL_JOYSTICK)
71 CLog::Log(LOGWARNING, "CPeripheralBusAndroid: invalid peripheral type: {}",
72 PeripheralTypeTranslator::TypeToString(peripheral.Type()));
73 return false;
76 int deviceId;
77 if (!GetDeviceId(peripheral.Location(), deviceId))
79 CLog::Log(LOGWARNING,
80 "CPeripheralBusAndroid: failed to initialize properties for peripheral \"{}\"",
81 peripheral.Location());
82 return false;
85 const CJNIViewInputDevice device = CXBMCApp::GetInputDevice(deviceId);
86 if (!device)
88 CLog::Log(LOGWARNING, "CPeripheralBusAndroid: failed to get input device with ID {}", deviceId);
89 return false;
92 CPeripheralJoystick& joystick = static_cast<CPeripheralJoystick&>(peripheral);
93 if (device.getControllerNumber() > 0)
94 joystick.SetRequestedPort(device.getControllerNumber() - 1);
95 joystick.SetProvider(JOYSTICK_PROVIDER_ANDROID);
97 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: Initializing device {} \"{}\"", deviceId,
98 peripheral.DeviceName());
100 // prepare the joystick state
101 CAndroidJoystickState state;
102 if (!state.Initialize(device))
104 CLog::Log(
105 LOGWARNING,
106 "CPeripheralBusAndroid: failed to initialize the state for input device \"{}\" with ID {}",
107 joystick.DeviceName(), deviceId);
108 return false;
111 // fill in the number of buttons, hats and axes
112 joystick.SetButtonCount(state.GetButtonCount());
113 joystick.SetAxisCount(state.GetAxisCount());
115 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
117 // remember the joystick state
118 m_joystickStates.insert(std::make_pair(deviceId, std::move(state)));
120 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: Device has {} buttons and {} axes",
121 joystick.ButtonCount(), joystick.AxisCount());
123 return true;
126 bool CPeripheralBusAndroid::InitializeButtonMap(const CPeripheral& peripheral,
127 JOYSTICK::IButtonMap& buttonMap) const
129 int deviceId;
130 if (!GetDeviceId(peripheral.Location(), deviceId))
132 CLog::Log(LOGWARNING,
133 "CPeripheralBusAndroid: failed to initialize buttonmap due to unknown device ID for "
134 "peripheral \"{}\"",
135 peripheral.Location());
136 return false;
139 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
141 // get the joystick state
142 auto it = m_joystickStates.find(deviceId);
143 if (it == m_joystickStates.end())
145 CLog::Log(LOGWARNING,
146 "CPeripheralBusAndroid: joystick with device ID {} not found for peripheral \"{}\"",
147 deviceId, peripheral.Location());
148 return false;
151 const CAndroidJoystickState& joystick = it->second;
152 if (joystick.GetButtonCount() == 0 && joystick.GetAxisCount() == 0)
154 CLog::Log(LOGDEBUG,
155 "CPeripheralBusAndroid: joystick has no buttons or axes for peripheral \"{}\"",
156 peripheral.Location());
157 return false;
160 if (!joystick.InitializeButtonMap(buttonMap))
162 CLog::Log(
163 LOGDEBUG,
164 "CPeripheralBusAndroid: failed to initialize joystick buttonmap for peripheral \"{}\"",
165 peripheral.Location());
166 return false;
169 return true;
172 std::string CPeripheralBusAndroid::GetAppearance(const CPeripheral& peripheral) const
174 int deviceId;
175 if (!GetDeviceId(peripheral.Location(), deviceId))
176 return "";
178 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
180 auto it = m_joystickStates.find(deviceId);
181 if (it == m_joystickStates.end())
182 return "";
184 const CAndroidJoystickState& joystick = it->second;
185 return joystick.GetAppearance();
188 void CPeripheralBusAndroid::Initialise(void)
190 CPeripheralBus::Initialise();
191 TriggerDeviceScan();
194 void CPeripheralBusAndroid::ProcessEvents()
196 std::vector<kodi::addon::PeripheralEvent> events;
198 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
199 for (auto& joystickState : m_joystickStates)
200 joystickState.second.GetEvents(events);
203 for (const auto& event : events)
205 PeripheralPtr device = GetPeripheral(GetDeviceLocation(event.PeripheralIndex()));
206 if (!device || device->Type() != PERIPHERAL_JOYSTICK)
207 continue;
209 CPeripheralJoystick* joystick = static_cast<CPeripheralJoystick*>(device.get());
210 switch (event.Type())
212 case PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON:
214 const bool bPressed = (event.ButtonState() == JOYSTICK_STATE_BUTTON_PRESSED);
215 joystick->OnButtonMotion(event.DriverIndex(), bPressed);
216 break;
218 case PERIPHERAL_EVENT_TYPE_DRIVER_AXIS:
220 joystick->OnAxisMotion(event.DriverIndex(), event.AxisState());
221 break;
223 default:
224 break;
229 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
230 for (const auto& joystickState : m_joystickStates)
232 PeripheralPtr device = GetPeripheral(GetDeviceLocation(joystickState.second.GetDeviceId()));
233 if (!device || device->Type() != PERIPHERAL_JOYSTICK)
234 continue;
236 static_cast<CPeripheralJoystick*>(device.get())->OnInputFrame();
241 void CPeripheralBusAndroid::OnInputDeviceAdded(int deviceId)
243 const std::string deviceLocation = GetDeviceLocation(deviceId);
245 std::unique_lock<CCriticalSection> lock(m_critSectionResults);
246 // add the device to the cached result list
247 const auto& it = std::find_if(m_scanResults.m_results.cbegin(), m_scanResults.m_results.cend(),
248 [&deviceLocation](const PeripheralScanResult& scanResult)
249 { return scanResult.m_strLocation == deviceLocation; });
251 if (it != m_scanResults.m_results.cend())
253 CLog::Log(LOGINFO,
254 "CPeripheralBusAndroid: ignoring added input device with ID {} because we already "
255 "know it",
256 deviceId);
257 return;
260 const CJNIViewInputDevice device = CXBMCApp::GetInputDevice(deviceId);
261 if (!device)
263 CLog::Log(LOGWARNING,
264 "CPeripheralBusAndroid: failed to add input device with ID {} because it couldn't "
265 "be found",
266 deviceId);
267 return;
270 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: Device added:");
271 LogInputDevice(device);
273 PeripheralScanResult result;
274 if (!ConvertToPeripheralScanResult(device, result))
275 return;
276 m_scanResults.m_results.emplace_back(std::move(result));
279 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: input device with ID {} added", deviceId);
280 OnDeviceAdded(deviceLocation);
283 void CPeripheralBusAndroid::OnInputDeviceChanged(int deviceId)
285 bool changed = false;
286 const std::string deviceLocation = GetDeviceLocation(deviceId);
288 std::unique_lock<CCriticalSection> lock(m_critSectionResults);
289 // change the device in the cached result list
290 for (auto result = m_scanResults.m_results.begin(); result != m_scanResults.m_results.end();
291 ++result)
293 if (result->m_strLocation == deviceLocation)
295 const CJNIViewInputDevice device = CXBMCApp::GetInputDevice(deviceId);
296 if (!device)
298 CLog::Log(LOGWARNING,
299 "CPeripheralBusAndroid: failed to update input device \"{}\" with ID {} "
300 "because it couldn't be found",
301 result->m_strDeviceName, deviceId);
302 return;
305 if (!ConvertToPeripheralScanResult(device, *result))
306 return;
308 CLog::Log(LOGINFO, "CPeripheralBusAndroid: input device \"{}\" with ID {} updated",
309 result->m_strDeviceName, deviceId);
310 changed = true;
311 break;
316 if (changed)
317 OnDeviceChanged(deviceLocation);
318 else
319 CLog::Log(LOGWARNING,
320 "CPeripheralBusAndroid: failed to update input device with ID {} because it couldn't "
321 "be found",
322 deviceId);
325 void CPeripheralBusAndroid::OnInputDeviceRemoved(int deviceId)
327 bool removed = false;
328 const std::string deviceLocation = GetDeviceLocation(deviceId);
330 std::unique_lock<CCriticalSection> lock(m_critSectionResults);
331 // remove the device from the cached result list
332 for (auto result = m_scanResults.m_results.begin(); result != m_scanResults.m_results.end();
333 ++result)
335 if (result->m_strLocation == deviceLocation)
337 CLog::Log(LOGINFO, "CPeripheralBusAndroid: input device \"{}\" with ID {} removed",
338 result->m_strDeviceName, deviceId);
339 m_scanResults.m_results.erase(result);
340 removed = true;
341 break;
346 if (removed)
349 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
350 m_joystickStates.erase(deviceId);
353 OnDeviceRemoved(deviceLocation);
355 else
356 CLog::Log(LOGWARNING,
357 "CPeripheralBusAndroid: failed to remove input device with ID {} because it couldn't "
358 "be found",
359 deviceId);
362 bool CPeripheralBusAndroid::OnInputDeviceEvent(const AInputEvent* event)
364 if (event == nullptr)
365 return false;
367 std::unique_lock<CCriticalSection> lock(m_critSectionStates);
368 // get the id of the input device which generated the event
369 int32_t deviceId = AInputEvent_getDeviceId(event);
371 // find the matching joystick state
372 auto joystickState = m_joystickStates.find(deviceId);
373 if (joystickState == m_joystickStates.end())
375 CLog::Log(LOGDEBUG,
376 "CPeripheralBusAndroid: ignoring input event for non-joystick device with ID {}",
377 deviceId);
378 return false;
381 return joystickState->second.ProcessEvent(event);
384 bool CPeripheralBusAndroid::PerformDeviceScan(PeripheralScanResults& results)
386 std::unique_lock<CCriticalSection> lock(m_critSectionResults);
387 results = m_scanResults;
389 return true;
392 PeripheralScanResults CPeripheralBusAndroid::GetInputDevices()
394 CLog::Log(LOGINFO, "CPeripheralBusAndroid: scanning for input devices...");
396 PeripheralScanResults results;
397 std::vector<int> deviceIds = CXBMCApp::GetInputDeviceIds();
399 for (const auto& deviceId : deviceIds)
401 const CJNIViewInputDevice device = CXBMCApp::GetInputDevice(deviceId);
402 if (!device)
404 CLog::Log(LOGWARNING, "CPeripheralBusAndroid: no input device with ID {} found", deviceId);
405 continue;
408 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: Device discovered:");
409 LogInputDevice(device);
411 PeripheralScanResult result;
412 if (!ConvertToPeripheralScanResult(device, result))
413 continue;
415 CLog::Log(LOGINFO, "CPeripheralBusAndroid: added input device");
416 results.m_results.emplace_back(std::move(result));
419 return results;
422 std::string CPeripheralBusAndroid::GetDeviceLocation(int deviceId)
424 return StringUtils::Format("{}{}", DeviceLocationPrefix, deviceId);
427 bool CPeripheralBusAndroid::GetDeviceId(const std::string& deviceLocation, int& deviceId)
429 if (deviceLocation.empty() || !StringUtils::StartsWith(deviceLocation, DeviceLocationPrefix) ||
430 deviceLocation.size() <= DeviceLocationPrefix.size())
431 return false;
433 std::string strDeviceId = deviceLocation.substr(DeviceLocationPrefix.size());
434 if (!StringUtils::IsNaturalNumber(strDeviceId))
435 return false;
437 deviceId = static_cast<int>(strtol(strDeviceId.c_str(), nullptr, 10));
438 return true;
441 bool CPeripheralBusAndroid::ConvertToPeripheralScanResult(
442 const CJNIViewInputDevice& inputDevice, PeripheralScanResult& peripheralScanResult)
444 if (inputDevice.isVirtual())
446 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: ignoring virtual input device");
447 return false;
450 if (!inputDevice.supportsSource(CJNIViewInputDevice::SOURCE_JOYSTICK) &&
451 !inputDevice.supportsSource(CJNIViewInputDevice::SOURCE_GAMEPAD))
453 CLog::Log(LOGDEBUG, "CPeripheralBusAndroid: ignoring non-joystick device");
454 return false;
457 peripheralScanResult.m_type = PERIPHERAL_JOYSTICK;
458 peripheralScanResult.m_strLocation = GetDeviceLocation(inputDevice.getId());
459 peripheralScanResult.m_iVendorId = inputDevice.getVendorId();
460 peripheralScanResult.m_iProductId = inputDevice.getProductId();
461 peripheralScanResult.m_mappedType = PERIPHERAL_JOYSTICK;
462 peripheralScanResult.m_strDeviceName = inputDevice.getName();
463 peripheralScanResult.m_busType = PERIPHERAL_BUS_ANDROID;
464 peripheralScanResult.m_mappedBusType = PERIPHERAL_BUS_ANDROID;
465 peripheralScanResult.m_iSequence = 0;
467 return true;
470 void CPeripheralBusAndroid::LogInputDevice(const CJNIViewInputDevice& device)
472 // Log device properties
473 CLog::Log(LOGDEBUG, " Name: \"{}\"", device.getName());
474 CLog::Log(LOGDEBUG, " ID: {}", device.getId());
475 CLog::Log(LOGDEBUG, " Controller number: {}", device.getControllerNumber());
476 std::string descriptor = device.getDescriptor();
477 if (descriptor.size() > 14)
478 CLog::Log(LOGDEBUG, " Descriptor: \"{}...\"", descriptor.substr(0, 14));
479 else
480 CLog::Log(LOGDEBUG, " Descriptor: \"{}\"", descriptor);
481 CLog::Log(LOGDEBUG, " Product ID: {:04X}", device.getProductId());
482 CLog::Log(LOGDEBUG, " Vendor ID: {:04X}", device.getVendorId());
483 CLog::Log(LOGDEBUG, " Has microphone: {}", device.hasMicrophone() ? "true" : "false");
484 CLog::Log(LOGDEBUG, " Is virtual: {}", device.isVirtual() ? "true" : "false");
486 // Log device sources
487 CLog::Log(LOGDEBUG, " Source flags: {:#08x}", device.getSources());
488 for (const auto& source : GetInputSources())
490 if (device.supportsSource(source.first))
491 CLog::Log(LOGDEBUG, " Has source: {} ({:#08x})", source.second, source.first);
494 // Log device keys
495 std::vector<int> keys(KEY_CODE_FINAL);
496 std::iota(keys.begin(), keys.end(), 1);
498 auto results = device.hasKeys(keys);
500 if (results.size() != keys.size())
502 CLog::Log(LOGERROR, "Failed to get key status for {} keys", keys.size());
504 else
506 for (unsigned int i = 0; i < keys.size(); i++)
508 if (results[i])
509 CLog::Log(LOGDEBUG, " Has key: {} ({})",
510 CAndroidJoystickTranslator::TranslateKeyCode(keys[i]), keys[i]);
514 // Log analog axes
515 const CJNIList<CJNIViewInputDeviceMotionRange> motionRanges = device.getMotionRanges();
516 for (int index = 0; index < motionRanges.size(); ++index)
518 const CJNIViewInputDeviceMotionRange motionRange = motionRanges.get(index);
520 int axisId = motionRange.getAxis();
521 CLog::Log(LOGDEBUG, " Has axis: {} ({})", CAndroidJoystickTranslator::TranslateAxis(axisId),
522 axisId);
523 CLog::Log(LOGDEBUG, " Endpoints: [{:f}, {:f}]", motionRange.getMin(),
524 motionRange.getMax());
525 CLog::Log(LOGDEBUG, " Center: {:f}", motionRange.getFlat());
526 CLog::Log(LOGDEBUG, " Fuzz: {:f}", motionRange.getFuzz());
530 std::vector<std::pair<int, const char*>> CPeripheralBusAndroid::GetInputSources()
532 std::vector<std::pair<int, const char*>> sources = {
533 {CJNIViewInputDevice::SOURCE_DPAD, "SOURCE_DPAD"},
534 {CJNIViewInputDevice::SOURCE_GAMEPAD, "SOURCE_GAMEPAD"},
535 {CJNIViewInputDevice::SOURCE_HDMI, "SOURCE_HDMI"},
536 {CJNIViewInputDevice::SOURCE_JOYSTICK, "SOURCE_JOYSTICK"},
537 {CJNIViewInputDevice::SOURCE_KEYBOARD, "SOURCE_KEYBOARD"},
538 {CJNIViewInputDevice::SOURCE_MOUSE, "SOURCE_MOUSE"},
539 {CJNIViewInputDevice::SOURCE_MOUSE_RELATIVE, "SOURCE_MOUSE_RELATIVE"},
540 {CJNIViewInputDevice::SOURCE_ROTARY_ENCODER, "SOURCE_ROTARY_ENCODER"},
541 {CJNIViewInputDevice::SOURCE_STYLUS, "SOURCE_STYLUS"},
542 {CJNIViewInputDevice::SOURCE_TOUCHPAD, "SOURCE_TOUCHPAD"},
543 {CJNIViewInputDevice::SOURCE_TOUCHSCREEN, "SOURCE_TOUCHSCREEN"},
544 {CJNIViewInputDevice::SOURCE_TOUCH_NAVIGATION, "SOURCE_TOUCH_NAVIGATION"},
545 {CJNIViewInputDevice::SOURCE_TRACKBALL, "SOURCE_TRACKBALL"},
548 sources.erase(std::remove_if(sources.begin(), sources.end(),
549 [](const std::pair<int, const char*>& source)
550 { return source.first == 0; }),
551 sources.end());
553 return sources;