blob: 78cdf8b97eef230057527d8971632a331a1dbd19 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.Context;
20import android.content.res.Configuration;
Michael Chan53071d62009-05-13 17:29:48 -070021import android.os.LatencyTimer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.os.PowerManager;
Michael Chan53071d62009-05-13 17:29:48 -070023import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.util.Log;
25import android.util.SparseArray;
26import android.view.Display;
27import android.view.KeyEvent;
28import android.view.MotionEvent;
29import android.view.RawInputEvent;
30import android.view.Surface;
31import android.view.WindowManagerPolicy;
32
33public abstract class KeyInputQueue {
34 static final String TAG = "KeyInputQueue";
35
36 SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
37
38 int mGlobalMetaState = 0;
39 boolean mHaveGlobalMetaState = false;
40
41 final QueuedEvent mFirst;
42 final QueuedEvent mLast;
43 QueuedEvent mCache;
44 int mCacheCount;
45
46 Display mDisplay = null;
47
48 int mOrientation = Surface.ROTATION_0;
49 int[] mKeyRotationMap = null;
50
51 PowerManager.WakeLock mWakeLock;
52
53 static final int[] KEY_90_MAP = new int[] {
54 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
55 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
56 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
57 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
58 };
59
60 static final int[] KEY_180_MAP = new int[] {
61 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
62 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
63 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
64 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
65 };
66
67 static final int[] KEY_270_MAP = new int[] {
68 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
69 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
70 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
71 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
72 };
73
74 public static final int FILTER_REMOVE = 0;
75 public static final int FILTER_KEEP = 1;
76 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -070077
78 private static final boolean MEASURE_LATENCY = false;
79 private LatencyTimer lt;
80
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 public interface FilterCallback {
82 int filterEvent(QueuedEvent ev);
83 }
84
85 static class QueuedEvent {
86 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -070087 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 int flags; // From the raw event
89 int classType; // One of the class constants in InputEvent
90 Object event;
91 boolean inQueue;
92
93 void copyFrom(QueuedEvent that) {
94 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -070095 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 this.flags = that.flags;
97 this.classType = that.classType;
98 this.event = that.event;
99 }
100
101 @Override
102 public String toString() {
103 return "QueuedEvent{"
104 + Integer.toHexString(System.identityHashCode(this))
105 + " " + event + "}";
106 }
107
108 // not copied
109 QueuedEvent prev;
110 QueuedEvent next;
111 }
112
113 KeyInputQueue(Context context) {
Michael Chan53071d62009-05-13 17:29:48 -0700114 if (MEASURE_LATENCY) {
115 lt = new LatencyTimer(100, 1000);
116 }
117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 PowerManager pm = (PowerManager)context.getSystemService(
119 Context.POWER_SERVICE);
120 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
121 "KeyInputQueue");
122 mWakeLock.setReferenceCounted(false);
123
124 mFirst = new QueuedEvent();
125 mLast = new QueuedEvent();
126 mFirst.next = mLast;
127 mLast.prev = mFirst;
128
129 mThread.start();
130 }
131
132 public void setDisplay(Display display) {
133 mDisplay = display;
134 }
135
136 public void getInputConfiguration(Configuration config) {
137 synchronized (mFirst) {
138 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
139 config.keyboard = Configuration.KEYBOARD_NOKEYS;
140 config.navigation = Configuration.NAVIGATION_NONAV;
141
142 final int N = mDevices.size();
143 for (int i=0; i<N; i++) {
144 InputDevice d = mDevices.valueAt(i);
145 if (d != null) {
146 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
147 config.touchscreen
148 = Configuration.TOUCHSCREEN_FINGER;
149 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
150 }
151 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
152 config.keyboard
153 = Configuration.KEYBOARD_QWERTY;
154 //Log.i("foo", "***** HAVE QWERTY!");
155 }
156 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
157 config.navigation
158 = Configuration.NAVIGATION_TRACKBALL;
159 //Log.i("foo", "***** HAVE TRACKBALL!");
160 }
161 }
162 }
163 }
164 }
165
166 public static native String getDeviceName(int deviceId);
167 public static native int getDeviceClasses(int deviceId);
168 public static native boolean getAbsoluteInfo(int deviceId, int axis,
169 InputDevice.AbsoluteInfo outInfo);
170 public static native int getSwitchState(int sw);
171 public static native int getSwitchState(int deviceId, int sw);
172 public static native int getScancodeState(int sw);
173 public static native int getScancodeState(int deviceId, int sw);
174 public static native int getKeycodeState(int sw);
175 public static native int getKeycodeState(int deviceId, int sw);
176 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
177
178 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
179 long eventTime, boolean down, int keycode, int repeatCount,
180 int scancode, int flags) {
181 return new KeyEvent(
182 downTime, eventTime,
183 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
184 keycode, repeatCount,
185 device != null ? device.mMetaKeysState : 0,
186 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700187 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189
190 Thread mThread = new Thread("InputDeviceReader") {
191 public void run() {
192 android.os.Process.setThreadPriority(
193 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
194
195 try {
196 RawInputEvent ev = new RawInputEvent();
197 while (true) {
198 InputDevice di;
199
200 // block, doesn't release the monitor
201 readEvent(ev);
202
203 boolean send = false;
204 boolean configChanged = false;
205
206 if (false) {
207 Log.i(TAG, "Input event: dev=0x"
208 + Integer.toHexString(ev.deviceId)
209 + " type=0x" + Integer.toHexString(ev.type)
210 + " scancode=" + ev.scancode
211 + " keycode=" + ev.keycode
212 + " value=" + ev.value);
213 }
214
215 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
216 synchronized (mFirst) {
217 di = newInputDevice(ev.deviceId);
218 mDevices.put(ev.deviceId, di);
219 configChanged = true;
220 }
221 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
222 synchronized (mFirst) {
223 Log.i(TAG, "Device removed: id=0x"
224 + Integer.toHexString(ev.deviceId));
225 di = mDevices.get(ev.deviceId);
226 if (di != null) {
227 mDevices.delete(ev.deviceId);
228 configChanged = true;
229 } else {
230 Log.w(TAG, "Bad device id: " + ev.deviceId);
231 }
232 }
233 } else {
234 di = getInputDevice(ev.deviceId);
235
236 // first crack at it
237 send = preprocessEvent(di, ev);
238
239 if (ev.type == RawInputEvent.EV_KEY) {
240 di.mMetaKeysState = makeMetaState(ev.keycode,
241 ev.value != 0, di.mMetaKeysState);
242 mHaveGlobalMetaState = false;
243 }
244 }
245
246 if (di == null) {
247 continue;
248 }
249
250 if (configChanged) {
251 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700252 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
254 null);
255 }
256 }
257
258 if (!send) {
259 continue;
260 }
261
262 synchronized (mFirst) {
263 // NOTE: The event timebase absolutely must be the same
264 // timebase as SystemClock.uptimeMillis().
265 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
266 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700267 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
269
270 final int classes = di.classes;
271 final int type = ev.type;
272 final int scancode = ev.scancode;
273 send = false;
274
275 // Is it a key event?
276 if (type == RawInputEvent.EV_KEY &&
277 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
278 (scancode < RawInputEvent.BTN_FIRST ||
279 scancode > RawInputEvent.BTN_LAST)) {
280 boolean down;
281 if (ev.value != 0) {
282 down = true;
283 di.mDownTime = curTime;
284 } else {
285 down = false;
286 }
287 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700288 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 RawInputEvent.CLASS_KEYBOARD,
290 newKeyEvent(di, di.mDownTime, curTime, down,
291 keycode, 0, scancode,
292 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
293 ? KeyEvent.FLAG_WOKE_HERE : 0));
294 } else if (ev.type == RawInputEvent.EV_KEY) {
295 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
296 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
297 di.mAbs.changed = true;
298 di.mAbs.down = ev.value != 0;
299 }
300 if (ev.scancode == RawInputEvent.BTN_MOUSE &&
301 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
302 di.mRel.changed = true;
303 di.mRel.down = ev.value != 0;
304 send = true;
305 }
306
307 } else if (ev.type == RawInputEvent.EV_ABS &&
308 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
309 if (ev.scancode == RawInputEvent.ABS_X) {
310 di.mAbs.changed = true;
311 di.mAbs.x = ev.value;
312 } else if (ev.scancode == RawInputEvent.ABS_Y) {
313 di.mAbs.changed = true;
314 di.mAbs.y = ev.value;
315 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
316 di.mAbs.changed = true;
317 di.mAbs.pressure = ev.value;
318 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
319 di.mAbs.changed = true;
320 di.mAbs.size = ev.value;
321 }
322
323 } else if (ev.type == RawInputEvent.EV_REL &&
324 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
325 // Add this relative movement into our totals.
326 if (ev.scancode == RawInputEvent.REL_X) {
327 di.mRel.changed = true;
328 di.mRel.x += ev.value;
329 } else if (ev.scancode == RawInputEvent.REL_Y) {
330 di.mRel.changed = true;
331 di.mRel.y += ev.value;
332 }
333 }
334
335 if (send || ev.type == RawInputEvent.EV_SYN) {
336 if (mDisplay != null) {
337 if (!mHaveGlobalMetaState) {
338 computeGlobalMetaStateLocked();
339 }
340
341 MotionEvent me;
Michael Chan53071d62009-05-13 17:29:48 -0700342 me = di.mAbs.generateMotion(di, curTime, curTimeNano, true,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 mDisplay, mOrientation, mGlobalMetaState);
344 if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
345 + " y=" + di.mAbs.y + " ev=" + me);
346 if (me != null) {
347 if (WindowManagerPolicy.WATCH_POINTER) {
348 Log.i(TAG, "Enqueueing: " + me);
349 }
Michael Chan53071d62009-05-13 17:29:48 -0700350 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 RawInputEvent.CLASS_TOUCHSCREEN, me);
352 }
Michael Chan53071d62009-05-13 17:29:48 -0700353 me = di.mRel.generateMotion(di, curTime, curTimeNano, false,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mDisplay, mOrientation, mGlobalMetaState);
355 if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
356 + " y=" + di.mRel.y + " ev=" + me);
357 if (me != null) {
Michael Chan53071d62009-05-13 17:29:48 -0700358 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 RawInputEvent.CLASS_TRACKBALL, me);
360 }
361 }
362 }
363 }
364 }
365 }
366 catch (RuntimeException exc) {
367 Log.e(TAG, "InputReaderThread uncaught exception", exc);
368 }
369 }
370 };
371
372 /**
373 * Returns a new meta state for the given keys and old state.
374 */
375 private static final int makeMetaState(int keycode, boolean down, int old) {
376 int mask;
377 switch (keycode) {
378 case KeyEvent.KEYCODE_ALT_LEFT:
379 mask = KeyEvent.META_ALT_LEFT_ON;
380 break;
381 case KeyEvent.KEYCODE_ALT_RIGHT:
382 mask = KeyEvent.META_ALT_RIGHT_ON;
383 break;
384 case KeyEvent.KEYCODE_SHIFT_LEFT:
385 mask = KeyEvent.META_SHIFT_LEFT_ON;
386 break;
387 case KeyEvent.KEYCODE_SHIFT_RIGHT:
388 mask = KeyEvent.META_SHIFT_RIGHT_ON;
389 break;
390 case KeyEvent.KEYCODE_SYM:
391 mask = KeyEvent.META_SYM_ON;
392 break;
393 default:
394 return old;
395 }
396 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
397 & (down ? (old | mask) : (old & ~mask));
398 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
399 result |= KeyEvent.META_ALT_ON;
400 }
401 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
402 result |= KeyEvent.META_SHIFT_ON;
403 }
404 return result;
405 }
406
407 private void computeGlobalMetaStateLocked() {
408 int i = mDevices.size();
409 mGlobalMetaState = 0;
410 while ((--i) >= 0) {
411 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
412 }
413 mHaveGlobalMetaState = true;
414 }
415
416 /*
417 * Return true if you want the event to get passed on to the
418 * rest of the system, and false if you've handled it and want
419 * it dropped.
420 */
421 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
422
423 InputDevice getInputDevice(int deviceId) {
424 synchronized (mFirst) {
425 return getInputDeviceLocked(deviceId);
426 }
427 }
428
429 private InputDevice getInputDeviceLocked(int deviceId) {
430 return mDevices.get(deviceId);
431 }
432
433 public void setOrientation(int orientation) {
434 synchronized(mFirst) {
435 mOrientation = orientation;
436 switch (orientation) {
437 case Surface.ROTATION_90:
438 mKeyRotationMap = KEY_90_MAP;
439 break;
440 case Surface.ROTATION_180:
441 mKeyRotationMap = KEY_180_MAP;
442 break;
443 case Surface.ROTATION_270:
444 mKeyRotationMap = KEY_270_MAP;
445 break;
446 default:
447 mKeyRotationMap = null;
448 break;
449 }
450 }
451 }
452
453 public int rotateKeyCode(int keyCode) {
454 synchronized(mFirst) {
455 return rotateKeyCodeLocked(keyCode);
456 }
457 }
458
459 private int rotateKeyCodeLocked(int keyCode) {
460 int[] map = mKeyRotationMap;
461 if (map != null) {
462 final int N = map.length;
463 for (int i=0; i<N; i+=2) {
464 if (map[i] == keyCode) {
465 return map[i+1];
466 }
467 }
468 }
469 return keyCode;
470 }
471
472 boolean hasEvents() {
473 synchronized (mFirst) {
474 return mFirst.next != mLast;
475 }
476 }
477
478 /*
479 * returns true if we returned an event, and false if we timed out
480 */
481 QueuedEvent getEvent(long timeoutMS) {
482 long begin = SystemClock.uptimeMillis();
483 final long end = begin+timeoutMS;
484 long now = begin;
485 synchronized (mFirst) {
486 while (mFirst.next == mLast && end > now) {
487 try {
488 mWakeLock.release();
489 mFirst.wait(end-now);
490 }
491 catch (InterruptedException e) {
492 }
493 now = SystemClock.uptimeMillis();
494 if (begin > now) {
495 begin = now;
496 }
497 }
498 if (mFirst.next == mLast) {
499 return null;
500 }
501 QueuedEvent p = mFirst.next;
502 mFirst.next = p.next;
503 mFirst.next.prev = mFirst;
504 p.inQueue = false;
505 return p;
506 }
507 }
508
509 void recycleEvent(QueuedEvent ev) {
510 synchronized (mFirst) {
511 //Log.i(TAG, "Recycle event: " + ev);
512 if (ev.event == ev.inputDevice.mAbs.currentMove) {
513 ev.inputDevice.mAbs.currentMove = null;
514 }
515 if (ev.event == ev.inputDevice.mRel.currentMove) {
516 if (false) Log.i(TAG, "Detach rel " + ev.event);
517 ev.inputDevice.mRel.currentMove = null;
518 ev.inputDevice.mRel.x = 0;
519 ev.inputDevice.mRel.y = 0;
520 }
521 recycleLocked(ev);
522 }
523 }
524
525 void filterQueue(FilterCallback cb) {
526 synchronized (mFirst) {
527 QueuedEvent cur = mLast.prev;
528 while (cur.prev != null) {
529 switch (cb.filterEvent(cur)) {
530 case FILTER_REMOVE:
531 cur.prev.next = cur.next;
532 cur.next.prev = cur.prev;
533 break;
534 case FILTER_ABORT:
535 return;
536 }
537 cur = cur.prev;
538 }
539 }
540 }
541
Michael Chan53071d62009-05-13 17:29:48 -0700542 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 int flags, int classType, Object event) {
544 QueuedEvent ev;
545 if (mCacheCount == 0) {
546 ev = new QueuedEvent();
547 } else {
548 ev = mCache;
549 ev.inQueue = false;
550 mCache = ev.next;
551 mCacheCount--;
552 }
553 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -0700554 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 ev.flags = flags;
556 ev.classType = classType;
557 ev.event = event;
558 return ev;
559 }
560
561 private void recycleLocked(QueuedEvent ev) {
562 if (ev.inQueue) {
563 throw new RuntimeException("Event already in queue!");
564 }
565 if (mCacheCount < 10) {
566 mCacheCount++;
567 ev.next = mCache;
568 mCache = ev;
569 ev.inQueue = true;
570 }
571 }
572
Michael Chan53071d62009-05-13 17:29:48 -0700573 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 int classType, Object event) {
575 boolean poke = mFirst.next == mLast;
576
Michael Chan53071d62009-05-13 17:29:48 -0700577 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -0700579 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 p = p.prev;
581 }
582
583 ev.next = p.next;
584 ev.prev = p;
585 p.next = ev;
586 ev.next.prev = ev;
587 ev.inQueue = true;
588
589 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -0700590 long time;
591 if (MEASURE_LATENCY) {
592 time = System.nanoTime();
593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 mFirst.notify();
595 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -0700596 if (MEASURE_LATENCY) {
597 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
598 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
600 }
601
602 private InputDevice newInputDevice(int deviceId) {
603 int classes = getDeviceClasses(deviceId);
604 String name = getDeviceName(deviceId);
605 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
606 + ", name=" + name
607 + ", classes=" + Integer.toHexString(classes));
608 InputDevice.AbsoluteInfo absX;
609 InputDevice.AbsoluteInfo absY;
610 InputDevice.AbsoluteInfo absPressure;
611 InputDevice.AbsoluteInfo absSize;
612 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
613 absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
614 absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
615 absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
616 absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
617 } else {
618 absX = null;
619 absY = null;
620 absPressure = null;
621 absSize = null;
622 }
623
624 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
625 }
626
627 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
628 String name) {
629 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
630 if (getAbsoluteInfo(id, channel, info)
631 && info.minValue != info.maxValue) {
632 Log.i(TAG, " " + name + ": min=" + info.minValue
633 + " max=" + info.maxValue
634 + " flat=" + info.flat
635 + " fuzz=" + info.fuzz);
636 info.range = info.maxValue-info.minValue;
637 return info;
638 }
639 Log.i(TAG, " " + name + ": unknown values");
640 return null;
641 }
642 private static native boolean readEvent(RawInputEvent outEvent);
643}