blob: 0535d4ccb5598fca79459779bdaae220b050d96a [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040021import android.os.Environment;
Michael Chan53071d62009-05-13 17:29:48 -070022import android.os.LatencyTimer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.os.PowerManager;
Michael Chan53071d62009-05-13 17:29:48 -070024import android.os.SystemClock;
Joe Onorato8a9b2202010-02-26 18:56:32 -080025import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.util.SparseArray;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040027import android.util.Xml;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.view.Display;
29import android.view.KeyEvent;
30import android.view.MotionEvent;
31import android.view.RawInputEvent;
32import android.view.Surface;
33import android.view.WindowManagerPolicy;
34
Dianne Hackborn2269d1572010-02-24 19:54:22 -080035import com.android.internal.util.XmlUtils;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040036
37import org.xmlpull.v1.XmlPullParser;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040038
Dianne Hackborne3dd8842009-07-14 12:06:54 -070039import java.io.BufferedReader;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040040import java.io.File;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070041import java.io.FileInputStream;
42import java.io.FileNotFoundException;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040043import java.io.FileReader;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070044import java.io.IOException;
45import java.io.InputStreamReader;
46import java.util.ArrayList;
47
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048public abstract class KeyInputQueue {
49 static final String TAG = "KeyInputQueue";
50
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -070051 static final boolean DEBUG = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070052 static final boolean DEBUG_VIRTUAL_KEYS = false;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -070053 static final boolean DEBUG_POINTERS = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070054
Dianne Hackborn1411d1c2009-10-12 23:21:18 -070055 /**
56 * Turn on some hacks we have to improve the touch interaction with a
57 * certain device whose screen currently is not all that good.
58 */
Dianne Hackborn65cb6052009-11-10 17:06:22 -080059 static boolean BAD_TOUCH_HACK = false;
Dianne Hackborn1411d1c2009-10-12 23:21:18 -070060
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040061 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
62
Dianne Hackborne3dd8842009-07-14 12:06:54 -070063 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
Dianne Hackborna8f60182009-09-01 19:01:50 -070064 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
Dianne Hackborne3dd8842009-07-14 12:06:54 -070065 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070066 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68 int mGlobalMetaState = 0;
69 boolean mHaveGlobalMetaState = false;
70
71 final QueuedEvent mFirst;
72 final QueuedEvent mLast;
73 QueuedEvent mCache;
74 int mCacheCount;
75
76 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070077 int mDisplayWidth;
78 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
80 int mOrientation = Surface.ROTATION_0;
81 int[] mKeyRotationMap = null;
82
Dianne Hackborne3dd8842009-07-14 12:06:54 -070083 VirtualKey mPressedVirtualKey = null;
84
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 PowerManager.WakeLock mWakeLock;
86
87 static final int[] KEY_90_MAP = new int[] {
88 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
89 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
90 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
91 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
92 };
93
94 static final int[] KEY_180_MAP = new int[] {
95 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
96 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
97 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
98 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
99 };
100
101 static final int[] KEY_270_MAP = new int[] {
102 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
103 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
104 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
105 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
106 };
107
108 public static final int FILTER_REMOVE = 0;
109 public static final int FILTER_KEEP = 1;
110 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700111
112 private static final boolean MEASURE_LATENCY = false;
113 private LatencyTimer lt;
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 public interface FilterCallback {
116 int filterEvent(QueuedEvent ev);
117 }
118
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700119 public interface HapticFeedbackCallback {
120 void virtualKeyFeedback(KeyEvent event);
121 }
122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 static class QueuedEvent {
124 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700125 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 int flags; // From the raw event
127 int classType; // One of the class constants in InputEvent
128 Object event;
129 boolean inQueue;
130
131 void copyFrom(QueuedEvent that) {
132 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700133 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 this.flags = that.flags;
135 this.classType = that.classType;
136 this.event = that.event;
137 }
138
139 @Override
140 public String toString() {
141 return "QueuedEvent{"
142 + Integer.toHexString(System.identityHashCode(this))
143 + " " + event + "}";
144 }
145
146 // not copied
147 QueuedEvent prev;
148 QueuedEvent next;
149 }
150
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700151 /**
152 * A key that exists as a part of the touch-screen, outside of the normal
153 * display area of the screen.
154 */
155 static class VirtualKey {
156 int scancode;
157 int centerx;
158 int centery;
159 int width;
160 int height;
161
162 int hitLeft;
163 int hitTop;
164 int hitRight;
165 int hitBottom;
166
167 InputDevice lastDevice;
168 int lastKeycode;
169
170 boolean checkHit(int x, int y) {
171 return (x >= hitLeft && x <= hitRight
172 && y >= hitTop && y <= hitBottom);
173 }
174
175 void computeHitRect(InputDevice dev, int dw, int dh) {
176 if (dev == lastDevice) {
177 return;
178 }
179
Joe Onorato8a9b2202010-02-26 18:56:32 -0800180 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700181 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
182
183 lastDevice = dev;
184
185 int minx = dev.absX.minValue;
186 int maxx = dev.absX.maxValue;
187
188 int halfw = width/2;
189 int left = centerx - halfw;
190 int right = centerx + halfw;
191 hitLeft = minx + ((left*maxx-minx)/dw);
192 hitRight = minx + ((right*maxx-minx)/dw);
193
194 int miny = dev.absY.minValue;
195 int maxy = dev.absY.maxValue;
196
197 int halfh = height/2;
198 int top = centery - halfh;
199 int bottom = centery + halfh;
200 hitTop = miny + ((top*maxy-miny)/dh);
201 hitBottom = miny + ((bottom*maxy-miny)/dh);
202 }
203 }
Michael Chan53071d62009-05-13 17:29:48 -0700204
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700205 private void readVirtualKeys(String deviceName) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700206 try {
207 FileInputStream fis = new FileInputStream(
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700208 "/sys/board_properties/virtualkeys." + deviceName);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700209 InputStreamReader isr = new InputStreamReader(fis);
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700210 BufferedReader br = new BufferedReader(isr, 2048);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700211 String str = br.readLine();
212 if (str != null) {
213 String[] it = str.split(":");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800214 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700215 final int N = it.length-6;
216 for (int i=0; i<=N; i+=6) {
217 if (!"0x01".equals(it[i])) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800218 Slog.w(TAG, "Unknown virtual key type at elem #" + i
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700219 + ": " + it[i]);
220 continue;
221 }
222 try {
223 VirtualKey sb = new VirtualKey();
224 sb.scancode = Integer.parseInt(it[i+1]);
225 sb.centerx = Integer.parseInt(it[i+2]);
226 sb.centery = Integer.parseInt(it[i+3]);
227 sb.width = Integer.parseInt(it[i+4]);
228 sb.height = Integer.parseInt(it[i+5]);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800229 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700230 + sb.scancode + ": center=" + sb.centerx + ","
231 + sb.centery + " size=" + sb.width + "x"
232 + sb.height);
233 mVirtualKeys.add(sb);
234 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800235 Slog.w(TAG, "Bad number at region " + i + " in: "
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700236 + str, e);
237 }
238 }
239 }
240 br.close();
241 } catch (FileNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800242 Slog.i(TAG, "No virtual keys found");
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700243 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800244 Slog.w(TAG, "Error reading virtual keys", e);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700245 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400246 }
247
248 private void readExcludedDevices() {
249 // Read partner-provided list of excluded input devices
250 XmlPullParser parser = null;
251 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
252 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
253 FileReader confreader = null;
254 try {
255 confreader = new FileReader(confFile);
256 parser = Xml.newPullParser();
257 parser.setInput(confreader);
258 XmlUtils.beginDocument(parser, "devices");
259
260 while (true) {
261 XmlUtils.nextElement(parser);
262 if (!"device".equals(parser.getName())) {
263 break;
264 }
265 String name = parser.getAttributeValue(null, "name");
266 if (name != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800267 if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400268 addExcludedDevice(name);
269 }
270 }
271 } catch (FileNotFoundException e) {
272 // It's ok if the file does not exist.
273 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800274 Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400275 } finally {
276 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
277 }
278 }
279
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700280 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400281 if (MEASURE_LATENCY) {
282 lt = new LatencyTimer(100, 1000);
283 }
284
Dianne Hackborn65cb6052009-11-10 17:06:22 -0800285 BAD_TOUCH_HACK = context.getResources().getBoolean(
286 com.android.internal.R.bool.config_filterTouchEvents);
287
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700288 mHapticFeedbackCallback = hapticFeedbackCallback;
289
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400290 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 PowerManager pm = (PowerManager)context.getSystemService(
293 Context.POWER_SERVICE);
294 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
295 "KeyInputQueue");
296 mWakeLock.setReferenceCounted(false);
297
298 mFirst = new QueuedEvent();
299 mLast = new QueuedEvent();
300 mFirst.next = mLast;
301 mLast.prev = mFirst;
302
303 mThread.start();
304 }
305
306 public void setDisplay(Display display) {
307 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700308
309 // We assume at this point that the display dimensions reflect the
310 // natural, unrotated display. We will perform hit tests for soft
311 // buttons based on that display.
312 mDisplayWidth = display.getWidth();
313 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315
316 public void getInputConfiguration(Configuration config) {
317 synchronized (mFirst) {
318 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
319 config.keyboard = Configuration.KEYBOARD_NOKEYS;
320 config.navigation = Configuration.NAVIGATION_NONAV;
321
322 final int N = mDevices.size();
323 for (int i=0; i<N; i++) {
324 InputDevice d = mDevices.valueAt(i);
325 if (d != null) {
326 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
327 config.touchscreen
328 = Configuration.TOUCHSCREEN_FINGER;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800329 //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
332 config.keyboard
333 = Configuration.KEYBOARD_QWERTY;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800334 //Slog.i("foo", "***** HAVE QWERTY!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 }
336 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
337 config.navigation
338 = Configuration.NAVIGATION_TRACKBALL;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800339 //Slog.i("foo", "***** HAVE TRACKBALL!");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700340 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
341 config.navigation
342 = Configuration.NAVIGATION_DPAD;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800343 //Slog.i("foo", "***** HAVE DPAD!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345 }
346 }
347 }
348 }
349
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700350 public int getScancodeState(int code) {
351 synchronized (mFirst) {
352 VirtualKey vk = mPressedVirtualKey;
353 if (vk != null) {
354 if (vk.scancode == code) {
355 return 2;
356 }
357 }
358 return nativeGetScancodeState(code);
359 }
360 }
361
362 public int getScancodeState(int deviceId, int code) {
363 synchronized (mFirst) {
364 VirtualKey vk = mPressedVirtualKey;
365 if (vk != null) {
366 if (vk.scancode == code) {
367 return 2;
368 }
369 }
370 return nativeGetScancodeState(deviceId, code);
371 }
372 }
373
Dianne Hackborn1d62ea92009-11-17 12:49:50 -0800374 public int getTrackballScancodeState(int code) {
375 synchronized (mFirst) {
376 final int N = mDevices.size();
377 for (int i=0; i<N; i++) {
378 InputDevice dev = mDevices.valueAt(i);
379 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
380 int res = nativeGetScancodeState(dev.id, code);
381 if (res > 0) {
382 return res;
383 }
384 }
385 }
386 }
387
388 return 0;
389 }
390
391 public int getDPadScancodeState(int code) {
392 synchronized (mFirst) {
393 final int N = mDevices.size();
394 for (int i=0; i<N; i++) {
395 InputDevice dev = mDevices.valueAt(i);
396 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
397 int res = nativeGetScancodeState(dev.id, code);
398 if (res > 0) {
399 return res;
400 }
401 }
402 }
403 }
404
405 return 0;
406 }
407
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700408 public int getKeycodeState(int code) {
409 synchronized (mFirst) {
410 VirtualKey vk = mPressedVirtualKey;
411 if (vk != null) {
412 if (vk.lastKeycode == code) {
413 return 2;
414 }
415 }
416 return nativeGetKeycodeState(code);
417 }
418 }
419
420 public int getKeycodeState(int deviceId, int code) {
421 synchronized (mFirst) {
422 VirtualKey vk = mPressedVirtualKey;
423 if (vk != null) {
424 if (vk.lastKeycode == code) {
425 return 2;
426 }
427 }
428 return nativeGetKeycodeState(deviceId, code);
429 }
430 }
431
Dianne Hackborn1d62ea92009-11-17 12:49:50 -0800432 public int getTrackballKeycodeState(int code) {
433 synchronized (mFirst) {
434 final int N = mDevices.size();
435 for (int i=0; i<N; i++) {
436 InputDevice dev = mDevices.valueAt(i);
437 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
438 int res = nativeGetKeycodeState(dev.id, code);
439 if (res > 0) {
440 return res;
441 }
442 }
443 }
444 }
445
446 return 0;
447 }
448
449 public int getDPadKeycodeState(int code) {
450 synchronized (mFirst) {
451 final int N = mDevices.size();
452 for (int i=0; i<N; i++) {
453 InputDevice dev = mDevices.valueAt(i);
454 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
455 int res = nativeGetKeycodeState(dev.id, code);
456 if (res > 0) {
457 return res;
458 }
459 }
460 }
461 }
462
463 return 0;
464 }
465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 public static native String getDeviceName(int deviceId);
467 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400468 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 public static native boolean getAbsoluteInfo(int deviceId, int axis,
470 InputDevice.AbsoluteInfo outInfo);
471 public static native int getSwitchState(int sw);
472 public static native int getSwitchState(int deviceId, int sw);
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700473 public static native int nativeGetScancodeState(int code);
474 public static native int nativeGetScancodeState(int deviceId, int code);
475 public static native int nativeGetKeycodeState(int code);
476 public static native int nativeGetKeycodeState(int deviceId, int code);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700477 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
479
480 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
481 long eventTime, boolean down, int keycode, int repeatCount,
482 int scancode, int flags) {
483 return new KeyEvent(
484 downTime, eventTime,
485 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
486 keycode, repeatCount,
487 device != null ? device.mMetaKeysState : 0,
488 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700489 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 }
491
492 Thread mThread = new Thread("InputDeviceReader") {
493 public void run() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800494 if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 android.os.Process.setThreadPriority(
496 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
497
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700498 RawInputEvent ev = new RawInputEvent();
499 while (true) {
500 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 InputDevice di;
502
503 // block, doesn't release the monitor
504 readEvent(ev);
505
506 boolean send = false;
507 boolean configChanged = false;
508
509 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800510 Slog.i(TAG, "Input event: dev=0x"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 + Integer.toHexString(ev.deviceId)
512 + " type=0x" + Integer.toHexString(ev.type)
513 + " scancode=" + ev.scancode
514 + " keycode=" + ev.keycode
515 + " value=" + ev.value);
516 }
517
518 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
519 synchronized (mFirst) {
520 di = newInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700521 if (di.classes != 0) {
522 // If this device is some kind of input class,
523 // we care about it.
524 mDevices.put(ev.deviceId, di);
525 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
526 readVirtualKeys(di.name);
527 }
528 // The configuration may have changed because
529 // of this device.
530 configChanged = true;
531 } else {
532 // We won't do anything with this device.
533 mIgnoredDevices.put(ev.deviceId, di);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800534 Slog.i(TAG, "Ignoring non-input device: id=0x"
Dianne Hackborna8f60182009-09-01 19:01:50 -0700535 + Integer.toHexString(di.id)
536 + ", name=" + di.name);
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
540 synchronized (mFirst) {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700541 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800542 Slog.i(TAG, "Device removed: id=0x"
Dianne Hackborna8f60182009-09-01 19:01:50 -0700543 + Integer.toHexString(ev.deviceId));
544 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 di = mDevices.get(ev.deviceId);
546 if (di != null) {
547 mDevices.delete(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700548 // The configuration may have changed because
549 // of this device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 configChanged = true;
Dianne Hackborna8f60182009-09-01 19:01:50 -0700551 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
552 mIgnoredDevices.remove(ev.deviceId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800554 Slog.w(TAG, "Removing bad device id: "
Dianne Hackborna8f60182009-09-01 19:01:50 -0700555 + Integer.toHexString(ev.deviceId));
556 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 }
558 }
559 } else {
560 di = getInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700561 if (di == null) {
562 // This may be some junk from an ignored device.
563 continue;
564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565
566 // first crack at it
567 send = preprocessEvent(di, ev);
568
569 if (ev.type == RawInputEvent.EV_KEY) {
570 di.mMetaKeysState = makeMetaState(ev.keycode,
571 ev.value != 0, di.mMetaKeysState);
572 mHaveGlobalMetaState = false;
573 }
574 }
575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 if (configChanged) {
577 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700578 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
580 null);
581 }
582 }
583
584 if (!send) {
585 continue;
586 }
587
588 synchronized (mFirst) {
589 // NOTE: The event timebase absolutely must be the same
590 // timebase as SystemClock.uptimeMillis().
591 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
592 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700593 final long curTimeNano = System.nanoTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800594 //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595
596 final int classes = di.classes;
597 final int type = ev.type;
598 final int scancode = ev.scancode;
599 send = false;
600
601 // Is it a key event?
602 if (type == RawInputEvent.EV_KEY &&
603 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
604 (scancode < RawInputEvent.BTN_FIRST ||
605 scancode > RawInputEvent.BTN_LAST)) {
606 boolean down;
607 if (ev.value != 0) {
608 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700609 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 } else {
611 down = false;
612 }
613 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700614 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700616 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 keycode, 0, scancode,
618 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
619 ? KeyEvent.FLAG_WOKE_HERE : 0));
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 } else if (ev.type == RawInputEvent.EV_KEY) {
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700622 // Single touch protocol: touch going down or up.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700624 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
625 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
626 == RawInputEvent.CLASS_TOUCHSCREEN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700628 di.mAbs.mDown[0] = ev.value != 0;
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700629
630 // Trackball (mouse) protocol: press down or up.
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700631 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
633 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700634 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 send = true;
636 }
637
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700638 // Process position events from multitouch protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 } else if (ev.type == RawInputEvent.EV_ABS &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700640 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
641 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
642 di.mAbs.changed = true;
643 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
644 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
645 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
646 di.mAbs.changed = true;
647 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
648 + MotionEvent.SAMPLE_X] = ev.value;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800649 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700650 + di.mAbs.mAddingPointerOffset
651 + " X:" + ev.value);
652 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
653 di.mAbs.changed = true;
654 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
655 + MotionEvent.SAMPLE_Y] = ev.value;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800656 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700657 + di.mAbs.mAddingPointerOffset
658 + " Y:" + ev.value);
659 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
660 di.mAbs.changed = true;
661 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
662 + MotionEvent.SAMPLE_SIZE] = ev.value;
663 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700664
665 // Process position events from single touch protocol.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700666 } else if (ev.type == RawInputEvent.EV_ABS &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
668 if (ev.scancode == RawInputEvent.ABS_X) {
669 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700670 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 } else if (ev.scancode == RawInputEvent.ABS_Y) {
672 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700673 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
675 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700676 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
677 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700678 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
680 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700681 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
682 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700683 + MotionEvent.SAMPLE_SIZE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 }
685
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700686 // Process movement events from trackball (mouse) protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 } else if (ev.type == RawInputEvent.EV_REL &&
688 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
689 // Add this relative movement into our totals.
690 if (ev.scancode == RawInputEvent.REL_X) {
691 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700692 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 } else if (ev.scancode == RawInputEvent.REL_Y) {
694 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700695 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 }
697 }
698
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700699 // Handle multitouch protocol sync: tells us that the
700 // driver has returned all data for -one- of the pointers
701 // that is currently down.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700702 if (ev.type == RawInputEvent.EV_SYN
703 && ev.scancode == RawInputEvent.SYN_MT_REPORT
704 && di.mAbs != null) {
705 di.mAbs.changed = true;
706 if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
707 // If the value is <= 0, the pointer is not
708 // down, so keep it in the count.
709
710 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
711 + MotionEvent.SAMPLE_PRESSURE] != 0) {
712 final int num = di.mAbs.mNextNumPointers+1;
713 di.mAbs.mNextNumPointers = num;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800714 if (DEBUG_POINTERS) Slog.v(TAG,
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700715 "MT_REPORT: now have " + num + " pointers");
716 final int newOffset = (num <= InputDevice.MAX_POINTERS)
717 ? (num * MotionEvent.NUM_SAMPLE_DATA)
718 : (InputDevice.MAX_POINTERS *
719 MotionEvent.NUM_SAMPLE_DATA);
720 di.mAbs.mAddingPointerOffset = newOffset;
721 di.mAbs.mNextData[newOffset
722 + MotionEvent.SAMPLE_PRESSURE] = 0;
723 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800724 if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700725 }
726 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700727
728 // Handle general event sync: all data for the current
729 // event update has been delivered.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700730 } else if (send || (ev.type == RawInputEvent.EV_SYN
731 && ev.scancode == RawInputEvent.SYN_REPORT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 if (mDisplay != null) {
733 if (!mHaveGlobalMetaState) {
734 computeGlobalMetaStateLocked();
735 }
736
737 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700738
739 InputDevice.MotionState ms = di.mAbs;
740 if (ms.changed) {
741 ms.changed = false;
742
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700743 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
744 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
745 == RawInputEvent.CLASS_TOUCHSCREEN) {
746 ms.mNextNumPointers = 0;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700747 if (ms.mDown[0]) {
748 System.arraycopy(di.curTouchVals, 0,
749 ms.mNextData, 0,
750 MotionEvent.NUM_SAMPLE_DATA);
751 ms.mNextNumPointers++;
752 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700753 }
754
755 if (BAD_TOUCH_HACK) {
756 ms.dropBadPoint(di);
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700757 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700758
759 boolean doMotion = !monitorVirtualKey(di,
760 ev, curTime, curTimeNano);
761
762 if (doMotion && ms.mNextNumPointers > 0
Dianne Hackborndc953722009-10-19 11:24:39 -0700763 && (ms.mLastNumPointers == 0
764 || ms.mSkipLastPointers)) {
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700765 doMotion = !generateVirtualKeyDown(di,
766 ev, curTime, curTimeNano);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700768
769 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700770 // XXX Need to be able to generate
771 // multiple events here, for example
772 // if two fingers change up/down state
773 // at the same time.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700774 do {
775 me = ms.generateAbsMotion(di, curTime,
776 curTimeNano, mDisplay,
777 mOrientation, mGlobalMetaState);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800778 if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700779 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
780 + " y="
781 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
782 + " ev=" + me);
783 if (me != null) {
784 if (WindowManagerPolicy.WATCH_POINTER) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800785 Slog.i(TAG, "Enqueueing: " + me);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700786 }
787 addLocked(di, curTimeNano, ev.flags,
788 RawInputEvent.CLASS_TOUCHSCREEN, me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700789 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700790 } while (ms.hasMore());
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700791 } else {
792 // We are consuming movement in the
793 // virtual key area... but still
794 // propagate this to the previous
795 // data for comparisons.
Dianne Hackbornf2ddfb12009-10-13 22:43:33 -0700796 int num = ms.mNextNumPointers;
797 if (num > InputDevice.MAX_POINTERS) {
798 num = InputDevice.MAX_POINTERS;
799 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700800 System.arraycopy(ms.mNextData, 0,
801 ms.mLastData, 0,
Dianne Hackbornf2ddfb12009-10-13 22:43:33 -0700802 num * MotionEvent.NUM_SAMPLE_DATA);
803 ms.mLastNumPointers = num;
Dianne Hackborndc953722009-10-19 11:24:39 -0700804 ms.mSkipLastPointers = true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700805 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700806
807 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700809
810 ms = di.mRel;
811 if (ms.changed) {
812 ms.changed = false;
813
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700814 me = ms.generateRelMotion(di, curTime,
815 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700816 mOrientation, mGlobalMetaState);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800817 if (false) Slog.v(TAG, "Relative: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700818 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700819 + " y="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700820 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700821 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700822 if (me != null) {
823 addLocked(di, curTimeNano, ev.flags,
824 RawInputEvent.CLASS_TRACKBALL, me);
825 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700826
827 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 }
829 }
830 }
831 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700832
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700833 } catch (RuntimeException exc) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800834 Slog.e(TAG, "InputReaderThread uncaught exception", exc);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 }
837 }
838 };
839
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700840 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700841 final InputDevice.AbsoluteInfo absx = dev.absX;
842 final InputDevice.AbsoluteInfo absy = dev.absY;
843 final InputDevice.MotionState absm = dev.mAbs;
844 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700845 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700846 }
847
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700848 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
849 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
850 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
851 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800852 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700853 + absm.mNextData[MotionEvent.SAMPLE_X]
854 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700855 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700856 return true;
857 }
858
859 return false;
860 }
861
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700862 private VirtualKey findVirtualKey(InputDevice dev) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700863 final int N = mVirtualKeys.size();
864 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700865 return null;
866 }
867
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700868 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700869 for (int i=0; i<N; i++) {
870 VirtualKey sb = mVirtualKeys.get(i);
871 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800872 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700873 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
874 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700875 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700876 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
877 + sb.hitBottom + ")");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700878 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
879 absm.mNextData[MotionEvent.SAMPLE_Y])) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800880 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700881 return sb;
882 }
883 }
884
885 return null;
886 }
887
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700888 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
889 long curTime, long curTimeNano) {
890 if (isInsideDisplay(di)) {
891 // Didn't consume event.
892 return false;
893 }
894
895
896 VirtualKey vk = findVirtualKey(di);
897 if (vk != null) {
898 final InputDevice.MotionState ms = di.mAbs;
899 mPressedVirtualKey = vk;
900 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
901 ms.mLastNumPointers = ms.mNextNumPointers;
902 di.mKeyDownTime = curTime;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800903 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700904 "Generate key down for: " + vk.scancode
905 + " (keycode=" + vk.lastKeycode + ")");
906 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
907 vk.lastKeycode, 0, vk.scancode,
908 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
909 mHapticFeedbackCallback.virtualKeyFeedback(event);
910 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
911 event);
912 }
913
914 // We always consume the event, even if we didn't
915 // generate a key event. There are two reasons for
916 // this: to avoid spurious touches when holding
917 // the edges of the device near the touchscreen,
918 // and to avoid reporting events if there are virtual
919 // keys on the touchscreen outside of the display
920 // area.
921 // Note that for all of this we are only looking at the
922 // first pointer, since what we are handling here is the
923 // first pointer going down, and this is the coordinate
924 // that will be used to dispatch the event.
925 if (false) {
926 final InputDevice.AbsoluteInfo absx = di.absX;
927 final InputDevice.AbsoluteInfo absy = di.absY;
928 final InputDevice.MotionState absm = di.mAbs;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800929 Slog.v(TAG, "Rejecting ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700930 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
931 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
932 + absx.minValue + "," + absy.minValue
933 + ")-(" + absx.maxValue + ","
934 + absx.maxValue + ")");
935 }
936 return true;
937 }
938
939 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
940 long curTime, long curTimeNano) {
941 VirtualKey vk = mPressedVirtualKey;
942 if (vk == null) {
943 return false;
944 }
945
946 final InputDevice.MotionState ms = di.mAbs;
947 if (ms.mNextNumPointers <= 0) {
948 mPressedVirtualKey = null;
949 ms.mLastNumPointers = 0;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800950 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700951 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
952 vk.lastKeycode, 0, vk.scancode,
953 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
954 mHapticFeedbackCallback.virtualKeyFeedback(event);
955 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
956 event);
957 return true;
958
959 } else if (isInsideDisplay(di)) {
960 // Whoops the pointer has moved into
961 // the display area! Cancel the
962 // virtual key and start a pointer
963 // motion.
964 mPressedVirtualKey = null;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800965 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700966 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
967 vk.lastKeycode, 0, vk.scancode,
968 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
969 mHapticFeedbackCallback.virtualKeyFeedback(event);
970 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
971 event);
972 ms.mLastNumPointers = 0;
973 return false;
974 }
975
976 return true;
977 }
978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 /**
980 * Returns a new meta state for the given keys and old state.
981 */
982 private static final int makeMetaState(int keycode, boolean down, int old) {
983 int mask;
984 switch (keycode) {
985 case KeyEvent.KEYCODE_ALT_LEFT:
986 mask = KeyEvent.META_ALT_LEFT_ON;
987 break;
988 case KeyEvent.KEYCODE_ALT_RIGHT:
989 mask = KeyEvent.META_ALT_RIGHT_ON;
990 break;
991 case KeyEvent.KEYCODE_SHIFT_LEFT:
992 mask = KeyEvent.META_SHIFT_LEFT_ON;
993 break;
994 case KeyEvent.KEYCODE_SHIFT_RIGHT:
995 mask = KeyEvent.META_SHIFT_RIGHT_ON;
996 break;
997 case KeyEvent.KEYCODE_SYM:
998 mask = KeyEvent.META_SYM_ON;
999 break;
1000 default:
1001 return old;
1002 }
1003 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
1004 & (down ? (old | mask) : (old & ~mask));
1005 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
1006 result |= KeyEvent.META_ALT_ON;
1007 }
1008 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
1009 result |= KeyEvent.META_SHIFT_ON;
1010 }
1011 return result;
1012 }
1013
1014 private void computeGlobalMetaStateLocked() {
1015 int i = mDevices.size();
1016 mGlobalMetaState = 0;
1017 while ((--i) >= 0) {
1018 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
1019 }
1020 mHaveGlobalMetaState = true;
1021 }
1022
1023 /*
1024 * Return true if you want the event to get passed on to the
1025 * rest of the system, and false if you've handled it and want
1026 * it dropped.
1027 */
1028 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
1029
1030 InputDevice getInputDevice(int deviceId) {
1031 synchronized (mFirst) {
1032 return getInputDeviceLocked(deviceId);
1033 }
1034 }
1035
1036 private InputDevice getInputDeviceLocked(int deviceId) {
1037 return mDevices.get(deviceId);
1038 }
1039
1040 public void setOrientation(int orientation) {
1041 synchronized(mFirst) {
1042 mOrientation = orientation;
1043 switch (orientation) {
1044 case Surface.ROTATION_90:
1045 mKeyRotationMap = KEY_90_MAP;
1046 break;
1047 case Surface.ROTATION_180:
1048 mKeyRotationMap = KEY_180_MAP;
1049 break;
1050 case Surface.ROTATION_270:
1051 mKeyRotationMap = KEY_270_MAP;
1052 break;
1053 default:
1054 mKeyRotationMap = null;
1055 break;
1056 }
1057 }
1058 }
1059
1060 public int rotateKeyCode(int keyCode) {
1061 synchronized(mFirst) {
1062 return rotateKeyCodeLocked(keyCode);
1063 }
1064 }
1065
1066 private int rotateKeyCodeLocked(int keyCode) {
1067 int[] map = mKeyRotationMap;
1068 if (map != null) {
1069 final int N = map.length;
1070 for (int i=0; i<N; i+=2) {
1071 if (map[i] == keyCode) {
1072 return map[i+1];
1073 }
1074 }
1075 }
1076 return keyCode;
1077 }
1078
1079 boolean hasEvents() {
1080 synchronized (mFirst) {
1081 return mFirst.next != mLast;
1082 }
1083 }
1084
1085 /*
1086 * returns true if we returned an event, and false if we timed out
1087 */
1088 QueuedEvent getEvent(long timeoutMS) {
1089 long begin = SystemClock.uptimeMillis();
1090 final long end = begin+timeoutMS;
1091 long now = begin;
1092 synchronized (mFirst) {
1093 while (mFirst.next == mLast && end > now) {
1094 try {
1095 mWakeLock.release();
1096 mFirst.wait(end-now);
1097 }
1098 catch (InterruptedException e) {
1099 }
1100 now = SystemClock.uptimeMillis();
1101 if (begin > now) {
1102 begin = now;
1103 }
1104 }
1105 if (mFirst.next == mLast) {
1106 return null;
1107 }
1108 QueuedEvent p = mFirst.next;
1109 mFirst.next = p.next;
1110 mFirst.next.prev = mFirst;
1111 p.inQueue = false;
1112 return p;
1113 }
1114 }
1115
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001116 /**
1117 * Return true if the queue has an up event pending that corresponds
1118 * to the same key as the given key event.
1119 */
1120 boolean hasKeyUpEvent(KeyEvent origEvent) {
1121 synchronized (mFirst) {
1122 final int keyCode = origEvent.getKeyCode();
1123 QueuedEvent cur = mLast.prev;
1124 while (cur.prev != null) {
1125 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1126 KeyEvent ke = (KeyEvent)cur.event;
1127 if (ke.getAction() == KeyEvent.ACTION_UP
1128 && ke.getKeyCode() == keyCode) {
1129 return true;
1130 }
1131 }
1132 cur = cur.prev;
1133 }
1134 }
1135
1136 return false;
1137 }
1138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 void recycleEvent(QueuedEvent ev) {
1140 synchronized (mFirst) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001141 //Slog.i(TAG, "Recycle event: " + ev);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1143 ev.inputDevice.mAbs.currentMove = null;
1144 }
1145 if (ev.event == ev.inputDevice.mRel.currentMove) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001146 if (false) Slog.i(TAG, "Detach rel " + ev.event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -07001148 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1149 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
1151 recycleLocked(ev);
1152 }
1153 }
1154
1155 void filterQueue(FilterCallback cb) {
1156 synchronized (mFirst) {
1157 QueuedEvent cur = mLast.prev;
1158 while (cur.prev != null) {
1159 switch (cb.filterEvent(cur)) {
1160 case FILTER_REMOVE:
1161 cur.prev.next = cur.next;
1162 cur.next.prev = cur.prev;
1163 break;
1164 case FILTER_ABORT:
1165 return;
1166 }
1167 cur = cur.prev;
1168 }
1169 }
1170 }
1171
Michael Chan53071d62009-05-13 17:29:48 -07001172 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 int flags, int classType, Object event) {
1174 QueuedEvent ev;
1175 if (mCacheCount == 0) {
1176 ev = new QueuedEvent();
1177 } else {
1178 ev = mCache;
1179 ev.inQueue = false;
1180 mCache = ev.next;
1181 mCacheCount--;
1182 }
1183 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -07001184 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 ev.flags = flags;
1186 ev.classType = classType;
1187 ev.event = event;
1188 return ev;
1189 }
1190
1191 private void recycleLocked(QueuedEvent ev) {
1192 if (ev.inQueue) {
1193 throw new RuntimeException("Event already in queue!");
1194 }
1195 if (mCacheCount < 10) {
1196 mCacheCount++;
1197 ev.next = mCache;
1198 mCache = ev;
1199 ev.inQueue = true;
1200 }
1201 }
1202
Michael Chan53071d62009-05-13 17:29:48 -07001203 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 int classType, Object event) {
1205 boolean poke = mFirst.next == mLast;
1206
Michael Chan53071d62009-05-13 17:29:48 -07001207 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -07001209 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 p = p.prev;
1211 }
1212
1213 ev.next = p.next;
1214 ev.prev = p;
1215 p.next = ev;
1216 ev.next.prev = ev;
1217 ev.inQueue = true;
1218
1219 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -07001220 long time;
1221 if (MEASURE_LATENCY) {
1222 time = System.nanoTime();
1223 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 mFirst.notify();
1225 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -07001226 if (MEASURE_LATENCY) {
1227 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 }
1230 }
1231
1232 private InputDevice newInputDevice(int deviceId) {
1233 int classes = getDeviceClasses(deviceId);
1234 String name = getDeviceName(deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -07001235 InputDevice.AbsoluteInfo absX = null;
1236 InputDevice.AbsoluteInfo absY = null;
1237 InputDevice.AbsoluteInfo absPressure = null;
1238 InputDevice.AbsoluteInfo absSize = null;
1239 if (classes != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001240 Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
Dianne Hackborna8f60182009-09-01 19:01:50 -07001241 + ", name=" + name
1242 + ", classes=" + Integer.toHexString(classes));
1243 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1244 absX = loadAbsoluteInfo(deviceId,
1245 RawInputEvent.ABS_MT_POSITION_X, "X");
1246 absY = loadAbsoluteInfo(deviceId,
1247 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1248 absPressure = loadAbsoluteInfo(deviceId,
1249 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1250 absSize = loadAbsoluteInfo(deviceId,
1251 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1252 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1253 absX = loadAbsoluteInfo(deviceId,
1254 RawInputEvent.ABS_X, "X");
1255 absY = loadAbsoluteInfo(deviceId,
1256 RawInputEvent.ABS_Y, "Y");
1257 absPressure = loadAbsoluteInfo(deviceId,
1258 RawInputEvent.ABS_PRESSURE, "Pressure");
1259 absSize = loadAbsoluteInfo(deviceId,
1260 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 }
1263
1264 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1265 }
1266
1267 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1268 String name) {
1269 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1270 if (getAbsoluteInfo(id, channel, info)
1271 && info.minValue != info.maxValue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001272 Slog.i(TAG, " " + name + ": min=" + info.minValue
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 + " max=" + info.maxValue
1274 + " flat=" + info.flat
1275 + " fuzz=" + info.fuzz);
1276 info.range = info.maxValue-info.minValue;
1277 return info;
1278 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001279 Slog.i(TAG, " " + name + ": unknown values");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 return null;
1281 }
1282 private static native boolean readEvent(RawInputEvent outEvent);
1283}