auto import from //depot/cupcake/@135843
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
new file mode 100644
index 0000000..63b486c
--- /dev/null
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.SystemClock;
+import android.os.PowerManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RawInputEvent;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+
+public abstract class KeyInputQueue {
+    static final String TAG = "KeyInputQueue";
+
+    SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+    
+    int mGlobalMetaState = 0;
+    boolean mHaveGlobalMetaState = false;
+    
+    final QueuedEvent mFirst;
+    final QueuedEvent mLast;
+    QueuedEvent mCache;
+    int mCacheCount;
+
+    Display mDisplay = null;
+    
+    int mOrientation = Surface.ROTATION_0;
+    int[] mKeyRotationMap = null;
+    
+    PowerManager.WakeLock mWakeLock;
+
+    static final int[] KEY_90_MAP = new int[] {
+        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
+        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
+        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
+        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
+    };
+    
+    static final int[] KEY_180_MAP = new int[] {
+        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
+        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
+        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
+        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
+    };
+    
+    static final int[] KEY_270_MAP = new int[] {
+        KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
+        KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
+        KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
+        KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
+    };
+    
+    public static final int FILTER_REMOVE = 0;
+    public static final int FILTER_KEEP = 1;
+    public static final int FILTER_ABORT = -1;
+    
+    public interface FilterCallback {
+        int filterEvent(QueuedEvent ev);
+    }
+    
+    static class QueuedEvent {
+        InputDevice inputDevice;
+        long when;
+        int flags; // From the raw event
+        int classType; // One of the class constants in InputEvent
+        Object event;
+        boolean inQueue;
+
+        void copyFrom(QueuedEvent that) {
+            this.inputDevice = that.inputDevice;
+            this.when = that.when;
+            this.flags = that.flags;
+            this.classType = that.classType;
+            this.event = that.event;
+        }
+
+        @Override
+        public String toString() {
+            return "QueuedEvent{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + event + "}";
+        }
+        
+        // not copied
+        QueuedEvent prev;
+        QueuedEvent next;
+    }
+
+    KeyInputQueue(Context context) {
+        PowerManager pm = (PowerManager)context.getSystemService(
+                                                        Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                                                        "KeyInputQueue");
+        mWakeLock.setReferenceCounted(false);
+
+        mFirst = new QueuedEvent();
+        mLast = new QueuedEvent();
+        mFirst.next = mLast;
+        mLast.prev = mFirst;
+
+        mThread.start();
+    }
+
+    public void setDisplay(Display display) {
+        mDisplay = display;
+    }
+    
+    public void getInputConfiguration(Configuration config) {
+        synchronized (mFirst) {
+            config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+            config.keyboard = Configuration.KEYBOARD_NOKEYS;
+            config.navigation = Configuration.NAVIGATION_NONAV;
+            
+            final int N = mDevices.size();
+            for (int i=0; i<N; i++) {
+                InputDevice d = mDevices.valueAt(i);
+                if (d != null) {
+                    if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+                        config.touchscreen
+                                = Configuration.TOUCHSCREEN_FINGER;
+                        //Log.i("foo", "***** HAVE TOUCHSCREEN!");
+                    }
+                    if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
+                        config.keyboard
+                                = Configuration.KEYBOARD_QWERTY;
+                        //Log.i("foo", "***** HAVE QWERTY!");
+                    }
+                    if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
+                        config.navigation
+                                = Configuration.NAVIGATION_TRACKBALL;
+                        //Log.i("foo", "***** HAVE TRACKBALL!");
+                    }
+                }
+            }
+        }
+    }
+    
+    public static native String getDeviceName(int deviceId);
+    public static native int getDeviceClasses(int deviceId);
+    public static native boolean getAbsoluteInfo(int deviceId, int axis,
+            InputDevice.AbsoluteInfo outInfo);
+    public static native int getSwitchState(int sw);
+    public static native int getSwitchState(int deviceId, int sw);
+    public static native int getScancodeState(int sw);
+    public static native int getScancodeState(int deviceId, int sw);
+    public static native int getKeycodeState(int sw);
+    public static native int getKeycodeState(int deviceId, int sw);
+    public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
+    
+    public static KeyEvent newKeyEvent(InputDevice device, long downTime,
+            long eventTime, boolean down, int keycode, int repeatCount,
+            int scancode, int flags) {
+        return new KeyEvent(
+                downTime, eventTime,
+                down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
+                keycode, repeatCount,
+                device != null ? device.mMetaKeysState : 0,
+                device != null ? device.id : -1, scancode,
+                flags);
+    }
+    
+    Thread mThread = new Thread("InputDeviceReader") {
+        public void run() {
+            android.os.Process.setThreadPriority(
+                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
+            
+            try {
+                RawInputEvent ev = new RawInputEvent();
+                while (true) {
+                    InputDevice di;
+
+                    // block, doesn't release the monitor
+                    readEvent(ev);
+
+                    boolean send = false;
+                    boolean configChanged = false;
+                    
+                    if (false) {
+                        Log.i(TAG, "Input event: dev=0x"
+                                + Integer.toHexString(ev.deviceId)
+                                + " type=0x" + Integer.toHexString(ev.type)
+                                + " scancode=" + ev.scancode
+                                + " keycode=" + ev.keycode
+                                + " value=" + ev.value);
+                    }
+                    
+                    if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
+                        synchronized (mFirst) {
+                            di = newInputDevice(ev.deviceId);
+                            mDevices.put(ev.deviceId, di);
+                            configChanged = true;
+                        }
+                    } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
+                        synchronized (mFirst) {
+                            Log.i(TAG, "Device removed: id=0x"
+                                    + Integer.toHexString(ev.deviceId));
+                            di = mDevices.get(ev.deviceId);
+                            if (di != null) {
+                                mDevices.delete(ev.deviceId);
+                                configChanged = true;
+                            } else {
+                                Log.w(TAG, "Bad device id: " + ev.deviceId);
+                            }
+                        }
+                    } else {
+                        di = getInputDevice(ev.deviceId);
+                        
+                        // first crack at it
+                        send = preprocessEvent(di, ev);
+
+                        if (ev.type == RawInputEvent.EV_KEY) {
+                            di.mMetaKeysState = makeMetaState(ev.keycode,
+                                    ev.value != 0, di.mMetaKeysState);
+                            mHaveGlobalMetaState = false;
+                        }
+                    }
+
+                    if (di == null) {
+                        continue;
+                    }
+                    
+                    if (configChanged) {
+                        synchronized (mFirst) {
+                            addLocked(di, SystemClock.uptimeMillis(), 0,
+                                    RawInputEvent.CLASS_CONFIGURATION_CHANGED,
+                                    null);
+                        }
+                    }
+                    
+                    if (!send) {
+                        continue;
+                    }
+                    
+                    synchronized (mFirst) {
+                        // NOTE: The event timebase absolutely must be the same
+                        // timebase as SystemClock.uptimeMillis().
+                        //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
+                        final long curTime = SystemClock.uptimeMillis();
+                        //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
+                        
+                        final int classes = di.classes;
+                        final int type = ev.type;
+                        final int scancode = ev.scancode;
+                        send = false;
+                        
+                        // Is it a key event?
+                        if (type == RawInputEvent.EV_KEY &&
+                                (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
+                                (scancode < RawInputEvent.BTN_FIRST ||
+                                        scancode > RawInputEvent.BTN_LAST)) {
+                            boolean down;
+                            if (ev.value != 0) {
+                                down = true;
+                                di.mDownTime = curTime;
+                            } else {
+                                down = false;
+                            }
+                            int keycode = rotateKeyCodeLocked(ev.keycode);
+                            addLocked(di, curTime, ev.flags,
+                                    RawInputEvent.CLASS_KEYBOARD,
+                                    newKeyEvent(di, di.mDownTime, curTime, down,
+                                            keycode, 0, scancode,
+                                            ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
+                                             ? KeyEvent.FLAG_WOKE_HERE : 0));
+                        } else if (ev.type == RawInputEvent.EV_KEY) {
+                            if (ev.scancode == RawInputEvent.BTN_TOUCH &&
+                                    (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+                                di.mAbs.changed = true;
+                                di.mAbs.down = ev.value != 0;
+                            }
+                            if (ev.scancode == RawInputEvent.BTN_MOUSE &&
+                                    (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
+                                di.mRel.changed = true;
+                                di.mRel.down = ev.value != 0;
+                                send = true;
+                            }
+    
+                        } else if (ev.type == RawInputEvent.EV_ABS &&
+                                (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+                            if (ev.scancode == RawInputEvent.ABS_X) {
+                                di.mAbs.changed = true;
+                                di.mAbs.x = ev.value;
+                            } else if (ev.scancode == RawInputEvent.ABS_Y) {
+                                di.mAbs.changed = true;
+                                di.mAbs.y = ev.value;
+                            } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
+                                di.mAbs.changed = true;
+                                di.mAbs.pressure = ev.value;
+                            } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
+                                di.mAbs.changed = true;
+                                di.mAbs.size = ev.value;
+                            }
+    
+                        } else if (ev.type == RawInputEvent.EV_REL &&
+                                (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
+                            // Add this relative movement into our totals.
+                            if (ev.scancode == RawInputEvent.REL_X) {
+                                di.mRel.changed = true;
+                                di.mRel.x += ev.value;
+                            } else if (ev.scancode == RawInputEvent.REL_Y) {
+                                di.mRel.changed = true;
+                                di.mRel.y += ev.value;
+                            }
+                        }
+                        
+                        if (send || ev.type == RawInputEvent.EV_SYN) {
+                            if (mDisplay != null) {
+                                if (!mHaveGlobalMetaState) {
+                                    computeGlobalMetaStateLocked();
+                                }
+                                
+                                MotionEvent me;
+                                me = di.mAbs.generateMotion(di, curTime, true,
+                                        mDisplay, mOrientation, mGlobalMetaState);
+                                if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
+                                        + " y=" + di.mAbs.y + " ev=" + me);
+                                if (me != null) {
+                                    if (WindowManagerPolicy.WATCH_POINTER) {
+                                        Log.i(TAG, "Enqueueing: " + me);
+                                    }
+                                    addLocked(di, curTime, ev.flags,
+                                            RawInputEvent.CLASS_TOUCHSCREEN, me);
+                                }
+                                me = di.mRel.generateMotion(di, curTime, false,
+                                        mDisplay, mOrientation, mGlobalMetaState);
+                                if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
+                                        + " y=" + di.mRel.y + " ev=" + me);
+                                if (me != null) {
+                                    addLocked(di, curTime, ev.flags,
+                                            RawInputEvent.CLASS_TRACKBALL, me);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            catch (RuntimeException exc) {
+                Log.e(TAG, "InputReaderThread uncaught exception", exc);
+            }
+        }
+    };
+
+    /**
+     * Returns a new meta state for the given keys and old state.
+     */
+    private static final int makeMetaState(int keycode, boolean down, int old) {
+        int mask;
+        switch (keycode) {
+        case KeyEvent.KEYCODE_ALT_LEFT:
+            mask = KeyEvent.META_ALT_LEFT_ON;
+            break;
+        case KeyEvent.KEYCODE_ALT_RIGHT:
+            mask = KeyEvent.META_ALT_RIGHT_ON;
+            break;
+        case KeyEvent.KEYCODE_SHIFT_LEFT:
+            mask = KeyEvent.META_SHIFT_LEFT_ON;
+            break;
+        case KeyEvent.KEYCODE_SHIFT_RIGHT:
+            mask = KeyEvent.META_SHIFT_RIGHT_ON;
+            break;
+        case KeyEvent.KEYCODE_SYM:
+            mask = KeyEvent.META_SYM_ON;
+            break;
+        default:
+            return old;
+        }
+        int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
+                    & (down ? (old | mask) : (old & ~mask));
+        if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
+            result |= KeyEvent.META_ALT_ON;
+        }
+        if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
+            result |= KeyEvent.META_SHIFT_ON;
+        }
+        return result;
+    }
+
+    private void computeGlobalMetaStateLocked() {
+        int i = mDevices.size();
+        mGlobalMetaState = 0;
+        while ((--i) >= 0) {
+            mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
+        }
+        mHaveGlobalMetaState = true;
+    }
+    
+    /*
+     * Return true if you want the event to get passed on to the 
+     * rest of the system, and false if you've handled it and want
+     * it dropped.
+     */
+    abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
+
+    InputDevice getInputDevice(int deviceId) {
+        synchronized (mFirst) {
+            return getInputDeviceLocked(deviceId);
+        }
+    }
+    
+    private InputDevice getInputDeviceLocked(int deviceId) {
+        return mDevices.get(deviceId);
+    }
+
+    public void setOrientation(int orientation) {
+        synchronized(mFirst) {
+            mOrientation = orientation;
+            switch (orientation) {
+                case Surface.ROTATION_90:
+                    mKeyRotationMap = KEY_90_MAP;
+                    break;
+                case Surface.ROTATION_180:
+                    mKeyRotationMap = KEY_180_MAP;
+                    break;
+                case Surface.ROTATION_270:
+                    mKeyRotationMap = KEY_270_MAP;
+                    break;
+                default:
+                    mKeyRotationMap = null;
+                    break;
+            }
+        }
+    }
+    
+    public int rotateKeyCode(int keyCode) {
+        synchronized(mFirst) {
+            return rotateKeyCodeLocked(keyCode);
+        }
+    }
+    
+    private int rotateKeyCodeLocked(int keyCode) {
+        int[] map = mKeyRotationMap;
+        if (map != null) {
+            final int N = map.length;
+            for (int i=0; i<N; i+=2) {
+                if (map[i] == keyCode) {
+                    return map[i+1];
+                }
+            }
+        }
+        return keyCode;
+    }
+    
+    boolean hasEvents() {
+        synchronized (mFirst) {
+            return mFirst.next != mLast;
+        }
+    }
+    
+    /*
+     * returns true if we returned an event, and false if we timed out
+     */
+    QueuedEvent getEvent(long timeoutMS) {
+        long begin = SystemClock.uptimeMillis();
+        final long end = begin+timeoutMS;
+        long now = begin;
+        synchronized (mFirst) {
+            while (mFirst.next == mLast && end > now) {
+                try {
+                    mWakeLock.release();
+                    mFirst.wait(end-now);
+                }
+                catch (InterruptedException e) {
+                }
+                now = SystemClock.uptimeMillis();
+                if (begin > now) {
+                    begin = now;
+                }
+            }
+            if (mFirst.next == mLast) {
+                return null;
+            }
+            QueuedEvent p = mFirst.next;
+            mFirst.next = p.next;
+            mFirst.next.prev = mFirst;
+            p.inQueue = false;
+            return p;
+        }
+    }
+
+    void recycleEvent(QueuedEvent ev) {
+        synchronized (mFirst) {
+            //Log.i(TAG, "Recycle event: " + ev);
+            if (ev.event == ev.inputDevice.mAbs.currentMove) {
+                ev.inputDevice.mAbs.currentMove = null;
+            }
+            if (ev.event == ev.inputDevice.mRel.currentMove) {
+                if (false) Log.i(TAG, "Detach rel " + ev.event);
+                ev.inputDevice.mRel.currentMove = null;
+                ev.inputDevice.mRel.x = 0;
+                ev.inputDevice.mRel.y = 0;
+            }
+            recycleLocked(ev);
+        }
+    }
+    
+    void filterQueue(FilterCallback cb) {
+        synchronized (mFirst) {
+            QueuedEvent cur = mLast.prev;
+            while (cur.prev != null) {
+                switch (cb.filterEvent(cur)) {
+                    case FILTER_REMOVE:
+                        cur.prev.next = cur.next;
+                        cur.next.prev = cur.prev;
+                        break;
+                    case FILTER_ABORT:
+                        return;
+                }
+                cur = cur.prev;
+            }
+        }
+    }
+    
+    private QueuedEvent obtainLocked(InputDevice device, long when,
+            int flags, int classType, Object event) {
+        QueuedEvent ev;
+        if (mCacheCount == 0) {
+            ev = new QueuedEvent();
+        } else {
+            ev = mCache;
+            ev.inQueue = false;
+            mCache = ev.next;
+            mCacheCount--;
+        }
+        ev.inputDevice = device;
+        ev.when = when;
+        ev.flags = flags;
+        ev.classType = classType;
+        ev.event = event;
+        return ev;
+    }
+
+    private void recycleLocked(QueuedEvent ev) {
+        if (ev.inQueue) {
+            throw new RuntimeException("Event already in queue!");
+        }
+        if (mCacheCount < 10) {
+            mCacheCount++;
+            ev.next = mCache;
+            mCache = ev;
+            ev.inQueue = true;
+        }
+    }
+
+    private void addLocked(InputDevice device, long when, int flags,
+            int classType, Object event) {
+        boolean poke = mFirst.next == mLast;
+
+        QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
+        QueuedEvent p = mLast.prev;
+        while (p != mFirst && ev.when < p.when) {
+            p = p.prev;
+        }
+
+        ev.next = p.next;
+        ev.prev = p;
+        p.next = ev;
+        ev.next.prev = ev;
+        ev.inQueue = true;
+
+        if (poke) {
+            mFirst.notify();
+            mWakeLock.acquire();
+        }
+    }
+
+    private InputDevice newInputDevice(int deviceId) {
+        int classes = getDeviceClasses(deviceId);
+        String name = getDeviceName(deviceId);
+        Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
+                + ", name=" + name
+                + ", classes=" + Integer.toHexString(classes));
+        InputDevice.AbsoluteInfo absX;
+        InputDevice.AbsoluteInfo absY;
+        InputDevice.AbsoluteInfo absPressure;
+        InputDevice.AbsoluteInfo absSize;
+        if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+            absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
+            absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
+            absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
+            absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
+        } else {
+            absX = null;
+            absY = null;
+            absPressure = null;
+            absSize = null;
+        }
+        
+        return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
+    }
+    
+    private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
+            String name) {
+        InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
+        if (getAbsoluteInfo(id, channel, info)
+                && info.minValue != info.maxValue) {
+            Log.i(TAG, "  " + name + ": min=" + info.minValue
+                    + " max=" + info.maxValue
+                    + " flat=" + info.flat
+                    + " fuzz=" + info.fuzz);
+            info.range = info.maxValue-info.minValue;
+            return info;
+        }
+        Log.i(TAG, "  " + name + ": unknown values");
+        return null;
+    }
+    private static native boolean readEvent(RawInputEvent outEvent);
+}