blob: 9874042eaeae760b9499b56017cc6ec20ae09530 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.os.SystemClock;
22import android.os.PowerManager;
23import android.util.Log;
24import android.util.SparseArray;
25import android.view.Display;
26import android.view.KeyEvent;
27import android.view.MotionEvent;
28import android.view.RawInputEvent;
29import android.view.Surface;
30import android.view.WindowManagerPolicy;
31
32public abstract class KeyInputQueue {
33 static final String TAG = "KeyInputQueue";
34
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080035 SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070036
37 int mGlobalMetaState = 0;
38 boolean mHaveGlobalMetaState = false;
39
40 final QueuedEvent mFirst;
41 final QueuedEvent mLast;
42 QueuedEvent mCache;
43 int mCacheCount;
44
45 Display mDisplay = null;
46
47 int mOrientation = Surface.ROTATION_0;
48 int[] mKeyRotationMap = null;
49
50 PowerManager.WakeLock mWakeLock;
51
52 static final int[] KEY_90_MAP = new int[] {
53 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
54 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
55 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
56 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
57 };
58
59 static final int[] KEY_180_MAP = new int[] {
60 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
61 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
62 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
63 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
64 };
65
66 static final int[] KEY_270_MAP = new int[] {
67 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
68 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
69 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
70 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
71 };
72
73 public static final int FILTER_REMOVE = 0;
74 public static final int FILTER_KEEP = 1;
75 public static final int FILTER_ABORT = -1;
76
77 public interface FilterCallback {
78 int filterEvent(QueuedEvent ev);
79 }
80
81 static class QueuedEvent {
82 InputDevice inputDevice;
83 long when;
84 int flags; // From the raw event
85 int classType; // One of the class constants in InputEvent
86 Object event;
87 boolean inQueue;
88
89 void copyFrom(QueuedEvent that) {
90 this.inputDevice = that.inputDevice;
91 this.when = that.when;
92 this.flags = that.flags;
93 this.classType = that.classType;
94 this.event = that.event;
95 }
96
97 @Override
98 public String toString() {
99 return "QueuedEvent{"
100 + Integer.toHexString(System.identityHashCode(this))
101 + " " + event + "}";
102 }
103
104 // not copied
105 QueuedEvent prev;
106 QueuedEvent next;
107 }
108
109 KeyInputQueue(Context context) {
110 PowerManager pm = (PowerManager)context.getSystemService(
111 Context.POWER_SERVICE);
112 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
113 "KeyInputQueue");
114 mWakeLock.setReferenceCounted(false);
115
116 mFirst = new QueuedEvent();
117 mLast = new QueuedEvent();
118 mFirst.next = mLast;
119 mLast.prev = mFirst;
120
121 mThread.start();
122 }
123
124 public void setDisplay(Display display) {
125 mDisplay = display;
126 }
127
128 public void getInputConfiguration(Configuration config) {
129 synchronized (mFirst) {
The Android Open Source Project9266c5582009-01-15 16:12:10 -0800130 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
131 config.keyboard = Configuration.KEYBOARD_NOKEYS;
132 config.navigation = Configuration.NAVIGATION_NONAV;
133
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700134 final int N = mDevices.size();
135 for (int i=0; i<N; i++) {
136 InputDevice d = mDevices.valueAt(i);
137 if (d != null) {
138 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
139 config.touchscreen
140 = Configuration.TOUCHSCREEN_FINGER;
141 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
142 }
The Android Open Source Project9266c5582009-01-15 16:12:10 -0800143 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
144 config.keyboard
145 = Configuration.KEYBOARD_QWERTY;
146 //Log.i("foo", "***** HAVE QWERTY!");
147 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700148 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
149 config.navigation
150 = Configuration.NAVIGATION_TRACKBALL;
151 //Log.i("foo", "***** HAVE TRACKBALL!");
152 }
153 }
154 }
155 }
156 }
157
158 public static native String getDeviceName(int deviceId);
159 public static native int getDeviceClasses(int deviceId);
160 public static native boolean getAbsoluteInfo(int deviceId, int axis,
161 InputDevice.AbsoluteInfo outInfo);
162 public static native int getSwitchState(int sw);
163 public static native int getSwitchState(int deviceId, int sw);
164 public static native int getScancodeState(int sw);
165 public static native int getScancodeState(int deviceId, int sw);
166 public static native int getKeycodeState(int sw);
167 public static native int getKeycodeState(int deviceId, int sw);
168
169 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
170 long eventTime, boolean down, int keycode, int repeatCount,
171 int scancode, int flags) {
172 return new KeyEvent(
173 downTime, eventTime,
174 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
175 keycode, repeatCount,
176 device != null ? device.mMetaKeysState : 0,
177 device != null ? device.id : -1, scancode,
178 flags);
179 }
180
181 Thread mThread = new Thread("InputDeviceReader") {
182 public void run() {
183 android.os.Process.setThreadPriority(
184 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
185
186 try {
187 RawInputEvent ev = new RawInputEvent();
188 while (true) {
189 InputDevice di;
190
191 // block, doesn't release the monitor
192 readEvent(ev);
193
194 boolean send = false;
195 boolean configChanged = false;
196
197 if (false) {
198 Log.i(TAG, "Input event: dev=0x"
199 + Integer.toHexString(ev.deviceId)
200 + " type=0x" + Integer.toHexString(ev.type)
201 + " scancode=" + ev.scancode
202 + " keycode=" + ev.keycode
203 + " value=" + ev.value);
204 }
205
206 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
207 synchronized (mFirst) {
208 di = newInputDevice(ev.deviceId);
209 mDevices.put(ev.deviceId, di);
210 configChanged = true;
211 }
212 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
213 synchronized (mFirst) {
214 Log.i(TAG, "Device removed: id=0x"
215 + Integer.toHexString(ev.deviceId));
216 di = mDevices.get(ev.deviceId);
217 if (di != null) {
218 mDevices.delete(ev.deviceId);
219 configChanged = true;
220 } else {
221 Log.w(TAG, "Bad device id: " + ev.deviceId);
222 }
223 }
224 } else {
225 di = getInputDevice(ev.deviceId);
226
227 // first crack at it
228 send = preprocessEvent(di, ev);
229
230 if (ev.type == RawInputEvent.EV_KEY) {
231 di.mMetaKeysState = makeMetaState(ev.keycode,
232 ev.value != 0, di.mMetaKeysState);
233 mHaveGlobalMetaState = false;
234 }
235 }
236
237 if (di == null) {
238 continue;
239 }
240
241 if (configChanged) {
242 synchronized (mFirst) {
243 addLocked(di, SystemClock.uptimeMillis(), 0,
244 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
245 null);
246 }
247 }
248
249 if (!send) {
250 continue;
251 }
252
253 synchronized (mFirst) {
254 // NOTE: The event timebase absolutely must be the same
255 // timebase as SystemClock.uptimeMillis().
256 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
257 final long curTime = SystemClock.uptimeMillis();
258 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
259
260 final int classes = di.classes;
261 final int type = ev.type;
262 final int scancode = ev.scancode;
263 send = false;
264
265 // Is it a key event?
266 if (type == RawInputEvent.EV_KEY &&
267 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
268 (scancode < RawInputEvent.BTN_FIRST ||
269 scancode > RawInputEvent.BTN_LAST)) {
270 boolean down;
271 if (ev.value != 0) {
272 down = true;
273 di.mDownTime = curTime;
274 } else {
275 down = false;
276 }
277 int keycode = rotateKeyCodeLocked(ev.keycode);
278 addLocked(di, curTime, ev.flags,
279 RawInputEvent.CLASS_KEYBOARD,
280 newKeyEvent(di, di.mDownTime, curTime, down,
281 keycode, 0, scancode,
282 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
283 ? KeyEvent.FLAG_WOKE_HERE : 0));
284 } else if (ev.type == RawInputEvent.EV_KEY) {
285 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
286 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
287 di.mAbs.changed = true;
288 di.mAbs.down = ev.value != 0;
289 }
290 if (ev.scancode == RawInputEvent.BTN_MOUSE &&
291 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
292 di.mRel.changed = true;
293 di.mRel.down = ev.value != 0;
294 send = true;
295 }
296
297 } else if (ev.type == RawInputEvent.EV_ABS &&
298 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
299 if (ev.scancode == RawInputEvent.ABS_X) {
300 di.mAbs.changed = true;
301 di.mAbs.x = ev.value;
302 } else if (ev.scancode == RawInputEvent.ABS_Y) {
303 di.mAbs.changed = true;
304 di.mAbs.y = ev.value;
305 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
306 di.mAbs.changed = true;
307 di.mAbs.pressure = ev.value;
308 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
309 di.mAbs.changed = true;
310 di.mAbs.size = ev.value;
311 }
312
313 } else if (ev.type == RawInputEvent.EV_REL &&
314 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
315 // Add this relative movement into our totals.
316 if (ev.scancode == RawInputEvent.REL_X) {
317 di.mRel.changed = true;
318 di.mRel.x += ev.value;
319 } else if (ev.scancode == RawInputEvent.REL_Y) {
320 di.mRel.changed = true;
321 di.mRel.y += ev.value;
322 }
323 }
324
325 if (send || ev.type == RawInputEvent.EV_SYN) {
326 if (mDisplay != null) {
327 if (!mHaveGlobalMetaState) {
328 computeGlobalMetaStateLocked();
329 }
330
331 MotionEvent me;
332 me = di.mAbs.generateMotion(di, curTime, true,
333 mDisplay, mOrientation, mGlobalMetaState);
334 if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
335 + " y=" + di.mAbs.y + " ev=" + me);
336 if (me != null) {
337 if (WindowManagerPolicy.WATCH_POINTER) {
338 Log.i(TAG, "Enqueueing: " + me);
339 }
340 addLocked(di, curTime, ev.flags,
341 RawInputEvent.CLASS_TOUCHSCREEN, me);
342 }
343 me = di.mRel.generateMotion(di, curTime, false,
344 mDisplay, mOrientation, mGlobalMetaState);
345 if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
346 + " y=" + di.mRel.y + " ev=" + me);
347 if (me != null) {
348 addLocked(di, curTime, ev.flags,
349 RawInputEvent.CLASS_TRACKBALL, me);
350 }
351 }
352 }
353 }
354 }
355 }
356 catch (RuntimeException exc) {
357 Log.e(TAG, "InputReaderThread uncaught exception", exc);
358 }
359 }
360 };
361
362 /**
363 * Returns a new meta state for the given keys and old state.
364 */
365 private static final int makeMetaState(int keycode, boolean down, int old) {
366 int mask;
367 switch (keycode) {
368 case KeyEvent.KEYCODE_ALT_LEFT:
369 mask = KeyEvent.META_ALT_LEFT_ON;
370 break;
371 case KeyEvent.KEYCODE_ALT_RIGHT:
372 mask = KeyEvent.META_ALT_RIGHT_ON;
373 break;
374 case KeyEvent.KEYCODE_SHIFT_LEFT:
375 mask = KeyEvent.META_SHIFT_LEFT_ON;
376 break;
377 case KeyEvent.KEYCODE_SHIFT_RIGHT:
378 mask = KeyEvent.META_SHIFT_RIGHT_ON;
379 break;
380 case KeyEvent.KEYCODE_SYM:
381 mask = KeyEvent.META_SYM_ON;
382 break;
383 default:
384 return old;
385 }
386 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
387 & (down ? (old | mask) : (old & ~mask));
388 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
389 result |= KeyEvent.META_ALT_ON;
390 }
391 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
392 result |= KeyEvent.META_SHIFT_ON;
393 }
394 return result;
395 }
396
397 private void computeGlobalMetaStateLocked() {
398 int i = mDevices.size();
399 mGlobalMetaState = 0;
400 while ((--i) >= 0) {
401 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
402 }
403 mHaveGlobalMetaState = true;
404 }
405
406 /*
407 * Return true if you want the event to get passed on to the
408 * rest of the system, and false if you've handled it and want
409 * it dropped.
410 */
411 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
412
413 InputDevice getInputDevice(int deviceId) {
414 synchronized (mFirst) {
415 return getInputDeviceLocked(deviceId);
416 }
417 }
418
419 private InputDevice getInputDeviceLocked(int deviceId) {
420 return mDevices.get(deviceId);
421 }
422
423 public void setOrientation(int orientation) {
424 synchronized(mFirst) {
425 mOrientation = orientation;
426 switch (orientation) {
427 case Surface.ROTATION_90:
428 mKeyRotationMap = KEY_90_MAP;
429 break;
430 case Surface.ROTATION_180:
431 mKeyRotationMap = KEY_180_MAP;
432 break;
433 case Surface.ROTATION_270:
434 mKeyRotationMap = KEY_270_MAP;
435 break;
436 default:
437 mKeyRotationMap = null;
438 break;
439 }
440 }
441 }
442
443 public int rotateKeyCode(int keyCode) {
444 synchronized(mFirst) {
445 return rotateKeyCodeLocked(keyCode);
446 }
447 }
448
449 private int rotateKeyCodeLocked(int keyCode) {
450 int[] map = mKeyRotationMap;
451 if (map != null) {
452 final int N = map.length;
453 for (int i=0; i<N; i+=2) {
454 if (map[i] == keyCode) {
455 return map[i+1];
456 }
457 }
458 }
459 return keyCode;
460 }
461
462 boolean hasEvents() {
463 synchronized (mFirst) {
464 return mFirst.next != mLast;
465 }
466 }
467
468 /*
469 * returns true if we returned an event, and false if we timed out
470 */
471 QueuedEvent getEvent(long timeoutMS) {
472 long begin = SystemClock.uptimeMillis();
473 final long end = begin+timeoutMS;
474 long now = begin;
475 synchronized (mFirst) {
476 while (mFirst.next == mLast && end > now) {
477 try {
478 mWakeLock.release();
479 mFirst.wait(end-now);
480 }
481 catch (InterruptedException e) {
482 }
483 now = SystemClock.uptimeMillis();
484 if (begin > now) {
485 begin = now;
486 }
487 }
488 if (mFirst.next == mLast) {
489 return null;
490 }
491 QueuedEvent p = mFirst.next;
492 mFirst.next = p.next;
493 mFirst.next.prev = mFirst;
494 p.inQueue = false;
495 return p;
496 }
497 }
498
499 void recycleEvent(QueuedEvent ev) {
500 synchronized (mFirst) {
501 //Log.i(TAG, "Recycle event: " + ev);
502 if (ev.event == ev.inputDevice.mAbs.currentMove) {
503 ev.inputDevice.mAbs.currentMove = null;
504 }
505 if (ev.event == ev.inputDevice.mRel.currentMove) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800506 if (false) Log.i(TAG, "Detach rel " + ev.event);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700507 ev.inputDevice.mRel.currentMove = null;
508 ev.inputDevice.mRel.x = 0;
509 ev.inputDevice.mRel.y = 0;
510 }
511 recycleLocked(ev);
512 }
513 }
514
515 void filterQueue(FilterCallback cb) {
516 synchronized (mFirst) {
517 QueuedEvent cur = mLast.prev;
518 while (cur.prev != null) {
519 switch (cb.filterEvent(cur)) {
520 case FILTER_REMOVE:
521 cur.prev.next = cur.next;
522 cur.next.prev = cur.prev;
523 break;
524 case FILTER_ABORT:
525 return;
526 }
527 cur = cur.prev;
528 }
529 }
530 }
531
532 private QueuedEvent obtainLocked(InputDevice device, long when,
533 int flags, int classType, Object event) {
534 QueuedEvent ev;
535 if (mCacheCount == 0) {
536 ev = new QueuedEvent();
537 } else {
538 ev = mCache;
539 ev.inQueue = false;
540 mCache = ev.next;
541 mCacheCount--;
542 }
543 ev.inputDevice = device;
544 ev.when = when;
545 ev.flags = flags;
546 ev.classType = classType;
547 ev.event = event;
548 return ev;
549 }
550
551 private void recycleLocked(QueuedEvent ev) {
552 if (ev.inQueue) {
553 throw new RuntimeException("Event already in queue!");
554 }
555 if (mCacheCount < 10) {
556 mCacheCount++;
557 ev.next = mCache;
558 mCache = ev;
559 ev.inQueue = true;
560 }
561 }
562
563 private void addLocked(InputDevice device, long when, int flags,
564 int classType, Object event) {
565 boolean poke = mFirst.next == mLast;
566
567 QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
568 QueuedEvent p = mLast.prev;
569 while (p != mFirst && ev.when < p.when) {
570 p = p.prev;
571 }
572
573 ev.next = p.next;
574 ev.prev = p;
575 p.next = ev;
576 ev.next.prev = ev;
577 ev.inQueue = true;
578
579 if (poke) {
580 mFirst.notify();
581 mWakeLock.acquire();
582 }
583 }
584
585 private InputDevice newInputDevice(int deviceId) {
586 int classes = getDeviceClasses(deviceId);
587 String name = getDeviceName(deviceId);
588 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
589 + ", name=" + name
590 + ", classes=" + Integer.toHexString(classes));
591 InputDevice.AbsoluteInfo absX;
592 InputDevice.AbsoluteInfo absY;
593 InputDevice.AbsoluteInfo absPressure;
594 InputDevice.AbsoluteInfo absSize;
595 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
596 absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
597 absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
598 absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
599 absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
600 } else {
601 absX = null;
602 absY = null;
603 absPressure = null;
604 absSize = null;
605 }
606
607 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
608 }
609
610 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
611 String name) {
612 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
613 if (getAbsoluteInfo(id, channel, info)
614 && info.minValue != info.maxValue) {
615 Log.i(TAG, " " + name + ": min=" + info.minValue
616 + " max=" + info.maxValue
617 + " flat=" + info.flat
618 + " fuzz=" + info.fuzz);
619 info.range = info.maxValue-info.minValue;
620 return info;
621 }
622 Log.i(TAG, " " + name + ": unknown values");
623 return null;
624 }
625 private static native boolean readEvent(RawInputEvent outEvent);
626}