Files
frameworks_base/services/java/com/android/server/KeyInputQueue.java
Dianne Hackborn e3dd884815 Implement virtual button support.
The kernel can now publish a property describing the layout of virtual
hardware buttons on the touchscreen.  These outside of the display
area (outside of the absolute x and y controller range the driver
reports), and when the user presses on them a key event will be
generated rather than a touch event.

This also includes a number of tweaks to the absolute controller
processing to make things work better on the new screens.  For
example, we now reject down events outside of the display area.

Still left to be done is the ability to cancel a key down event,
so the user can slide up from the virtual keys to the touch screen
without causing a virtual key to execute.
2009-07-14 18:51:53 -07:00

851 lines
34 KiB
Java

/*
* 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.LatencyTimer;
import android.os.PowerManager;
import android.os.SystemClock;
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;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";
static final boolean DEBUG_VIRTUAL_KEYS = false;
final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;
final QueuedEvent mFirst;
final QueuedEvent mLast;
QueuedEvent mCache;
int mCacheCount;
Display mDisplay = null;
int mDisplayWidth;
int mDisplayHeight;
int mOrientation = Surface.ROTATION_0;
int[] mKeyRotationMap = null;
VirtualKey mPressedVirtualKey = 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;
private static final boolean MEASURE_LATENCY = false;
private LatencyTimer lt;
public interface FilterCallback {
int filterEvent(QueuedEvent ev);
}
static class QueuedEvent {
InputDevice inputDevice;
long whenNano;
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.whenNano = that.whenNano;
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;
}
/**
* A key that exists as a part of the touch-screen, outside of the normal
* display area of the screen.
*/
static class VirtualKey {
int scancode;
int centerx;
int centery;
int width;
int height;
int hitLeft;
int hitTop;
int hitRight;
int hitBottom;
InputDevice lastDevice;
int lastKeycode;
boolean checkHit(int x, int y) {
return (x >= hitLeft && x <= hitRight
&& y >= hitTop && y <= hitBottom);
}
void computeHitRect(InputDevice dev, int dw, int dh) {
if (dev == lastDevice) {
return;
}
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
+ ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
lastDevice = dev;
int minx = dev.absX.minValue;
int maxx = dev.absX.maxValue;
int halfw = width/2;
int left = centerx - halfw;
int right = centerx + halfw;
hitLeft = minx + ((left*maxx-minx)/dw);
hitRight = minx + ((right*maxx-minx)/dw);
int miny = dev.absY.minValue;
int maxy = dev.absY.maxValue;
int halfh = height/2;
int top = centery - halfh;
int bottom = centery + halfh;
hitTop = miny + ((top*maxy-miny)/dh);
hitBottom = miny + ((bottom*maxy-miny)/dh);
}
}
KeyInputQueue(Context context) {
if (MEASURE_LATENCY) {
lt = new LatencyTimer(100, 1000);
}
try {
FileInputStream fis = new FileInputStream(
"/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String str = br.readLine();
if (str != null) {
String[] it = str.split(":");
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
final int N = it.length-6;
for (int i=0; i<=N; i+=6) {
if (!"0x01".equals(it[i])) {
Log.w(TAG, "Unknown virtual key type at elem #" + i
+ ": " + it[i]);
continue;
}
try {
VirtualKey sb = new VirtualKey();
sb.scancode = Integer.parseInt(it[i+1]);
sb.centerx = Integer.parseInt(it[i+2]);
sb.centery = Integer.parseInt(it[i+3]);
sb.width = Integer.parseInt(it[i+4]);
sb.height = Integer.parseInt(it[i+5]);
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
+ sb.scancode + ": center=" + sb.centerx + ","
+ sb.centery + " size=" + sb.width + "x"
+ sb.height);
mVirtualKeys.add(sb);
} catch (NumberFormatException e) {
Log.w(TAG, "Bad number at region " + i + " in: "
+ str, e);
}
}
}
br.close();
} catch (FileNotFoundException e) {
Log.i(TAG, "No virtual keys found");
} catch (IOException e) {
Log.w(TAG, "Error reading virtual keys", e);
}
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;
// We assume at this point that the display dimensions reflect the
// natural, unrotated display. We will perform hit tests for soft
// buttons based on that display.
mDisplayWidth = display.getWidth();
mDisplayHeight = display.getHeight();
}
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 int scancodeToKeycode(int deviceId, int scancode);
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 | KeyEvent.FLAG_FROM_SYSTEM);
}
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, System.nanoTime(), 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();
final long curTimeNano = System.nanoTime();
//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, curTimeNano, 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;
InputDevice.MotionState ms = di.mAbs;
if (ms.changed) {
ms.changed = false;
boolean doMotion = true;
// Look for virtual buttons.
VirtualKey vk = mPressedVirtualKey;
if (vk != null) {
doMotion = false;
if (!ms.down) {
mPressedVirtualKey = null;
ms.lastDown = ms.down;
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
"Generate key up for: " + vk.scancode);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mDownTime,
curTime, false,
vk.lastKeycode,
0, vk.scancode, 0));
}
} else if (ms.down && !ms.lastDown) {
vk = findSoftButton(di);
if (vk != null) {
doMotion = false;
mPressedVirtualKey = vk;
vk.lastKeycode = scancodeToKeycode(
di.id, vk.scancode);
ms.lastDown = ms.down;
di.mDownTime = curTime;
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
"Generate key down for: " + vk.scancode
+ " (keycode=" + vk.lastKeycode + ")");
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mDownTime,
curTime, true,
vk.lastKeycode, 0,
vk.scancode, 0));
}
}
if (doMotion) {
me = ms.generateMotion(di, curTime,
curTimeNano, 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, curTimeNano, ev.flags,
RawInputEvent.CLASS_TOUCHSCREEN, me);
}
}
}
ms = di.mRel;
if (ms.changed) {
ms.changed = false;
me = ms.generateMotion(di, curTime,
curTimeNano, 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, curTimeNano, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
}
}
}
}
}
}
} catch (RuntimeException exc) {
Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
};
private VirtualKey findSoftButton(InputDevice dev) {
final int N = mVirtualKeys.size();
if (N <= 0) {
return null;
}
final InputDevice.AbsoluteInfo absx = dev.absX;
final InputDevice.AbsoluteInfo absy = dev.absY;
final InputDevice.MotionState absm = dev.mAbs;
if (absx == null || absy == null || absm == null) {
return null;
}
if (absm.x >= absx.minValue && absm.x <= absx.maxValue
&& absm.y >= absy.minValue && absm.y <= absy.maxValue) {
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input (" + absm.x
+ "," + absm.y + ") inside of display");
return null;
}
for (int i=0; i<N; i++) {
VirtualKey sb = mVirtualKeys.get(i);
sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test (" + absm.x + ","
+ absm.y + ") in code " + sb.scancode + " - (" + sb.hitLeft
+ "," + sb.hitTop + ")-(" + sb.hitRight + ","
+ sb.hitBottom + ")");
if (sb.checkHit(absm.x, absm.y)) {
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
return sb;
}
}
return null;
}
/**
* 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 whenNano,
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.whenNano = whenNano;
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 whenNano, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;
QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
QueuedEvent p = mLast.prev;
while (p != mFirst && ev.whenNano < p.whenNano) {
p = p.prev;
}
ev.next = p.next;
ev.prev = p;
p.next = ev;
ev.next.prev = ev;
ev.inQueue = true;
if (poke) {
long time;
if (MEASURE_LATENCY) {
time = System.nanoTime();
}
mFirst.notify();
mWakeLock.acquire();
if (MEASURE_LATENCY) {
lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
}
}
}
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);
}