• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server;
18 
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.os.Environment;
22 import android.os.LatencyTimer;
23 import android.os.PowerManager;
24 import android.os.SystemClock;
25 import android.util.Log;
26 import android.util.SparseArray;
27 import android.util.Xml;
28 import android.view.Display;
29 import android.view.KeyEvent;
30 import android.view.MotionEvent;
31 import android.view.RawInputEvent;
32 import android.view.Surface;
33 import android.view.WindowManagerPolicy;
34 
35 import com.android.internal.util.XmlUtils;
36 
37 import org.xmlpull.v1.XmlPullParser;
38 
39 import java.io.BufferedReader;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.FileReader;
44 import java.io.IOException;
45 import java.io.InputStreamReader;
46 import java.util.ArrayList;
47 
48 public abstract class KeyInputQueue {
49     static final String TAG = "KeyInputQueue";
50 
51     static final boolean DEBUG = false;
52     static final boolean DEBUG_VIRTUAL_KEYS = false;
53     static final boolean DEBUG_POINTERS = false;
54 
55     /**
56      * Turn on some hacks we have to improve the touch interaction with a
57      * certain device whose screen currently is not all that good.
58      */
59     static boolean BAD_TOUCH_HACK = false;
60 
61     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
62 
63     final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
64     final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
65     final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
66     final HapticFeedbackCallback mHapticFeedbackCallback;
67 
68     int mGlobalMetaState = 0;
69     boolean mHaveGlobalMetaState = false;
70 
71     final QueuedEvent mFirst;
72     final QueuedEvent mLast;
73     QueuedEvent mCache;
74     int mCacheCount;
75 
76     Display mDisplay = null;
77     int mDisplayWidth;
78     int mDisplayHeight;
79 
80     int mOrientation = Surface.ROTATION_0;
81     int[] mKeyRotationMap = null;
82 
83     VirtualKey mPressedVirtualKey = null;
84 
85     PowerManager.WakeLock mWakeLock;
86 
87     static final int[] KEY_90_MAP = new int[] {
88         KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
89         KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
90         KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
91         KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
92     };
93 
94     static final int[] KEY_180_MAP = new int[] {
95         KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
96         KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
97         KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
98         KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
99     };
100 
101     static final int[] KEY_270_MAP = new int[] {
102         KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
103         KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
104         KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
105         KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
106     };
107 
108     public static final int FILTER_REMOVE = 0;
109     public static final int FILTER_KEEP = 1;
110     public static final int FILTER_ABORT = -1;
111 
112     private static final boolean MEASURE_LATENCY = false;
113     private LatencyTimer lt;
114 
115     public interface FilterCallback {
filterEvent(QueuedEvent ev)116         int filterEvent(QueuedEvent ev);
117     }
118 
119     public interface HapticFeedbackCallback {
virtualKeyFeedback(KeyEvent event)120         void virtualKeyFeedback(KeyEvent event);
121     }
122 
123     static class QueuedEvent {
124         InputDevice inputDevice;
125         long whenNano;
126         int flags; // From the raw event
127         int classType; // One of the class constants in InputEvent
128         Object event;
129         boolean inQueue;
130 
copyFrom(QueuedEvent that)131         void copyFrom(QueuedEvent that) {
132             this.inputDevice = that.inputDevice;
133             this.whenNano = that.whenNano;
134             this.flags = that.flags;
135             this.classType = that.classType;
136             this.event = that.event;
137         }
138 
139         @Override
toString()140         public String toString() {
141             return "QueuedEvent{"
142                 + Integer.toHexString(System.identityHashCode(this))
143                 + " " + event + "}";
144         }
145 
146         // not copied
147         QueuedEvent prev;
148         QueuedEvent next;
149     }
150 
151     /**
152      * A key that exists as a part of the touch-screen, outside of the normal
153      * display area of the screen.
154      */
155     static class VirtualKey {
156         int scancode;
157         int centerx;
158         int centery;
159         int width;
160         int height;
161 
162         int hitLeft;
163         int hitTop;
164         int hitRight;
165         int hitBottom;
166 
167         InputDevice lastDevice;
168         int lastKeycode;
169 
checkHit(int x, int y)170         boolean checkHit(int x, int y) {
171             return (x >= hitLeft && x <= hitRight
172                     && y >= hitTop && y <= hitBottom);
173         }
174 
computeHitRect(InputDevice dev, int dw, int dh)175         void computeHitRect(InputDevice dev, int dw, int dh) {
176             if (dev == lastDevice) {
177                 return;
178             }
179 
180             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
181                     + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
182 
183             lastDevice = dev;
184 
185             int minx = dev.absX.minValue;
186             int maxx = dev.absX.maxValue;
187 
188             int halfw = width/2;
189             int left = centerx - halfw;
190             int right = centerx + halfw;
191             hitLeft = minx + ((left*maxx-minx)/dw);
192             hitRight = minx + ((right*maxx-minx)/dw);
193 
194             int miny = dev.absY.minValue;
195             int maxy = dev.absY.maxValue;
196 
197             int halfh = height/2;
198             int top = centery - halfh;
199             int bottom = centery + halfh;
200             hitTop = miny + ((top*maxy-miny)/dh);
201             hitBottom = miny + ((bottom*maxy-miny)/dh);
202         }
203     }
204 
readVirtualKeys(String deviceName)205     private void readVirtualKeys(String deviceName) {
206         try {
207             FileInputStream fis = new FileInputStream(
208                     "/sys/board_properties/virtualkeys." + deviceName);
209             InputStreamReader isr = new InputStreamReader(fis);
210             BufferedReader br = new BufferedReader(isr, 2048);
211             String str = br.readLine();
212             if (str != null) {
213                 String[] it = str.split(":");
214                 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
215                 final int N = it.length-6;
216                 for (int i=0; i<=N; i+=6) {
217                     if (!"0x01".equals(it[i])) {
218                         Log.w(TAG, "Unknown virtual key type at elem #" + i
219                                 + ": " + it[i]);
220                         continue;
221                     }
222                     try {
223                         VirtualKey sb = new VirtualKey();
224                         sb.scancode = Integer.parseInt(it[i+1]);
225                         sb.centerx = Integer.parseInt(it[i+2]);
226                         sb.centery = Integer.parseInt(it[i+3]);
227                         sb.width = Integer.parseInt(it[i+4]);
228                         sb.height = Integer.parseInt(it[i+5]);
229                         if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
230                                 + sb.scancode + ": center=" + sb.centerx + ","
231                                 + sb.centery + " size=" + sb.width + "x"
232                                 + sb.height);
233                         mVirtualKeys.add(sb);
234                     } catch (NumberFormatException e) {
235                         Log.w(TAG, "Bad number at region " + i + " in: "
236                                 + str, e);
237                     }
238                 }
239             }
240             br.close();
241         } catch (FileNotFoundException e) {
242             Log.i(TAG, "No virtual keys found");
243         } catch (IOException e) {
244             Log.w(TAG, "Error reading virtual keys", e);
245         }
246     }
247 
readExcludedDevices()248     private void readExcludedDevices() {
249         // Read partner-provided list of excluded input devices
250         XmlPullParser parser = null;
251         // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
252         File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
253         FileReader confreader = null;
254         try {
255             confreader = new FileReader(confFile);
256             parser = Xml.newPullParser();
257             parser.setInput(confreader);
258             XmlUtils.beginDocument(parser, "devices");
259 
260             while (true) {
261                 XmlUtils.nextElement(parser);
262                 if (!"device".equals(parser.getName())) {
263                     break;
264                 }
265                 String name = parser.getAttributeValue(null, "name");
266                 if (name != null) {
267                     if (DEBUG) Log.v(TAG, "addExcludedDevice " + name);
268                     addExcludedDevice(name);
269                 }
270             }
271         } catch (FileNotFoundException e) {
272             // It's ok if the file does not exist.
273         } catch (Exception e) {
274             Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
275         } finally {
276             try { if (confreader != null) confreader.close(); } catch (IOException e) { }
277         }
278     }
279 
KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback)280     KeyInputQueue(Context context, HapticFeedbackCallback  hapticFeedbackCallback) {
281         if (MEASURE_LATENCY) {
282             lt = new LatencyTimer(100, 1000);
283         }
284 
285         BAD_TOUCH_HACK = context.getResources().getBoolean(
286                 com.android.internal.R.bool.config_filterTouchEvents);
287 
288         mHapticFeedbackCallback = hapticFeedbackCallback;
289 
290         readExcludedDevices();
291 
292         PowerManager pm = (PowerManager)context.getSystemService(
293                                                         Context.POWER_SERVICE);
294         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
295                                                         "KeyInputQueue");
296         mWakeLock.setReferenceCounted(false);
297 
298         mFirst = new QueuedEvent();
299         mLast = new QueuedEvent();
300         mFirst.next = mLast;
301         mLast.prev = mFirst;
302 
303         mThread.start();
304     }
305 
setDisplay(Display display)306     public void setDisplay(Display display) {
307         mDisplay = display;
308 
309         // We assume at this point that the display dimensions reflect the
310         // natural, unrotated display.  We will perform hit tests for soft
311         // buttons based on that display.
312         mDisplayWidth = display.getWidth();
313         mDisplayHeight = display.getHeight();
314     }
315 
getInputConfiguration(Configuration config)316     public void getInputConfiguration(Configuration config) {
317         synchronized (mFirst) {
318             config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
319             config.keyboard = Configuration.KEYBOARD_NOKEYS;
320             config.navigation = Configuration.NAVIGATION_NONAV;
321 
322             final int N = mDevices.size();
323             for (int i=0; i<N; i++) {
324                 InputDevice d = mDevices.valueAt(i);
325                 if (d != null) {
326                     if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
327                         config.touchscreen
328                                 = Configuration.TOUCHSCREEN_FINGER;
329                         //Log.i("foo", "***** HAVE TOUCHSCREEN!");
330                     }
331                     if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
332                         config.keyboard
333                                 = Configuration.KEYBOARD_QWERTY;
334                         //Log.i("foo", "***** HAVE QWERTY!");
335                     }
336                     if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
337                         config.navigation
338                                 = Configuration.NAVIGATION_TRACKBALL;
339                         //Log.i("foo", "***** HAVE TRACKBALL!");
340                     } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
341                         config.navigation
342                                 = Configuration.NAVIGATION_DPAD;
343                         //Log.i("foo", "***** HAVE DPAD!");
344                     }
345                 }
346             }
347         }
348     }
349 
getScancodeState(int code)350     public int getScancodeState(int code) {
351         synchronized (mFirst) {
352             VirtualKey vk = mPressedVirtualKey;
353             if (vk != null) {
354                 if (vk.scancode == code) {
355                     return 2;
356                 }
357             }
358             return nativeGetScancodeState(code);
359         }
360     }
361 
getScancodeState(int deviceId, int code)362     public int getScancodeState(int deviceId, int code) {
363         synchronized (mFirst) {
364             VirtualKey vk = mPressedVirtualKey;
365             if (vk != null) {
366                 if (vk.scancode == code) {
367                     return 2;
368                 }
369             }
370             return nativeGetScancodeState(deviceId, code);
371         }
372     }
373 
getKeycodeState(int code)374     public int getKeycodeState(int code) {
375         synchronized (mFirst) {
376             VirtualKey vk = mPressedVirtualKey;
377             if (vk != null) {
378                 if (vk.lastKeycode == code) {
379                     return 2;
380                 }
381             }
382             return nativeGetKeycodeState(code);
383         }
384     }
385 
getKeycodeState(int deviceId, int code)386     public int getKeycodeState(int deviceId, int code) {
387         synchronized (mFirst) {
388             VirtualKey vk = mPressedVirtualKey;
389             if (vk != null) {
390                 if (vk.lastKeycode == code) {
391                     return 2;
392                 }
393             }
394             return nativeGetKeycodeState(deviceId, code);
395         }
396     }
397 
getDeviceName(int deviceId)398     public static native String getDeviceName(int deviceId);
getDeviceClasses(int deviceId)399     public static native int getDeviceClasses(int deviceId);
addExcludedDevice(String deviceName)400     public static native void addExcludedDevice(String deviceName);
getAbsoluteInfo(int deviceId, int axis, InputDevice.AbsoluteInfo outInfo)401     public static native boolean getAbsoluteInfo(int deviceId, int axis,
402             InputDevice.AbsoluteInfo outInfo);
getSwitchState(int sw)403     public static native int getSwitchState(int sw);
getSwitchState(int deviceId, int sw)404     public static native int getSwitchState(int deviceId, int sw);
nativeGetScancodeState(int code)405     public static native int nativeGetScancodeState(int code);
nativeGetScancodeState(int deviceId, int code)406     public static native int nativeGetScancodeState(int deviceId, int code);
nativeGetKeycodeState(int code)407     public static native int nativeGetKeycodeState(int code);
nativeGetKeycodeState(int deviceId, int code)408     public static native int nativeGetKeycodeState(int deviceId, int code);
scancodeToKeycode(int deviceId, int scancode)409     public static native int scancodeToKeycode(int deviceId, int scancode);
hasKeys(int[] keycodes, boolean[] keyExists)410     public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
411 
newKeyEvent(InputDevice device, long downTime, long eventTime, boolean down, int keycode, int repeatCount, int scancode, int flags)412     public static KeyEvent newKeyEvent(InputDevice device, long downTime,
413             long eventTime, boolean down, int keycode, int repeatCount,
414             int scancode, int flags) {
415         return new KeyEvent(
416                 downTime, eventTime,
417                 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
418                 keycode, repeatCount,
419                 device != null ? device.mMetaKeysState : 0,
420                 device != null ? device.id : -1, scancode,
421                 flags | KeyEvent.FLAG_FROM_SYSTEM);
422     }
423 
424     Thread mThread = new Thread("InputDeviceReader") {
425         public void run() {
426             if (DEBUG) Log.v(TAG, "InputDeviceReader.run()");
427             android.os.Process.setThreadPriority(
428                     android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
429 
430             RawInputEvent ev = new RawInputEvent();
431             while (true) {
432                 try {
433                     InputDevice di;
434 
435                     // block, doesn't release the monitor
436                     readEvent(ev);
437 
438                     boolean send = false;
439                     boolean configChanged = false;
440 
441                     if (false) {
442                         Log.i(TAG, "Input event: dev=0x"
443                                 + Integer.toHexString(ev.deviceId)
444                                 + " type=0x" + Integer.toHexString(ev.type)
445                                 + " scancode=" + ev.scancode
446                                 + " keycode=" + ev.keycode
447                                 + " value=" + ev.value);
448                     }
449 
450                     if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
451                         synchronized (mFirst) {
452                             di = newInputDevice(ev.deviceId);
453                             if (di.classes != 0) {
454                                 // If this device is some kind of input class,
455                                 // we care about it.
456                                 mDevices.put(ev.deviceId, di);
457                                 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
458                                     readVirtualKeys(di.name);
459                                 }
460                                 // The configuration may have changed because
461                                 // of this device.
462                                 configChanged = true;
463                             } else {
464                                 // We won't do anything with this device.
465                                 mIgnoredDevices.put(ev.deviceId, di);
466                                 Log.i(TAG, "Ignoring non-input device: id=0x"
467                                         + Integer.toHexString(di.id)
468                                         + ", name=" + di.name);
469                             }
470                         }
471                     } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
472                         synchronized (mFirst) {
473                             if (false) {
474                                 Log.i(TAG, "Device removed: id=0x"
475                                         + Integer.toHexString(ev.deviceId));
476                             }
477                             di = mDevices.get(ev.deviceId);
478                             if (di != null) {
479                                 mDevices.delete(ev.deviceId);
480                                 // The configuration may have changed because
481                                 // of this device.
482                                 configChanged = true;
483                             } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
484                                 mIgnoredDevices.remove(ev.deviceId);
485                             } else {
486                                 Log.w(TAG, "Removing bad device id: "
487                                         + Integer.toHexString(ev.deviceId));
488                                 continue;
489                             }
490                         }
491                     } else {
492                         di = getInputDevice(ev.deviceId);
493                         if (di == null) {
494                             // This may be some junk from an ignored device.
495                             continue;
496                         }
497 
498                         // first crack at it
499                         send = preprocessEvent(di, ev);
500 
501                         if (ev.type == RawInputEvent.EV_KEY) {
502                             di.mMetaKeysState = makeMetaState(ev.keycode,
503                                     ev.value != 0, di.mMetaKeysState);
504                             mHaveGlobalMetaState = false;
505                         }
506                     }
507 
508                     if (configChanged) {
509                         synchronized (mFirst) {
510                             addLocked(di, System.nanoTime(), 0,
511                                     RawInputEvent.CLASS_CONFIGURATION_CHANGED,
512                                     null);
513                         }
514                     }
515 
516                     if (!send) {
517                         continue;
518                     }
519 
520                     synchronized (mFirst) {
521                         // NOTE: The event timebase absolutely must be the same
522                         // timebase as SystemClock.uptimeMillis().
523                         //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
524                         final long curTime = SystemClock.uptimeMillis();
525                         final long curTimeNano = System.nanoTime();
526                         //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
527 
528                         final int classes = di.classes;
529                         final int type = ev.type;
530                         final int scancode = ev.scancode;
531                         send = false;
532 
533                         // Is it a key event?
534                         if (type == RawInputEvent.EV_KEY &&
535                                 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
536                                 (scancode < RawInputEvent.BTN_FIRST ||
537                                         scancode > RawInputEvent.BTN_LAST)) {
538                             boolean down;
539                             if (ev.value != 0) {
540                                 down = true;
541                                 di.mKeyDownTime = curTime;
542                             } else {
543                                 down = false;
544                             }
545                             int keycode = rotateKeyCodeLocked(ev.keycode);
546                             addLocked(di, curTimeNano, ev.flags,
547                                     RawInputEvent.CLASS_KEYBOARD,
548                                     newKeyEvent(di, di.mKeyDownTime, curTime, down,
549                                             keycode, 0, scancode,
550                                             ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
551                                              ? KeyEvent.FLAG_WOKE_HERE : 0));
552 
553                         } else if (ev.type == RawInputEvent.EV_KEY) {
554                             // Single touch protocol: touch going down or up.
555                             if (ev.scancode == RawInputEvent.BTN_TOUCH &&
556                                     (classes&(RawInputEvent.CLASS_TOUCHSCREEN
557                                             |RawInputEvent.CLASS_TOUCHSCREEN_MT))
558                                             == RawInputEvent.CLASS_TOUCHSCREEN) {
559                                 di.mAbs.changed = true;
560                                 di.mAbs.mDown[0] = ev.value != 0;
561 
562                             // Trackball (mouse) protocol: press down or up.
563                             } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
564                                     (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
565                                 di.mRel.changed = true;
566                                 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
567                                 send = true;
568                             }
569 
570                         // Process position events from multitouch protocol.
571                         } else if (ev.type == RawInputEvent.EV_ABS &&
572                                 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
573                             if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
574                                 di.mAbs.changed = true;
575                                 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
576                                         + MotionEvent.SAMPLE_PRESSURE] = ev.value;
577                             } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
578                                 di.mAbs.changed = true;
579                                 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
580                                     + MotionEvent.SAMPLE_X] = ev.value;
581                                 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
582                                         + di.mAbs.mAddingPointerOffset
583                                         + " X:" + ev.value);
584                             } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
585                                 di.mAbs.changed = true;
586                                 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
587                                     + MotionEvent.SAMPLE_Y] = ev.value;
588                                 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
589                                         + di.mAbs.mAddingPointerOffset
590                                         + " Y:" + ev.value);
591                             } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
592                                 di.mAbs.changed = true;
593                                 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
594                                     + MotionEvent.SAMPLE_SIZE] = ev.value;
595                             }
596 
597                         // Process position events from single touch protocol.
598                         } else if (ev.type == RawInputEvent.EV_ABS &&
599                                 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
600                             if (ev.scancode == RawInputEvent.ABS_X) {
601                                 di.mAbs.changed = true;
602                                 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
603                             } else if (ev.scancode == RawInputEvent.ABS_Y) {
604                                 di.mAbs.changed = true;
605                                 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
606                             } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
607                                 di.mAbs.changed = true;
608                                 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
609                                 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
610                                                  + MotionEvent.SAMPLE_PRESSURE] = ev.value;
611                             } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
612                                 di.mAbs.changed = true;
613                                 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
614                                 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
615                                                  + MotionEvent.SAMPLE_SIZE] = ev.value;
616                             }
617 
618                         // Process movement events from trackball (mouse) protocol.
619                         } else if (ev.type == RawInputEvent.EV_REL &&
620                                 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
621                             // Add this relative movement into our totals.
622                             if (ev.scancode == RawInputEvent.REL_X) {
623                                 di.mRel.changed = true;
624                                 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
625                             } else if (ev.scancode == RawInputEvent.REL_Y) {
626                                 di.mRel.changed = true;
627                                 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
628                             }
629                         }
630 
631                         // Handle multitouch protocol sync: tells us that the
632                         // driver has returned all data for -one- of the pointers
633                         // that is currently down.
634                         if (ev.type == RawInputEvent.EV_SYN
635                                 && ev.scancode == RawInputEvent.SYN_MT_REPORT
636                                 && di.mAbs != null) {
637                             di.mAbs.changed = true;
638                             if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
639                                 // If the value is <= 0, the pointer is not
640                                 // down, so keep it in the count.
641 
642                                 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
643                                                       + MotionEvent.SAMPLE_PRESSURE] != 0) {
644                                     final int num = di.mAbs.mNextNumPointers+1;
645                                     di.mAbs.mNextNumPointers = num;
646                                     if (DEBUG_POINTERS) Log.v(TAG,
647                                             "MT_REPORT: now have " + num + " pointers");
648                                     final int newOffset = (num <= InputDevice.MAX_POINTERS)
649                                             ? (num * MotionEvent.NUM_SAMPLE_DATA)
650                                             : (InputDevice.MAX_POINTERS *
651                                                     MotionEvent.NUM_SAMPLE_DATA);
652                                     di.mAbs.mAddingPointerOffset = newOffset;
653                                     di.mAbs.mNextData[newOffset
654                                             + MotionEvent.SAMPLE_PRESSURE] = 0;
655                                 } else {
656                                     if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer");
657                                 }
658                             }
659 
660                         // Handle general event sync: all data for the current
661                         // event update has been delivered.
662                         } else if (send || (ev.type == RawInputEvent.EV_SYN
663                                 && ev.scancode == RawInputEvent.SYN_REPORT)) {
664                             if (mDisplay != null) {
665                                 if (!mHaveGlobalMetaState) {
666                                     computeGlobalMetaStateLocked();
667                                 }
668 
669                                 MotionEvent me;
670 
671                                 InputDevice.MotionState ms = di.mAbs;
672                                 if (ms.changed) {
673                                     ms.changed = false;
674 
675                                     if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
676                                             |RawInputEvent.CLASS_TOUCHSCREEN_MT))
677                                             == RawInputEvent.CLASS_TOUCHSCREEN) {
678                                         ms.mNextNumPointers = 0;
679                                         if (ms.mDown[0]) {
680                                             System.arraycopy(di.curTouchVals, 0,
681                                                     ms.mNextData, 0,
682                                                     MotionEvent.NUM_SAMPLE_DATA);
683                                             ms.mNextNumPointers++;
684                                         }
685                                     }
686 
687                                     if (BAD_TOUCH_HACK) {
688                                         ms.dropBadPoint(di);
689                                     }
690 
691                                     boolean doMotion = !monitorVirtualKey(di,
692                                             ev, curTime, curTimeNano);
693 
694                                     if (doMotion && ms.mNextNumPointers > 0
695                                             && (ms.mLastNumPointers == 0
696                                                     || ms.mSkipLastPointers)) {
697                                         doMotion = !generateVirtualKeyDown(di,
698                                                 ev, curTime, curTimeNano);
699                                     }
700 
701                                     if (doMotion) {
702                                         // XXX Need to be able to generate
703                                         // multiple events here, for example
704                                         // if two fingers change up/down state
705                                         // at the same time.
706                                         do {
707                                             me = ms.generateAbsMotion(di, curTime,
708                                                     curTimeNano, mDisplay,
709                                                     mOrientation, mGlobalMetaState);
710                                             if (DEBUG_POINTERS) Log.v(TAG, "Absolute: x="
711                                                     + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
712                                                     + " y="
713                                                     + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
714                                                     + " ev=" + me);
715                                             if (me != null) {
716                                                 if (WindowManagerPolicy.WATCH_POINTER) {
717                                                     Log.i(TAG, "Enqueueing: " + me);
718                                                 }
719                                                 addLocked(di, curTimeNano, ev.flags,
720                                                         RawInputEvent.CLASS_TOUCHSCREEN, me);
721                                             }
722                                         } while (ms.hasMore());
723                                     } else {
724                                         // We are consuming movement in the
725                                         // virtual key area...  but still
726                                         // propagate this to the previous
727                                         // data for comparisons.
728                                         int num = ms.mNextNumPointers;
729                                         if (num > InputDevice.MAX_POINTERS) {
730                                             num = InputDevice.MAX_POINTERS;
731                                         }
732                                         System.arraycopy(ms.mNextData, 0,
733                                                 ms.mLastData, 0,
734                                                 num * MotionEvent.NUM_SAMPLE_DATA);
735                                         ms.mLastNumPointers = num;
736                                         ms.mSkipLastPointers = true;
737                                     }
738 
739                                     ms.finish();
740                                 }
741 
742                                 ms = di.mRel;
743                                 if (ms.changed) {
744                                     ms.changed = false;
745 
746                                     me = ms.generateRelMotion(di, curTime,
747                                             curTimeNano,
748                                             mOrientation, mGlobalMetaState);
749                                     if (false) Log.v(TAG, "Relative: x="
750                                             + di.mRel.mNextData[MotionEvent.SAMPLE_X]
751                                             + " y="
752                                             + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
753                                             + " ev=" + me);
754                                     if (me != null) {
755                                         addLocked(di, curTimeNano, ev.flags,
756                                                 RawInputEvent.CLASS_TRACKBALL, me);
757                                     }
758 
759                                     ms.finish();
760                                 }
761                             }
762                         }
763                     }
764 
765                 } catch (RuntimeException exc) {
766                     Log.e(TAG, "InputReaderThread uncaught exception", exc);
767                 }
768             }
769         }
770     };
771 
isInsideDisplay(InputDevice dev)772     private boolean isInsideDisplay(InputDevice dev) {
773         final InputDevice.AbsoluteInfo absx = dev.absX;
774         final InputDevice.AbsoluteInfo absy = dev.absY;
775         final InputDevice.MotionState absm = dev.mAbs;
776         if (absx == null || absy == null || absm == null) {
777             return true;
778         }
779 
780         if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
781                 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
782                 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
783                 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
784             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
785                     + absm.mNextData[MotionEvent.SAMPLE_X]
786                     + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
787                     + ") inside of display");
788             return true;
789         }
790 
791         return false;
792     }
793 
findVirtualKey(InputDevice dev)794     private VirtualKey findVirtualKey(InputDevice dev) {
795         final int N = mVirtualKeys.size();
796         if (N <= 0) {
797             return null;
798         }
799 
800         final InputDevice.MotionState absm = dev.mAbs;
801         for (int i=0; i<N; i++) {
802             VirtualKey sb = mVirtualKeys.get(i);
803             sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
804             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
805                     + absm.mNextData[MotionEvent.SAMPLE_X] + ","
806                     + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
807                     + sb.scancode + " - (" + sb.hitLeft
808                     + "," + sb.hitTop + ")-(" + sb.hitRight + ","
809                     + sb.hitBottom + ")");
810             if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
811                     absm.mNextData[MotionEvent.SAMPLE_Y])) {
812                 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
813                 return sb;
814             }
815         }
816 
817         return null;
818     }
819 
generateVirtualKeyDown(InputDevice di, RawInputEvent ev, long curTime, long curTimeNano)820     private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
821             long curTime, long curTimeNano) {
822         if (isInsideDisplay(di)) {
823             // Didn't consume event.
824             return false;
825         }
826 
827 
828         VirtualKey vk = findVirtualKey(di);
829         if (vk != null) {
830             final InputDevice.MotionState ms = di.mAbs;
831             mPressedVirtualKey = vk;
832             vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
833             ms.mLastNumPointers = ms.mNextNumPointers;
834             di.mKeyDownTime = curTime;
835             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
836                     "Generate key down for: " + vk.scancode
837                     + " (keycode=" + vk.lastKeycode + ")");
838             KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
839                     vk.lastKeycode, 0, vk.scancode,
840                     KeyEvent.FLAG_VIRTUAL_HARD_KEY);
841             mHapticFeedbackCallback.virtualKeyFeedback(event);
842             addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
843                     event);
844         }
845 
846         // We always consume the event, even if we didn't
847         // generate a key event.  There are two reasons for
848         // this: to avoid spurious touches when holding
849         // the edges of the device near the touchscreen,
850         // and to avoid reporting events if there are virtual
851         // keys on the touchscreen outside of the display
852         // area.
853         // Note that for all of this we are only looking at the
854         // first pointer, since what we are handling here is the
855         // first pointer going down, and this is the coordinate
856         // that will be used to dispatch the event.
857         if (false) {
858             final InputDevice.AbsoluteInfo absx = di.absX;
859             final InputDevice.AbsoluteInfo absy = di.absY;
860             final InputDevice.MotionState absm = di.mAbs;
861             Log.v(TAG, "Rejecting ("
862                 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
863                 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
864                 + absx.minValue + "," + absy.minValue
865                 + ")-(" + absx.maxValue + ","
866                 + absx.maxValue + ")");
867         }
868         return true;
869     }
870 
monitorVirtualKey(InputDevice di, RawInputEvent ev, long curTime, long curTimeNano)871     private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
872             long curTime, long curTimeNano) {
873         VirtualKey vk = mPressedVirtualKey;
874         if (vk == null) {
875             return false;
876         }
877 
878         final InputDevice.MotionState ms = di.mAbs;
879         if (ms.mNextNumPointers <= 0) {
880             mPressedVirtualKey = null;
881             ms.mLastNumPointers = 0;
882             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode);
883             KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
884                     vk.lastKeycode, 0, vk.scancode,
885                     KeyEvent.FLAG_VIRTUAL_HARD_KEY);
886             mHapticFeedbackCallback.virtualKeyFeedback(event);
887             addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
888                     event);
889             return true;
890 
891         } else if (isInsideDisplay(di)) {
892             // Whoops the pointer has moved into
893             // the display area!  Cancel the
894             // virtual key and start a pointer
895             // motion.
896             mPressedVirtualKey = null;
897             if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode);
898             KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
899                     vk.lastKeycode, 0, vk.scancode,
900                     KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
901             mHapticFeedbackCallback.virtualKeyFeedback(event);
902             addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
903                     event);
904             ms.mLastNumPointers = 0;
905             return false;
906         }
907 
908         return true;
909     }
910 
911     /**
912      * Returns a new meta state for the given keys and old state.
913      */
makeMetaState(int keycode, boolean down, int old)914     private static final int makeMetaState(int keycode, boolean down, int old) {
915         int mask;
916         switch (keycode) {
917         case KeyEvent.KEYCODE_ALT_LEFT:
918             mask = KeyEvent.META_ALT_LEFT_ON;
919             break;
920         case KeyEvent.KEYCODE_ALT_RIGHT:
921             mask = KeyEvent.META_ALT_RIGHT_ON;
922             break;
923         case KeyEvent.KEYCODE_SHIFT_LEFT:
924             mask = KeyEvent.META_SHIFT_LEFT_ON;
925             break;
926         case KeyEvent.KEYCODE_SHIFT_RIGHT:
927             mask = KeyEvent.META_SHIFT_RIGHT_ON;
928             break;
929         case KeyEvent.KEYCODE_SYM:
930             mask = KeyEvent.META_SYM_ON;
931             break;
932         default:
933             return old;
934         }
935         int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
936                     & (down ? (old | mask) : (old & ~mask));
937         if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
938             result |= KeyEvent.META_ALT_ON;
939         }
940         if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
941             result |= KeyEvent.META_SHIFT_ON;
942         }
943         return result;
944     }
945 
computeGlobalMetaStateLocked()946     private void computeGlobalMetaStateLocked() {
947         int i = mDevices.size();
948         mGlobalMetaState = 0;
949         while ((--i) >= 0) {
950             mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
951         }
952         mHaveGlobalMetaState = true;
953     }
954 
955     /*
956      * Return true if you want the event to get passed on to the
957      * rest of the system, and false if you've handled it and want
958      * it dropped.
959      */
preprocessEvent(InputDevice device, RawInputEvent event)960     abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
961 
getInputDevice(int deviceId)962     InputDevice getInputDevice(int deviceId) {
963         synchronized (mFirst) {
964             return getInputDeviceLocked(deviceId);
965         }
966     }
967 
getInputDeviceLocked(int deviceId)968     private InputDevice getInputDeviceLocked(int deviceId) {
969         return mDevices.get(deviceId);
970     }
971 
setOrientation(int orientation)972     public void setOrientation(int orientation) {
973         synchronized(mFirst) {
974             mOrientation = orientation;
975             switch (orientation) {
976                 case Surface.ROTATION_90:
977                     mKeyRotationMap = KEY_90_MAP;
978                     break;
979                 case Surface.ROTATION_180:
980                     mKeyRotationMap = KEY_180_MAP;
981                     break;
982                 case Surface.ROTATION_270:
983                     mKeyRotationMap = KEY_270_MAP;
984                     break;
985                 default:
986                     mKeyRotationMap = null;
987                     break;
988             }
989         }
990     }
991 
rotateKeyCode(int keyCode)992     public int rotateKeyCode(int keyCode) {
993         synchronized(mFirst) {
994             return rotateKeyCodeLocked(keyCode);
995         }
996     }
997 
rotateKeyCodeLocked(int keyCode)998     private int rotateKeyCodeLocked(int keyCode) {
999         int[] map = mKeyRotationMap;
1000         if (map != null) {
1001             final int N = map.length;
1002             for (int i=0; i<N; i+=2) {
1003                 if (map[i] == keyCode) {
1004                     return map[i+1];
1005                 }
1006             }
1007         }
1008         return keyCode;
1009     }
1010 
hasEvents()1011     boolean hasEvents() {
1012         synchronized (mFirst) {
1013             return mFirst.next != mLast;
1014         }
1015     }
1016 
1017     /*
1018      * returns true if we returned an event, and false if we timed out
1019      */
getEvent(long timeoutMS)1020     QueuedEvent getEvent(long timeoutMS) {
1021         long begin = SystemClock.uptimeMillis();
1022         final long end = begin+timeoutMS;
1023         long now = begin;
1024         synchronized (mFirst) {
1025             while (mFirst.next == mLast && end > now) {
1026                 try {
1027                     mWakeLock.release();
1028                     mFirst.wait(end-now);
1029                 }
1030                 catch (InterruptedException e) {
1031                 }
1032                 now = SystemClock.uptimeMillis();
1033                 if (begin > now) {
1034                     begin = now;
1035                 }
1036             }
1037             if (mFirst.next == mLast) {
1038                 return null;
1039             }
1040             QueuedEvent p = mFirst.next;
1041             mFirst.next = p.next;
1042             mFirst.next.prev = mFirst;
1043             p.inQueue = false;
1044             return p;
1045         }
1046     }
1047 
1048     /**
1049      * Return true if the queue has an up event pending that corresponds
1050      * to the same key as the given key event.
1051      */
hasKeyUpEvent(KeyEvent origEvent)1052     boolean hasKeyUpEvent(KeyEvent origEvent) {
1053         synchronized (mFirst) {
1054             final int keyCode = origEvent.getKeyCode();
1055             QueuedEvent cur = mLast.prev;
1056             while (cur.prev != null) {
1057                 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1058                     KeyEvent ke = (KeyEvent)cur.event;
1059                     if (ke.getAction() == KeyEvent.ACTION_UP
1060                             && ke.getKeyCode() == keyCode) {
1061                         return true;
1062                     }
1063                 }
1064                 cur = cur.prev;
1065             }
1066         }
1067 
1068         return false;
1069     }
1070 
recycleEvent(QueuedEvent ev)1071     void recycleEvent(QueuedEvent ev) {
1072         synchronized (mFirst) {
1073             //Log.i(TAG, "Recycle event: " + ev);
1074             if (ev.event == ev.inputDevice.mAbs.currentMove) {
1075                 ev.inputDevice.mAbs.currentMove = null;
1076             }
1077             if (ev.event == ev.inputDevice.mRel.currentMove) {
1078                 if (false) Log.i(TAG, "Detach rel " + ev.event);
1079                 ev.inputDevice.mRel.currentMove = null;
1080                 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1081                 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
1082             }
1083             recycleLocked(ev);
1084         }
1085     }
1086 
filterQueue(FilterCallback cb)1087     void filterQueue(FilterCallback cb) {
1088         synchronized (mFirst) {
1089             QueuedEvent cur = mLast.prev;
1090             while (cur.prev != null) {
1091                 switch (cb.filterEvent(cur)) {
1092                     case FILTER_REMOVE:
1093                         cur.prev.next = cur.next;
1094                         cur.next.prev = cur.prev;
1095                         break;
1096                     case FILTER_ABORT:
1097                         return;
1098                 }
1099                 cur = cur.prev;
1100             }
1101         }
1102     }
1103 
obtainLocked(InputDevice device, long whenNano, int flags, int classType, Object event)1104     private QueuedEvent obtainLocked(InputDevice device, long whenNano,
1105             int flags, int classType, Object event) {
1106         QueuedEvent ev;
1107         if (mCacheCount == 0) {
1108             ev = new QueuedEvent();
1109         } else {
1110             ev = mCache;
1111             ev.inQueue = false;
1112             mCache = ev.next;
1113             mCacheCount--;
1114         }
1115         ev.inputDevice = device;
1116         ev.whenNano = whenNano;
1117         ev.flags = flags;
1118         ev.classType = classType;
1119         ev.event = event;
1120         return ev;
1121     }
1122 
recycleLocked(QueuedEvent ev)1123     private void recycleLocked(QueuedEvent ev) {
1124         if (ev.inQueue) {
1125             throw new RuntimeException("Event already in queue!");
1126         }
1127         if (mCacheCount < 10) {
1128             mCacheCount++;
1129             ev.next = mCache;
1130             mCache = ev;
1131             ev.inQueue = true;
1132         }
1133     }
1134 
addLocked(InputDevice device, long whenNano, int flags, int classType, Object event)1135     private void addLocked(InputDevice device, long whenNano, int flags,
1136             int classType, Object event) {
1137         boolean poke = mFirst.next == mLast;
1138 
1139         QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
1140         QueuedEvent p = mLast.prev;
1141         while (p != mFirst && ev.whenNano < p.whenNano) {
1142             p = p.prev;
1143         }
1144 
1145         ev.next = p.next;
1146         ev.prev = p;
1147         p.next = ev;
1148         ev.next.prev = ev;
1149         ev.inQueue = true;
1150 
1151         if (poke) {
1152             long time;
1153             if (MEASURE_LATENCY) {
1154                 time = System.nanoTime();
1155             }
1156             mFirst.notify();
1157             mWakeLock.acquire();
1158             if (MEASURE_LATENCY) {
1159                 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1160             }
1161         }
1162     }
1163 
newInputDevice(int deviceId)1164     private InputDevice newInputDevice(int deviceId) {
1165         int classes = getDeviceClasses(deviceId);
1166         String name = getDeviceName(deviceId);
1167         InputDevice.AbsoluteInfo absX = null;
1168         InputDevice.AbsoluteInfo absY = null;
1169         InputDevice.AbsoluteInfo absPressure = null;
1170         InputDevice.AbsoluteInfo absSize = null;
1171         if (classes != 0) {
1172             Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
1173                     + ", name=" + name
1174                     + ", classes=" + Integer.toHexString(classes));
1175             if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1176                 absX = loadAbsoluteInfo(deviceId,
1177                         RawInputEvent.ABS_MT_POSITION_X, "X");
1178                 absY = loadAbsoluteInfo(deviceId,
1179                         RawInputEvent.ABS_MT_POSITION_Y, "Y");
1180                 absPressure = loadAbsoluteInfo(deviceId,
1181                         RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1182                 absSize = loadAbsoluteInfo(deviceId,
1183                         RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1184             } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1185                 absX = loadAbsoluteInfo(deviceId,
1186                         RawInputEvent.ABS_X, "X");
1187                 absY = loadAbsoluteInfo(deviceId,
1188                         RawInputEvent.ABS_Y, "Y");
1189                 absPressure = loadAbsoluteInfo(deviceId,
1190                         RawInputEvent.ABS_PRESSURE, "Pressure");
1191                 absSize = loadAbsoluteInfo(deviceId,
1192                         RawInputEvent.ABS_TOOL_WIDTH, "Size");
1193             }
1194         }
1195 
1196         return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1197     }
1198 
loadAbsoluteInfo(int id, int channel, String name)1199     private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1200             String name) {
1201         InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1202         if (getAbsoluteInfo(id, channel, info)
1203                 && info.minValue != info.maxValue) {
1204             Log.i(TAG, "  " + name + ": min=" + info.minValue
1205                     + " max=" + info.maxValue
1206                     + " flat=" + info.flat
1207                     + " fuzz=" + info.fuzz);
1208             info.range = info.maxValue-info.minValue;
1209             return info;
1210         }
1211         Log.i(TAG, "  " + name + ": unknown values");
1212         return null;
1213     }
readEvent(RawInputEvent outEvent)1214     private static native boolean readEvent(RawInputEvent outEvent);
1215 }
1216