1 // Copyright 2013 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 "ui/events/ozone/evdev/event_device_info.h"
6
7 #include <linux/input.h>
8
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "ui/events/devices/device_util_linux.h"
14
15 #if !defined(EVIOCGMTSLOTS)
16 #define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len)
17 #endif
18
19 namespace ui {
20
21 namespace {
22
23 // USB vendor and product strings are pragmatically limited to 126
24 // characters each, so device names more than twice that should be
25 // unusual.
26 const size_t kMaximumDeviceNameLength = 256;
27
28 constexpr struct {
29 uint16_t vendor;
30 uint16_t product_id;
31 } kKeyboardBlocklist[] = {
32 {0x045e, 0x0b05}, // Xbox One Elite Series 2 gamepad
33 };
34
35 constexpr struct {
36 uint16_t vendor;
37 uint16_t product_id;
38 } kStylusButtonDevices[] = {
39 {0x413c, 0x81d5}, // Dell Active Pen PN579X
40 };
41
GetEventBits(int fd,const base::FilePath & path,unsigned int type,void * buf,unsigned int size)42 bool GetEventBits(int fd,
43 const base::FilePath& path,
44 unsigned int type,
45 void* buf,
46 unsigned int size) {
47 if (ioctl(fd, EVIOCGBIT(type, size), buf) < 0) {
48 PLOG(ERROR) << "Failed EVIOCGBIT (path=" << path.value() << " type=" << type
49 << " size=" << size << ")";
50 return false;
51 }
52
53 return true;
54 }
55
GetPropBits(int fd,const base::FilePath & path,void * buf,unsigned int size)56 bool GetPropBits(int fd,
57 const base::FilePath& path,
58 void* buf,
59 unsigned int size) {
60 if (ioctl(fd, EVIOCGPROP(size), buf) < 0) {
61 PLOG(ERROR) << "Failed EVIOCGPROP (path=" << path.value() << ")";
62 return false;
63 }
64
65 return true;
66 }
67
GetAbsInfo(int fd,const base::FilePath & path,int code,struct input_absinfo * absinfo)68 bool GetAbsInfo(int fd,
69 const base::FilePath& path,
70 int code,
71 struct input_absinfo* absinfo) {
72 if (ioctl(fd, EVIOCGABS(code), absinfo)) {
73 PLOG(ERROR) << "Failed EVIOCGABS (path=" << path.value() << " code=" << code
74 << ")";
75 return false;
76 }
77 return true;
78 }
79
GetDeviceName(int fd,const base::FilePath & path,std::string * name)80 bool GetDeviceName(int fd, const base::FilePath& path, std::string* name) {
81 char device_name[kMaximumDeviceNameLength];
82 if (ioctl(fd, EVIOCGNAME(kMaximumDeviceNameLength - 1), &device_name) < 0) {
83 PLOG(INFO) << "Failed EVIOCGNAME (path=" << path.value() << ")";
84 return false;
85 }
86 *name = device_name;
87 return true;
88 }
89
GetDeviceIdentifiers(int fd,const base::FilePath & path,input_id * id)90 bool GetDeviceIdentifiers(int fd, const base::FilePath& path, input_id* id) {
91 *id = {};
92 if (ioctl(fd, EVIOCGID, id) < 0) {
93 PLOG(INFO) << "Failed EVIOCGID (path=" << path.value() << ")";
94 return false;
95 }
96 return true;
97 }
98
GetDevicePhysInfo(int fd,const base::FilePath & path,std::string * phys)99 void GetDevicePhysInfo(int fd, const base::FilePath& path, std::string* phys) {
100 char device_phys[kMaximumDeviceNameLength];
101 if (ioctl(fd, EVIOCGPHYS(kMaximumDeviceNameLength - 1), &device_phys) < 0) {
102 PLOG(INFO) << "Failed EVIOCGPHYS (path=" << path.value() << ")";
103 return;
104 }
105 *phys = device_phys;
106 }
107
108 // |request| needs to be the equivalent to:
109 // struct input_mt_request_layout {
110 // uint32_t code;
111 // int32_t values[num_slots];
112 // };
113 //
114 // |size| is num_slots + 1 (for code).
GetSlotValues(int fd,const base::FilePath & path,int32_t * request,unsigned int size)115 void GetSlotValues(int fd,
116 const base::FilePath& path,
117 int32_t* request,
118 unsigned int size) {
119 size_t data_size = size * sizeof(*request);
120 if (ioctl(fd, EVIOCGMTSLOTS(data_size), request) < 0) {
121 PLOG(ERROR) << "Failed EVIOCGMTSLOTS (code=" << request[0]
122 << " path=" << path.value() << ")";
123 }
124 }
125
AssignBitset(const unsigned long * src,size_t src_len,unsigned long * dst,size_t dst_len)126 void AssignBitset(const unsigned long* src,
127 size_t src_len,
128 unsigned long* dst,
129 size_t dst_len) {
130 memcpy(dst, src, std::min(src_len, dst_len) * sizeof(unsigned long));
131 if (src_len < dst_len)
132 memset(&dst[src_len], 0, (dst_len - src_len) * sizeof(unsigned long));
133 }
134
IsBlacklistedAbsoluteMouseDevice(const input_id & id)135 bool IsBlacklistedAbsoluteMouseDevice(const input_id& id) {
136 static constexpr struct {
137 uint16_t vid;
138 uint16_t pid;
139 } kUSBLegacyBlackListedDevices[] = {
140 {0x222a, 0x0001}, // ILITEK ILITEK-TP
141 };
142
143 for (size_t i = 0; i < base::size(kUSBLegacyBlackListedDevices); ++i) {
144 if (id.vendor == kUSBLegacyBlackListedDevices[i].vid &&
145 id.product == kUSBLegacyBlackListedDevices[i].pid) {
146 return true;
147 }
148 }
149
150 return false;
151 }
152
153 } // namespace
154
EventDeviceInfo()155 EventDeviceInfo::EventDeviceInfo() {
156 memset(ev_bits_, 0, sizeof(ev_bits_));
157 memset(key_bits_, 0, sizeof(key_bits_));
158 memset(rel_bits_, 0, sizeof(rel_bits_));
159 memset(abs_bits_, 0, sizeof(abs_bits_));
160 memset(msc_bits_, 0, sizeof(msc_bits_));
161 memset(sw_bits_, 0, sizeof(sw_bits_));
162 memset(led_bits_, 0, sizeof(led_bits_));
163 memset(prop_bits_, 0, sizeof(prop_bits_));
164 memset(abs_info_, 0, sizeof(abs_info_));
165 }
166
~EventDeviceInfo()167 EventDeviceInfo::~EventDeviceInfo() {}
168
Initialize(int fd,const base::FilePath & path)169 bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) {
170 if (!GetEventBits(fd, path, 0, ev_bits_, sizeof(ev_bits_)))
171 return false;
172
173 if (!GetEventBits(fd, path, EV_KEY, key_bits_, sizeof(key_bits_)))
174 return false;
175
176 if (!GetEventBits(fd, path, EV_REL, rel_bits_, sizeof(rel_bits_)))
177 return false;
178
179 if (!GetEventBits(fd, path, EV_ABS, abs_bits_, sizeof(abs_bits_)))
180 return false;
181
182 if (!GetEventBits(fd, path, EV_MSC, msc_bits_, sizeof(msc_bits_)))
183 return false;
184
185 if (!GetEventBits(fd, path, EV_SW, sw_bits_, sizeof(sw_bits_)))
186 return false;
187
188 if (!GetEventBits(fd, path, EV_LED, led_bits_, sizeof(led_bits_)))
189 return false;
190
191 if (!GetPropBits(fd, path, prop_bits_, sizeof(prop_bits_)))
192 return false;
193
194 for (unsigned int i = 0; i < ABS_CNT; ++i)
195 if (HasAbsEvent(i))
196 if (!GetAbsInfo(fd, path, i, &abs_info_[i]))
197 return false;
198
199 int max_num_slots = GetAbsMtSlotCount();
200
201 // |request| is MT code + slots.
202 int32_t request[max_num_slots + 1];
203 int32_t* request_code = &request[0];
204 int32_t* request_slots = &request[1];
205 for (unsigned int i = EVDEV_ABS_MT_FIRST; i <= EVDEV_ABS_MT_LAST; ++i) {
206 if (!HasAbsEvent(i))
207 continue;
208
209 memset(request, 0, sizeof(request));
210 *request_code = i;
211 GetSlotValues(fd, path, request, max_num_slots + 1);
212
213 std::vector<int32_t>* slots = &slot_values_[i - EVDEV_ABS_MT_FIRST];
214 slots->assign(request_slots, request_slots + max_num_slots);
215 }
216
217 if (!GetDeviceName(fd, path, &name_))
218 return false;
219
220 if (!GetDeviceIdentifiers(fd, path, &input_id_))
221 return false;
222
223 GetDevicePhysInfo(fd, path, &phys_);
224
225 device_type_ = GetInputDeviceTypeFromId(input_id_);
226 if (device_type_ == InputDeviceType::INPUT_DEVICE_UNKNOWN)
227 device_type_ = GetInputDeviceTypeFromPath(path);
228
229 return true;
230 }
231
SetEventTypes(const unsigned long * ev_bits,size_t len)232 void EventDeviceInfo::SetEventTypes(const unsigned long* ev_bits, size_t len) {
233 AssignBitset(ev_bits, len, ev_bits_, base::size(ev_bits_));
234 }
235
SetKeyEvents(const unsigned long * key_bits,size_t len)236 void EventDeviceInfo::SetKeyEvents(const unsigned long* key_bits, size_t len) {
237 AssignBitset(key_bits, len, key_bits_, base::size(key_bits_));
238 }
239
SetRelEvents(const unsigned long * rel_bits,size_t len)240 void EventDeviceInfo::SetRelEvents(const unsigned long* rel_bits, size_t len) {
241 AssignBitset(rel_bits, len, rel_bits_, base::size(rel_bits_));
242 }
243
SetAbsEvents(const unsigned long * abs_bits,size_t len)244 void EventDeviceInfo::SetAbsEvents(const unsigned long* abs_bits, size_t len) {
245 AssignBitset(abs_bits, len, abs_bits_, base::size(abs_bits_));
246 }
247
SetMscEvents(const unsigned long * msc_bits,size_t len)248 void EventDeviceInfo::SetMscEvents(const unsigned long* msc_bits, size_t len) {
249 AssignBitset(msc_bits, len, msc_bits_, base::size(msc_bits_));
250 }
251
SetSwEvents(const unsigned long * sw_bits,size_t len)252 void EventDeviceInfo::SetSwEvents(const unsigned long* sw_bits, size_t len) {
253 AssignBitset(sw_bits, len, sw_bits_, base::size(sw_bits_));
254 }
255
SetLedEvents(const unsigned long * led_bits,size_t len)256 void EventDeviceInfo::SetLedEvents(const unsigned long* led_bits, size_t len) {
257 AssignBitset(led_bits, len, led_bits_, base::size(led_bits_));
258 }
259
SetProps(const unsigned long * prop_bits,size_t len)260 void EventDeviceInfo::SetProps(const unsigned long* prop_bits, size_t len) {
261 AssignBitset(prop_bits, len, prop_bits_, base::size(prop_bits_));
262 }
263
SetAbsInfo(unsigned int code,const input_absinfo & abs_info)264 void EventDeviceInfo::SetAbsInfo(unsigned int code,
265 const input_absinfo& abs_info) {
266 if (code > ABS_MAX)
267 return;
268
269 memcpy(&abs_info_[code], &abs_info, sizeof(abs_info));
270 }
271
SetAbsMtSlots(unsigned int code,const std::vector<int32_t> & values)272 void EventDeviceInfo::SetAbsMtSlots(unsigned int code,
273 const std::vector<int32_t>& values) {
274 DCHECK_EQ(GetAbsMtSlotCount(), values.size());
275 int index = code - EVDEV_ABS_MT_FIRST;
276 if (index < 0 || index >= EVDEV_ABS_MT_COUNT)
277 return;
278 slot_values_[index] = values;
279 }
280
SetAbsMtSlot(unsigned int code,unsigned int slot,uint32_t value)281 void EventDeviceInfo::SetAbsMtSlot(unsigned int code,
282 unsigned int slot,
283 uint32_t value) {
284 int index = code - EVDEV_ABS_MT_FIRST;
285 if (index < 0 || index >= EVDEV_ABS_MT_COUNT)
286 return;
287 slot_values_[index][slot] = value;
288 }
289
SetDeviceType(InputDeviceType type)290 void EventDeviceInfo::SetDeviceType(InputDeviceType type) {
291 device_type_ = type;
292 }
293
SetId(input_id id)294 void EventDeviceInfo::SetId(input_id id) {
295 input_id_ = id;
296 }
SetName(const std::string & name)297 void EventDeviceInfo::SetName(const std::string& name) {
298 name_ = name;
299 }
300
HasEventType(unsigned int type) const301 bool EventDeviceInfo::HasEventType(unsigned int type) const {
302 if (type > EV_MAX)
303 return false;
304 return EvdevBitIsSet(ev_bits_, type);
305 }
306
HasKeyEvent(unsigned int code) const307 bool EventDeviceInfo::HasKeyEvent(unsigned int code) const {
308 if (code > KEY_MAX)
309 return false;
310 return EvdevBitIsSet(key_bits_, code);
311 }
312
HasRelEvent(unsigned int code) const313 bool EventDeviceInfo::HasRelEvent(unsigned int code) const {
314 if (code > REL_MAX)
315 return false;
316 return EvdevBitIsSet(rel_bits_, code);
317 }
318
HasAbsEvent(unsigned int code) const319 bool EventDeviceInfo::HasAbsEvent(unsigned int code) const {
320 if (code > ABS_MAX)
321 return false;
322 return EvdevBitIsSet(abs_bits_, code);
323 }
324
HasMscEvent(unsigned int code) const325 bool EventDeviceInfo::HasMscEvent(unsigned int code) const {
326 if (code > MSC_MAX)
327 return false;
328 return EvdevBitIsSet(msc_bits_, code);
329 }
330
HasSwEvent(unsigned int code) const331 bool EventDeviceInfo::HasSwEvent(unsigned int code) const {
332 if (code > SW_MAX)
333 return false;
334 return EvdevBitIsSet(sw_bits_, code);
335 }
336
HasLedEvent(unsigned int code) const337 bool EventDeviceInfo::HasLedEvent(unsigned int code) const {
338 if (code > LED_MAX)
339 return false;
340 return EvdevBitIsSet(led_bits_, code);
341 }
342
HasProp(unsigned int code) const343 bool EventDeviceInfo::HasProp(unsigned int code) const {
344 if (code > INPUT_PROP_MAX)
345 return false;
346 return EvdevBitIsSet(prop_bits_, code);
347 }
348
GetAbsMinimum(unsigned int code) const349 int32_t EventDeviceInfo::GetAbsMinimum(unsigned int code) const {
350 return abs_info_[code].minimum;
351 }
352
GetAbsMaximum(unsigned int code) const353 int32_t EventDeviceInfo::GetAbsMaximum(unsigned int code) const {
354 return abs_info_[code].maximum;
355 }
356
GetAbsResolution(unsigned int code) const357 int32_t EventDeviceInfo::GetAbsResolution(unsigned int code) const {
358 return abs_info_[code].resolution;
359 }
360
GetAbsValue(unsigned int code) const361 int32_t EventDeviceInfo::GetAbsValue(unsigned int code) const {
362 return abs_info_[code].value;
363 }
364
GetAbsInfoByCode(unsigned int code) const365 input_absinfo EventDeviceInfo::GetAbsInfoByCode(unsigned int code) const {
366 return abs_info_[code];
367 }
368
GetAbsMtSlotCount() const369 uint32_t EventDeviceInfo::GetAbsMtSlotCount() const {
370 if (!HasAbsEvent(ABS_MT_SLOT))
371 return 0;
372 return GetAbsMaximum(ABS_MT_SLOT) + 1;
373 }
374
GetAbsMtSlotValue(unsigned int code,unsigned int slot) const375 int32_t EventDeviceInfo::GetAbsMtSlotValue(unsigned int code,
376 unsigned int slot) const {
377 unsigned int index = code - EVDEV_ABS_MT_FIRST;
378 DCHECK(index < EVDEV_ABS_MT_COUNT);
379 return slot_values_[index][slot];
380 }
381
GetAbsMtSlotValueWithDefault(unsigned int code,unsigned int slot,int32_t default_value) const382 int32_t EventDeviceInfo::GetAbsMtSlotValueWithDefault(
383 unsigned int code,
384 unsigned int slot,
385 int32_t default_value) const {
386 if (!HasAbsEvent(code))
387 return default_value;
388 return GetAbsMtSlotValue(code, slot);
389 }
390
HasAbsXY() const391 bool EventDeviceInfo::HasAbsXY() const {
392 return HasAbsEvent(ABS_X) && HasAbsEvent(ABS_Y);
393 }
394
HasMTAbsXY() const395 bool EventDeviceInfo::HasMTAbsXY() const {
396 return HasAbsEvent(ABS_MT_POSITION_X) && HasAbsEvent(ABS_MT_POSITION_Y);
397 }
398
HasRelXY() const399 bool EventDeviceInfo::HasRelXY() const {
400 return HasRelEvent(REL_X) && HasRelEvent(REL_Y);
401 }
402
HasMultitouch() const403 bool EventDeviceInfo::HasMultitouch() const {
404 return HasAbsEvent(ABS_MT_SLOT);
405 }
406
HasDirect() const407 bool EventDeviceInfo::HasDirect() const {
408 bool has_direct = HasProp(INPUT_PROP_DIRECT);
409 bool has_pointer = HasProp(INPUT_PROP_POINTER);
410 if (has_direct || has_pointer)
411 return has_direct;
412
413 switch (ProbeLegacyAbsoluteDevice()) {
414 case LegacyAbsoluteDeviceType::TOUCHSCREEN:
415 return true;
416
417 case LegacyAbsoluteDeviceType::TABLET:
418 case LegacyAbsoluteDeviceType::TOUCHPAD:
419 case LegacyAbsoluteDeviceType::NONE:
420 return false;
421 }
422
423 NOTREACHED();
424 return false;
425 }
426
HasPointer() const427 bool EventDeviceInfo::HasPointer() const {
428 bool has_direct = HasProp(INPUT_PROP_DIRECT);
429 bool has_pointer = HasProp(INPUT_PROP_POINTER);
430 if (has_direct || has_pointer)
431 return has_pointer;
432
433 switch (ProbeLegacyAbsoluteDevice()) {
434 case LegacyAbsoluteDeviceType::TOUCHPAD:
435 case LegacyAbsoluteDeviceType::TABLET:
436 return true;
437
438 case LegacyAbsoluteDeviceType::TOUCHSCREEN:
439 case LegacyAbsoluteDeviceType::NONE:
440 return false;
441 }
442
443 NOTREACHED();
444 return false;
445 }
446
HasStylus() const447 bool EventDeviceInfo::HasStylus() const {
448 return HasKeyEvent(BTN_TOOL_PEN) || HasKeyEvent(BTN_STYLUS) ||
449 HasKeyEvent(BTN_STYLUS2);
450 }
451
IsStylusButtonDevice() const452 bool EventDeviceInfo::IsStylusButtonDevice() const {
453 for (const auto& device_id : kStylusButtonDevices) {
454 if (input_id_.vendor == device_id.vendor &&
455 input_id_.product == device_id.product_id)
456 return true;
457 }
458
459 return false;
460 }
461
IsInKeyboardBlockList(input_id input_id_)462 bool IsInKeyboardBlockList(input_id input_id_) {
463 for (const auto& blocklist_id : kKeyboardBlocklist) {
464 if (input_id_.vendor == blocklist_id.vendor &&
465 input_id_.product == blocklist_id.product_id)
466 return true;
467 }
468
469 return false;
470 }
471
HasKeyboard() const472 bool EventDeviceInfo::HasKeyboard() const {
473 if (!HasEventType(EV_KEY))
474 return false;
475 if (IsInKeyboardBlockList(input_id_))
476 return false;
477 if (IsStylusButtonDevice())
478 return false;
479
480 // Check first 31 keys: If we have all of them, consider it a full
481 // keyboard. This is exactly what udev does for ID_INPUT_KEYBOARD.
482 for (int key = KEY_ESC; key <= KEY_D; ++key)
483 if (!HasKeyEvent(key))
484 return false;
485
486 return true;
487 }
488
HasMouse() const489 bool EventDeviceInfo::HasMouse() const {
490 return HasRelXY();
491 }
492
HasTouchpad() const493 bool EventDeviceInfo::HasTouchpad() const {
494 return HasAbsXY() && HasPointer() && !HasStylus();
495 }
496
HasTablet() const497 bool EventDeviceInfo::HasTablet() const {
498 return HasAbsXY() && HasPointer() && HasStylus();
499 }
500
HasTouchscreen() const501 bool EventDeviceInfo::HasTouchscreen() const {
502 return HasAbsXY() && HasDirect();
503 }
504
HasGamepad() const505 bool EventDeviceInfo::HasGamepad() const {
506 if (!HasEventType(EV_KEY))
507 return false;
508
509 // If the device has gamepad button, and it's not keyboard or tablet, it will
510 // be considered to be a gamepad. Note: this WILL have false positives and
511 // false negatives. A concrete solution will use ID_INPUT_JOYSTICK with some
512 // patch removing false positives.
513 bool support_gamepad_btn = false;
514 for (int key = BTN_JOYSTICK; key <= BTN_THUMBR; ++key) {
515 if (HasKeyEvent(key))
516 support_gamepad_btn = true;
517 }
518
519 return support_gamepad_btn && !HasTablet() && !HasKeyboard();
520 }
521
522 // static
GetInputDeviceTypeFromId(input_id id)523 ui::InputDeviceType EventDeviceInfo::GetInputDeviceTypeFromId(input_id id) {
524 static constexpr struct {
525 uint16_t vid;
526 uint16_t pid;
527 } kUSBInternalDevices[] = {
528 {0x18d1, 0x502b}, // Google, Hammer PID (soraka)
529 {0x18d1, 0x5030}, // Google, Whiskers PID (nocturne)
530 {0x18d1, 0x503c}, // Google, Masterball PID (krane)
531 {0x18d1, 0x503d}, // Google, Magnemite PID (kodama)
532 {0x18d1, 0x5044}, // Google, Moonball PID (kakadu)
533 {0x1fd2, 0x8103}, // LG, Internal TouchScreen PID
534 };
535
536 if (id.bustype == BUS_USB) {
537 for (size_t i = 0; i < base::size(kUSBInternalDevices); ++i) {
538 if (id.vendor == kUSBInternalDevices[i].vid &&
539 id.product == kUSBInternalDevices[i].pid)
540 return InputDeviceType::INPUT_DEVICE_INTERNAL;
541 }
542 }
543
544 switch (id.bustype) {
545 case BUS_I2C:
546 case BUS_I8042:
547 return ui::InputDeviceType::INPUT_DEVICE_INTERNAL;
548 case BUS_USB:
549 return ui::InputDeviceType::INPUT_DEVICE_USB;
550 case BUS_BLUETOOTH:
551 return ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH;
552 default:
553 return ui::InputDeviceType::INPUT_DEVICE_UNKNOWN;
554 }
555 }
556
557 EventDeviceInfo::LegacyAbsoluteDeviceType
ProbeLegacyAbsoluteDevice() const558 EventDeviceInfo::ProbeLegacyAbsoluteDevice() const {
559 if (!HasAbsXY())
560 return LegacyAbsoluteDeviceType::NONE;
561
562 // Treat internal stylus devices as touchscreens.
563 if (device_type_ == INPUT_DEVICE_INTERNAL && HasStylus())
564 return LegacyAbsoluteDeviceType::TOUCHSCREEN;
565
566 if (HasStylus())
567 return LegacyAbsoluteDeviceType::TABLET;
568
569 if (HasKeyEvent(BTN_TOOL_FINGER) && HasKeyEvent(BTN_TOUCH))
570 return LegacyAbsoluteDeviceType::TOUCHPAD;
571
572 if (HasKeyEvent(BTN_TOUCH))
573 return LegacyAbsoluteDeviceType::TOUCHSCREEN;
574
575 // ABS_Z mitigation for extra device on some Elo devices.
576 if (HasKeyEvent(BTN_LEFT) && !HasAbsEvent(ABS_Z) &&
577 !IsBlacklistedAbsoluteMouseDevice(input_id_))
578 return LegacyAbsoluteDeviceType::TOUCHSCREEN;
579
580 return LegacyAbsoluteDeviceType::NONE;
581 }
582
583 } // namespace ui
584