/* * 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 mDevices = new SparseArray(); 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= 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 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); }