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