blob: a08258ac0b63666688f9b636f28329e4adb8c5b0 [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;
Dianne Hackborna2e92262010-03-02 17:19:29 -080025import android.os.SystemProperties;
Joe Onorato8a9b2202010-02-26 18:56:32 -080026import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.util.SparseArray;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040028import android.util.Xml;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.view.Display;
30import android.view.KeyEvent;
31import android.view.MotionEvent;
32import android.view.RawInputEvent;
33import android.view.Surface;
34import android.view.WindowManagerPolicy;
35
Dianne Hackborn2269d1572010-02-24 19:54:22 -080036import com.android.internal.util.XmlUtils;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040037
38import org.xmlpull.v1.XmlPullParser;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040039
Dianne Hackborne3dd8842009-07-14 12:06:54 -070040import java.io.BufferedReader;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040041import java.io.File;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070042import java.io.FileInputStream;
43import java.io.FileNotFoundException;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040044import java.io.FileReader;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070045import java.io.IOException;
46import java.io.InputStreamReader;
Dianne Hackborna2e92262010-03-02 17:19:29 -080047import java.io.PrintWriter;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070048import java.util.ArrayList;
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050public abstract class KeyInputQueue {
51 static final String TAG = "KeyInputQueue";
52
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -070053 static final boolean DEBUG = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070054 static final boolean DEBUG_VIRTUAL_KEYS = false;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -070055 static final boolean DEBUG_POINTERS = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070056
Dianne Hackborn1411d1c2009-10-12 23:21:18 -070057 /**
58 * Turn on some hacks we have to improve the touch interaction with a
59 * certain device whose screen currently is not all that good.
60 */
Dianne Hackborn65cb6052009-11-10 17:06:22 -080061 static boolean BAD_TOUCH_HACK = false;
Dianne Hackborn1411d1c2009-10-12 23:21:18 -070062
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040063 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
64
Dianne Hackborne3dd8842009-07-14 12:06:54 -070065 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
Dianne Hackborna8f60182009-09-01 19:01:50 -070066 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
Dianne Hackborne3dd8842009-07-14 12:06:54 -070067 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070068 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
70 int mGlobalMetaState = 0;
71 boolean mHaveGlobalMetaState = false;
72
73 final QueuedEvent mFirst;
74 final QueuedEvent mLast;
75 QueuedEvent mCache;
76 int mCacheCount;
77
78 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070079 int mDisplayWidth;
80 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82 int mOrientation = Surface.ROTATION_0;
83 int[] mKeyRotationMap = null;
84
Dianne Hackborne3dd8842009-07-14 12:06:54 -070085 VirtualKey mPressedVirtualKey = null;
86
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 PowerManager.WakeLock mWakeLock;
88
89 static final int[] KEY_90_MAP = new int[] {
90 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
91 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
92 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
93 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
94 };
95
96 static final int[] KEY_180_MAP = new int[] {
97 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
98 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
99 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
100 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
101 };
102
103 static final int[] KEY_270_MAP = new int[] {
104 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
105 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
106 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
107 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
108 };
109
110 public static final int FILTER_REMOVE = 0;
111 public static final int FILTER_KEEP = 1;
112 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700113
114 private static final boolean MEASURE_LATENCY = false;
115 private LatencyTimer lt;
116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 public interface FilterCallback {
118 int filterEvent(QueuedEvent ev);
119 }
120
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700121 public interface HapticFeedbackCallback {
122 void virtualKeyFeedback(KeyEvent event);
123 }
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 static class QueuedEvent {
126 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700127 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 int flags; // From the raw event
129 int classType; // One of the class constants in InputEvent
130 Object event;
131 boolean inQueue;
132
133 void copyFrom(QueuedEvent that) {
134 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700135 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 this.flags = that.flags;
137 this.classType = that.classType;
138 this.event = that.event;
139 }
140
141 @Override
142 public String toString() {
143 return "QueuedEvent{"
144 + Integer.toHexString(System.identityHashCode(this))
145 + " " + event + "}";
146 }
147
148 // not copied
149 QueuedEvent prev;
150 QueuedEvent next;
151 }
152
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700153 /**
154 * A key that exists as a part of the touch-screen, outside of the normal
155 * display area of the screen.
156 */
157 static class VirtualKey {
158 int scancode;
159 int centerx;
160 int centery;
161 int width;
162 int height;
163
164 int hitLeft;
165 int hitTop;
166 int hitRight;
167 int hitBottom;
168
169 InputDevice lastDevice;
170 int lastKeycode;
171
172 boolean checkHit(int x, int y) {
173 return (x >= hitLeft && x <= hitRight
174 && y >= hitTop && y <= hitBottom);
175 }
176
177 void computeHitRect(InputDevice dev, int dw, int dh) {
178 if (dev == lastDevice) {
179 return;
180 }
181
Joe Onorato8a9b2202010-02-26 18:56:32 -0800182 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700183 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
184
185 lastDevice = dev;
186
187 int minx = dev.absX.minValue;
188 int maxx = dev.absX.maxValue;
189
190 int halfw = width/2;
191 int left = centerx - halfw;
192 int right = centerx + halfw;
193 hitLeft = minx + ((left*maxx-minx)/dw);
194 hitRight = minx + ((right*maxx-minx)/dw);
195
196 int miny = dev.absY.minValue;
197 int maxy = dev.absY.maxValue;
198
199 int halfh = height/2;
200 int top = centery - halfh;
201 int bottom = centery + halfh;
202 hitTop = miny + ((top*maxy-miny)/dh);
203 hitBottom = miny + ((bottom*maxy-miny)/dh);
204 }
205 }
Michael Chan53071d62009-05-13 17:29:48 -0700206
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700207 private void readVirtualKeys(String deviceName) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700208 try {
209 FileInputStream fis = new FileInputStream(
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700210 "/sys/board_properties/virtualkeys." + deviceName);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700211 InputStreamReader isr = new InputStreamReader(fis);
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700212 BufferedReader br = new BufferedReader(isr, 2048);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700213 String str = br.readLine();
214 if (str != null) {
215 String[] it = str.split(":");
Joe Onorato8a9b2202010-02-26 18:56:32 -0800216 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700217 final int N = it.length-6;
218 for (int i=0; i<=N; i+=6) {
219 if (!"0x01".equals(it[i])) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800220 Slog.w(TAG, "Unknown virtual key type at elem #" + i
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700221 + ": " + it[i]);
222 continue;
223 }
224 try {
225 VirtualKey sb = new VirtualKey();
226 sb.scancode = Integer.parseInt(it[i+1]);
227 sb.centerx = Integer.parseInt(it[i+2]);
228 sb.centery = Integer.parseInt(it[i+3]);
229 sb.width = Integer.parseInt(it[i+4]);
230 sb.height = Integer.parseInt(it[i+5]);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800231 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700232 + sb.scancode + ": center=" + sb.centerx + ","
233 + sb.centery + " size=" + sb.width + "x"
234 + sb.height);
235 mVirtualKeys.add(sb);
236 } catch (NumberFormatException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800237 Slog.w(TAG, "Bad number at region " + i + " in: "
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700238 + str, e);
239 }
240 }
241 }
242 br.close();
243 } catch (FileNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800244 Slog.i(TAG, "No virtual keys found");
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700245 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800246 Slog.w(TAG, "Error reading virtual keys", e);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700247 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400248 }
249
250 private void readExcludedDevices() {
251 // Read partner-provided list of excluded input devices
252 XmlPullParser parser = null;
253 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
254 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
255 FileReader confreader = null;
256 try {
257 confreader = new FileReader(confFile);
258 parser = Xml.newPullParser();
259 parser.setInput(confreader);
260 XmlUtils.beginDocument(parser, "devices");
261
262 while (true) {
263 XmlUtils.nextElement(parser);
264 if (!"device".equals(parser.getName())) {
265 break;
266 }
267 String name = parser.getAttributeValue(null, "name");
268 if (name != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800269 if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400270 addExcludedDevice(name);
271 }
272 }
273 } catch (FileNotFoundException e) {
274 // It's ok if the file does not exist.
275 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800276 Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400277 } finally {
278 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
279 }
280 }
281
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700282 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400283 if (MEASURE_LATENCY) {
284 lt = new LatencyTimer(100, 1000);
285 }
286
Dianne Hackborn65cb6052009-11-10 17:06:22 -0800287 BAD_TOUCH_HACK = context.getResources().getBoolean(
288 com.android.internal.R.bool.config_filterTouchEvents);
289
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700290 mHapticFeedbackCallback = hapticFeedbackCallback;
291
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400292 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 PowerManager pm = (PowerManager)context.getSystemService(
295 Context.POWER_SERVICE);
296 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
297 "KeyInputQueue");
298 mWakeLock.setReferenceCounted(false);
299
300 mFirst = new QueuedEvent();
301 mLast = new QueuedEvent();
302 mFirst.next = mLast;
303 mLast.prev = mFirst;
304
305 mThread.start();
306 }
307
308 public void setDisplay(Display display) {
309 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700310
311 // We assume at this point that the display dimensions reflect the
312 // natural, unrotated display. We will perform hit tests for soft
313 // buttons based on that display.
314 mDisplayWidth = display.getWidth();
315 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317
318 public void getInputConfiguration(Configuration config) {
319 synchronized (mFirst) {
320 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
321 config.keyboard = Configuration.KEYBOARD_NOKEYS;
322 config.navigation = Configuration.NAVIGATION_NONAV;
323
324 final int N = mDevices.size();
325 for (int i=0; i<N; i++) {
326 InputDevice d = mDevices.valueAt(i);
327 if (d != null) {
328 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
329 config.touchscreen
330 = Configuration.TOUCHSCREEN_FINGER;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800331 //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 }
333 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
334 config.keyboard
335 = Configuration.KEYBOARD_QWERTY;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800336 //Slog.i("foo", "***** HAVE QWERTY!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
338 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
339 config.navigation
340 = Configuration.NAVIGATION_TRACKBALL;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800341 //Slog.i("foo", "***** HAVE TRACKBALL!");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700342 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
343 config.navigation
344 = Configuration.NAVIGATION_DPAD;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800345 //Slog.i("foo", "***** HAVE DPAD!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
347 }
348 }
349 }
350 }
351
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700352 public int getScancodeState(int code) {
353 synchronized (mFirst) {
354 VirtualKey vk = mPressedVirtualKey;
355 if (vk != null) {
356 if (vk.scancode == code) {
357 return 2;
358 }
359 }
360 return nativeGetScancodeState(code);
361 }
362 }
363
364 public int getScancodeState(int deviceId, int code) {
365 synchronized (mFirst) {
366 VirtualKey vk = mPressedVirtualKey;
367 if (vk != null) {
368 if (vk.scancode == code) {
369 return 2;
370 }
371 }
372 return nativeGetScancodeState(deviceId, code);
373 }
374 }
375
Dianne Hackborn1d62ea92009-11-17 12:49:50 -0800376 public int getTrackballScancodeState(int code) {
377 synchronized (mFirst) {
378 final int N = mDevices.size();
379 for (int i=0; i<N; i++) {
380 InputDevice dev = mDevices.valueAt(i);
381 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
382 int res = nativeGetScancodeState(dev.id, code);
383 if (res > 0) {
384 return res;
385 }
386 }
387 }
388 }
389
390 return 0;
391 }
392
393 public int getDPadScancodeState(int code) {
394 synchronized (mFirst) {
395 final int N = mDevices.size();
396 for (int i=0; i<N; i++) {
397 InputDevice dev = mDevices.valueAt(i);
398 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
399 int res = nativeGetScancodeState(dev.id, code);
400 if (res > 0) {
401 return res;
402 }
403 }
404 }
405 }
406
407 return 0;
408 }
409
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700410 public int getKeycodeState(int code) {
411 synchronized (mFirst) {
412 VirtualKey vk = mPressedVirtualKey;
413 if (vk != null) {
414 if (vk.lastKeycode == code) {
415 return 2;
416 }
417 }
418 return nativeGetKeycodeState(code);
419 }
420 }
421
422 public int getKeycodeState(int deviceId, int code) {
423 synchronized (mFirst) {
424 VirtualKey vk = mPressedVirtualKey;
425 if (vk != null) {
426 if (vk.lastKeycode == code) {
427 return 2;
428 }
429 }
430 return nativeGetKeycodeState(deviceId, code);
431 }
432 }
433
Dianne Hackborn1d62ea92009-11-17 12:49:50 -0800434 public int getTrackballKeycodeState(int code) {
435 synchronized (mFirst) {
436 final int N = mDevices.size();
437 for (int i=0; i<N; i++) {
438 InputDevice dev = mDevices.valueAt(i);
439 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
440 int res = nativeGetKeycodeState(dev.id, code);
441 if (res > 0) {
442 return res;
443 }
444 }
445 }
446 }
447
448 return 0;
449 }
450
451 public int getDPadKeycodeState(int code) {
452 synchronized (mFirst) {
453 final int N = mDevices.size();
454 for (int i=0; i<N; i++) {
455 InputDevice dev = mDevices.valueAt(i);
456 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
457 int res = nativeGetKeycodeState(dev.id, code);
458 if (res > 0) {
459 return res;
460 }
461 }
462 }
463 }
464
465 return 0;
466 }
467
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 public static native String getDeviceName(int deviceId);
469 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400470 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 public static native boolean getAbsoluteInfo(int deviceId, int axis,
472 InputDevice.AbsoluteInfo outInfo);
473 public static native int getSwitchState(int sw);
474 public static native int getSwitchState(int deviceId, int sw);
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700475 public static native int nativeGetScancodeState(int code);
476 public static native int nativeGetScancodeState(int deviceId, int code);
477 public static native int nativeGetKeycodeState(int code);
478 public static native int nativeGetKeycodeState(int deviceId, int code);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700479 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
481
482 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
483 long eventTime, boolean down, int keycode, int repeatCount,
484 int scancode, int flags) {
485 return new KeyEvent(
486 downTime, eventTime,
487 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
488 keycode, repeatCount,
489 device != null ? device.mMetaKeysState : 0,
490 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700491 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 }
493
494 Thread mThread = new Thread("InputDeviceReader") {
495 public void run() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800496 if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 android.os.Process.setThreadPriority(
498 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
499
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700500 RawInputEvent ev = new RawInputEvent();
501 while (true) {
502 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 InputDevice di;
504
505 // block, doesn't release the monitor
506 readEvent(ev);
507
508 boolean send = false;
509 boolean configChanged = false;
510
511 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800512 Slog.i(TAG, "Input event: dev=0x"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 + Integer.toHexString(ev.deviceId)
514 + " type=0x" + Integer.toHexString(ev.type)
515 + " scancode=" + ev.scancode
516 + " keycode=" + ev.keycode
517 + " value=" + ev.value);
518 }
519
520 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
521 synchronized (mFirst) {
522 di = newInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700523 if (di.classes != 0) {
524 // If this device is some kind of input class,
525 // we care about it.
526 mDevices.put(ev.deviceId, di);
527 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
528 readVirtualKeys(di.name);
529 }
530 // The configuration may have changed because
531 // of this device.
532 configChanged = true;
533 } else {
534 // We won't do anything with this device.
535 mIgnoredDevices.put(ev.deviceId, di);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800536 Slog.i(TAG, "Ignoring non-input device: id=0x"
Dianne Hackborna8f60182009-09-01 19:01:50 -0700537 + Integer.toHexString(di.id)
538 + ", name=" + di.name);
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700539 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 }
541 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
542 synchronized (mFirst) {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700543 if (false) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800544 Slog.i(TAG, "Device removed: id=0x"
Dianne Hackborna8f60182009-09-01 19:01:50 -0700545 + Integer.toHexString(ev.deviceId));
546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 di = mDevices.get(ev.deviceId);
548 if (di != null) {
549 mDevices.delete(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700550 // The configuration may have changed because
551 // of this device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 configChanged = true;
Dianne Hackborna8f60182009-09-01 19:01:50 -0700553 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
554 mIgnoredDevices.remove(ev.deviceId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800556 Slog.w(TAG, "Removing bad device id: "
Dianne Hackborna8f60182009-09-01 19:01:50 -0700557 + Integer.toHexString(ev.deviceId));
558 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 }
560 }
561 } else {
562 di = getInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700563 if (di == null) {
564 // This may be some junk from an ignored device.
565 continue;
566 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567
568 // first crack at it
569 send = preprocessEvent(di, ev);
570
571 if (ev.type == RawInputEvent.EV_KEY) {
572 di.mMetaKeysState = makeMetaState(ev.keycode,
573 ev.value != 0, di.mMetaKeysState);
574 mHaveGlobalMetaState = false;
575 }
576 }
577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 if (configChanged) {
579 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700580 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
582 null);
583 }
584 }
585
586 if (!send) {
587 continue;
588 }
589
590 synchronized (mFirst) {
591 // NOTE: The event timebase absolutely must be the same
592 // timebase as SystemClock.uptimeMillis().
593 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
594 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700595 final long curTimeNano = System.nanoTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800596 //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597
598 final int classes = di.classes;
599 final int type = ev.type;
600 final int scancode = ev.scancode;
601 send = false;
602
603 // Is it a key event?
604 if (type == RawInputEvent.EV_KEY &&
605 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
606 (scancode < RawInputEvent.BTN_FIRST ||
607 scancode > RawInputEvent.BTN_LAST)) {
608 boolean down;
609 if (ev.value != 0) {
610 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700611 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 } else {
613 down = false;
614 }
615 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700616 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700618 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 keycode, 0, scancode,
620 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
621 ? KeyEvent.FLAG_WOKE_HERE : 0));
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 } else if (ev.type == RawInputEvent.EV_KEY) {
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700624 // Single touch protocol: touch going down or up.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700626 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
627 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
628 == RawInputEvent.CLASS_TOUCHSCREEN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700630 di.mAbs.mDown[0] = ev.value != 0;
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700631
632 // Trackball (mouse) protocol: press down or up.
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700633 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
635 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700636 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 send = true;
638 }
639
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700640 // Process position events from multitouch protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 } else if (ev.type == RawInputEvent.EV_ABS &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700642 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
643 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
644 di.mAbs.changed = true;
645 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
646 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
647 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
648 di.mAbs.changed = true;
649 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
650 + MotionEvent.SAMPLE_X] = ev.value;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800651 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700652 + di.mAbs.mAddingPointerOffset
653 + " X:" + ev.value);
654 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
655 di.mAbs.changed = true;
656 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
657 + MotionEvent.SAMPLE_Y] = ev.value;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800658 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700659 + di.mAbs.mAddingPointerOffset
660 + " Y:" + ev.value);
661 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
662 di.mAbs.changed = true;
663 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
664 + MotionEvent.SAMPLE_SIZE] = ev.value;
665 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700666
667 // Process position events from single touch protocol.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700668 } else if (ev.type == RawInputEvent.EV_ABS &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
670 if (ev.scancode == RawInputEvent.ABS_X) {
671 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700672 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 } else if (ev.scancode == RawInputEvent.ABS_Y) {
674 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700675 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
677 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700678 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
679 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700680 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
682 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700683 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
684 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700685 + MotionEvent.SAMPLE_SIZE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 }
687
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700688 // Process movement events from trackball (mouse) protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 } else if (ev.type == RawInputEvent.EV_REL &&
690 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
691 // Add this relative movement into our totals.
692 if (ev.scancode == RawInputEvent.REL_X) {
693 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700694 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 } else if (ev.scancode == RawInputEvent.REL_Y) {
696 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700697 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 }
699 }
700
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700701 // Handle multitouch protocol sync: tells us that the
702 // driver has returned all data for -one- of the pointers
703 // that is currently down.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700704 if (ev.type == RawInputEvent.EV_SYN
705 && ev.scancode == RawInputEvent.SYN_MT_REPORT
706 && di.mAbs != null) {
707 di.mAbs.changed = true;
708 if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
709 // If the value is <= 0, the pointer is not
710 // down, so keep it in the count.
711
712 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
713 + MotionEvent.SAMPLE_PRESSURE] != 0) {
714 final int num = di.mAbs.mNextNumPointers+1;
715 di.mAbs.mNextNumPointers = num;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800716 if (DEBUG_POINTERS) Slog.v(TAG,
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700717 "MT_REPORT: now have " + num + " pointers");
718 final int newOffset = (num <= InputDevice.MAX_POINTERS)
719 ? (num * MotionEvent.NUM_SAMPLE_DATA)
720 : (InputDevice.MAX_POINTERS *
721 MotionEvent.NUM_SAMPLE_DATA);
722 di.mAbs.mAddingPointerOffset = newOffset;
723 di.mAbs.mNextData[newOffset
724 + MotionEvent.SAMPLE_PRESSURE] = 0;
725 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800726 if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700727 }
728 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700729
730 // Handle general event sync: all data for the current
731 // event update has been delivered.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700732 } else if (send || (ev.type == RawInputEvent.EV_SYN
733 && ev.scancode == RawInputEvent.SYN_REPORT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 if (mDisplay != null) {
735 if (!mHaveGlobalMetaState) {
736 computeGlobalMetaStateLocked();
737 }
738
739 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700740
741 InputDevice.MotionState ms = di.mAbs;
742 if (ms.changed) {
Dianne Hackborna2e92262010-03-02 17:19:29 -0800743 ms.everChanged = true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700744 ms.changed = false;
745
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700746 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
747 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
748 == RawInputEvent.CLASS_TOUCHSCREEN) {
749 ms.mNextNumPointers = 0;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700750 if (ms.mDown[0]) {
751 System.arraycopy(di.curTouchVals, 0,
752 ms.mNextData, 0,
753 MotionEvent.NUM_SAMPLE_DATA);
754 ms.mNextNumPointers++;
755 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700756 }
757
758 if (BAD_TOUCH_HACK) {
759 ms.dropBadPoint(di);
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700760 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700761
762 boolean doMotion = !monitorVirtualKey(di,
763 ev, curTime, curTimeNano);
764
765 if (doMotion && ms.mNextNumPointers > 0
Dianne Hackborndc953722009-10-19 11:24:39 -0700766 && (ms.mLastNumPointers == 0
767 || ms.mSkipLastPointers)) {
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700768 doMotion = !generateVirtualKeyDown(di,
769 ev, curTime, curTimeNano);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700771
772 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700773 // XXX Need to be able to generate
774 // multiple events here, for example
775 // if two fingers change up/down state
776 // at the same time.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700777 do {
778 me = ms.generateAbsMotion(di, curTime,
779 curTimeNano, mDisplay,
780 mOrientation, mGlobalMetaState);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800781 if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700782 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
783 + " y="
784 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
785 + " ev=" + me);
786 if (me != null) {
787 if (WindowManagerPolicy.WATCH_POINTER) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800788 Slog.i(TAG, "Enqueueing: " + me);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700789 }
790 addLocked(di, curTimeNano, ev.flags,
791 RawInputEvent.CLASS_TOUCHSCREEN, me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700792 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700793 } while (ms.hasMore());
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700794 } else {
795 // We are consuming movement in the
796 // virtual key area... but still
797 // propagate this to the previous
798 // data for comparisons.
Dianne Hackbornf2ddfb12009-10-13 22:43:33 -0700799 int num = ms.mNextNumPointers;
800 if (num > InputDevice.MAX_POINTERS) {
801 num = InputDevice.MAX_POINTERS;
802 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700803 System.arraycopy(ms.mNextData, 0,
804 ms.mLastData, 0,
Dianne Hackbornf2ddfb12009-10-13 22:43:33 -0700805 num * MotionEvent.NUM_SAMPLE_DATA);
806 ms.mLastNumPointers = num;
Dianne Hackborndc953722009-10-19 11:24:39 -0700807 ms.mSkipLastPointers = true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700808 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700809
810 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700812
813 ms = di.mRel;
814 if (ms.changed) {
Dianne Hackborna2e92262010-03-02 17:19:29 -0800815 ms.everChanged = true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700816 ms.changed = false;
817
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700818 me = ms.generateRelMotion(di, curTime,
819 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700820 mOrientation, mGlobalMetaState);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800821 if (false) Slog.v(TAG, "Relative: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700822 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700823 + " y="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700824 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700825 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700826 if (me != null) {
827 addLocked(di, curTimeNano, ev.flags,
828 RawInputEvent.CLASS_TRACKBALL, me);
829 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700830
831 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
833 }
834 }
835 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700836
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700837 } catch (RuntimeException exc) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800838 Slog.e(TAG, "InputReaderThread uncaught exception", exc);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 }
841 }
842 };
843
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700844 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700845 final InputDevice.AbsoluteInfo absx = dev.absX;
846 final InputDevice.AbsoluteInfo absy = dev.absY;
847 final InputDevice.MotionState absm = dev.mAbs;
848 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700849 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700850 }
851
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700852 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
853 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
854 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
855 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800856 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700857 + absm.mNextData[MotionEvent.SAMPLE_X]
858 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700859 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700860 return true;
861 }
862
863 return false;
864 }
865
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700866 private VirtualKey findVirtualKey(InputDevice dev) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700867 final int N = mVirtualKeys.size();
868 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700869 return null;
870 }
871
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700872 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700873 for (int i=0; i<N; i++) {
874 VirtualKey sb = mVirtualKeys.get(i);
875 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800876 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700877 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
878 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700879 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700880 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
881 + sb.hitBottom + ")");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700882 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
883 absm.mNextData[MotionEvent.SAMPLE_Y])) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800884 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700885 return sb;
886 }
887 }
888
889 return null;
890 }
891
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700892 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
893 long curTime, long curTimeNano) {
894 if (isInsideDisplay(di)) {
895 // Didn't consume event.
896 return false;
897 }
898
899
900 VirtualKey vk = findVirtualKey(di);
901 if (vk != null) {
902 final InputDevice.MotionState ms = di.mAbs;
903 mPressedVirtualKey = vk;
904 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
905 ms.mLastNumPointers = ms.mNextNumPointers;
906 di.mKeyDownTime = curTime;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800907 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700908 "Generate key down for: " + vk.scancode
909 + " (keycode=" + vk.lastKeycode + ")");
910 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
911 vk.lastKeycode, 0, vk.scancode,
912 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
913 mHapticFeedbackCallback.virtualKeyFeedback(event);
914 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
915 event);
916 }
917
918 // We always consume the event, even if we didn't
919 // generate a key event. There are two reasons for
920 // this: to avoid spurious touches when holding
921 // the edges of the device near the touchscreen,
922 // and to avoid reporting events if there are virtual
923 // keys on the touchscreen outside of the display
924 // area.
925 // Note that for all of this we are only looking at the
926 // first pointer, since what we are handling here is the
927 // first pointer going down, and this is the coordinate
928 // that will be used to dispatch the event.
929 if (false) {
930 final InputDevice.AbsoluteInfo absx = di.absX;
931 final InputDevice.AbsoluteInfo absy = di.absY;
932 final InputDevice.MotionState absm = di.mAbs;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800933 Slog.v(TAG, "Rejecting ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700934 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
935 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
936 + absx.minValue + "," + absy.minValue
937 + ")-(" + absx.maxValue + ","
938 + absx.maxValue + ")");
939 }
940 return true;
941 }
942
943 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
944 long curTime, long curTimeNano) {
945 VirtualKey vk = mPressedVirtualKey;
946 if (vk == null) {
947 return false;
948 }
949
950 final InputDevice.MotionState ms = di.mAbs;
951 if (ms.mNextNumPointers <= 0) {
952 mPressedVirtualKey = null;
953 ms.mLastNumPointers = 0;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800954 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700955 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
956 vk.lastKeycode, 0, vk.scancode,
957 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
958 mHapticFeedbackCallback.virtualKeyFeedback(event);
959 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
960 event);
961 return true;
962
963 } else if (isInsideDisplay(di)) {
964 // Whoops the pointer has moved into
965 // the display area! Cancel the
966 // virtual key and start a pointer
967 // motion.
968 mPressedVirtualKey = null;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800969 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700970 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
971 vk.lastKeycode, 0, vk.scancode,
972 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
973 mHapticFeedbackCallback.virtualKeyFeedback(event);
974 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
975 event);
976 ms.mLastNumPointers = 0;
977 return false;
978 }
979
980 return true;
981 }
982
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 /**
984 * Returns a new meta state for the given keys and old state.
985 */
986 private static final int makeMetaState(int keycode, boolean down, int old) {
987 int mask;
988 switch (keycode) {
989 case KeyEvent.KEYCODE_ALT_LEFT:
990 mask = KeyEvent.META_ALT_LEFT_ON;
991 break;
992 case KeyEvent.KEYCODE_ALT_RIGHT:
993 mask = KeyEvent.META_ALT_RIGHT_ON;
994 break;
995 case KeyEvent.KEYCODE_SHIFT_LEFT:
996 mask = KeyEvent.META_SHIFT_LEFT_ON;
997 break;
998 case KeyEvent.KEYCODE_SHIFT_RIGHT:
999 mask = KeyEvent.META_SHIFT_RIGHT_ON;
1000 break;
1001 case KeyEvent.KEYCODE_SYM:
1002 mask = KeyEvent.META_SYM_ON;
1003 break;
1004 default:
1005 return old;
1006 }
1007 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
1008 & (down ? (old | mask) : (old & ~mask));
1009 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
1010 result |= KeyEvent.META_ALT_ON;
1011 }
1012 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
1013 result |= KeyEvent.META_SHIFT_ON;
1014 }
1015 return result;
1016 }
1017
1018 private void computeGlobalMetaStateLocked() {
1019 int i = mDevices.size();
1020 mGlobalMetaState = 0;
1021 while ((--i) >= 0) {
1022 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
1023 }
1024 mHaveGlobalMetaState = true;
1025 }
1026
1027 /*
1028 * Return true if you want the event to get passed on to the
1029 * rest of the system, and false if you've handled it and want
1030 * it dropped.
1031 */
1032 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
1033
1034 InputDevice getInputDevice(int deviceId) {
1035 synchronized (mFirst) {
1036 return getInputDeviceLocked(deviceId);
1037 }
1038 }
1039
1040 private InputDevice getInputDeviceLocked(int deviceId) {
1041 return mDevices.get(deviceId);
1042 }
1043
1044 public void setOrientation(int orientation) {
1045 synchronized(mFirst) {
1046 mOrientation = orientation;
1047 switch (orientation) {
1048 case Surface.ROTATION_90:
1049 mKeyRotationMap = KEY_90_MAP;
1050 break;
1051 case Surface.ROTATION_180:
1052 mKeyRotationMap = KEY_180_MAP;
1053 break;
1054 case Surface.ROTATION_270:
1055 mKeyRotationMap = KEY_270_MAP;
1056 break;
1057 default:
1058 mKeyRotationMap = null;
1059 break;
1060 }
1061 }
1062 }
1063
1064 public int rotateKeyCode(int keyCode) {
1065 synchronized(mFirst) {
1066 return rotateKeyCodeLocked(keyCode);
1067 }
1068 }
1069
1070 private int rotateKeyCodeLocked(int keyCode) {
1071 int[] map = mKeyRotationMap;
1072 if (map != null) {
1073 final int N = map.length;
1074 for (int i=0; i<N; i+=2) {
1075 if (map[i] == keyCode) {
1076 return map[i+1];
1077 }
1078 }
1079 }
1080 return keyCode;
1081 }
1082
1083 boolean hasEvents() {
1084 synchronized (mFirst) {
1085 return mFirst.next != mLast;
1086 }
1087 }
1088
1089 /*
1090 * returns true if we returned an event, and false if we timed out
1091 */
1092 QueuedEvent getEvent(long timeoutMS) {
1093 long begin = SystemClock.uptimeMillis();
1094 final long end = begin+timeoutMS;
1095 long now = begin;
1096 synchronized (mFirst) {
1097 while (mFirst.next == mLast && end > now) {
1098 try {
1099 mWakeLock.release();
1100 mFirst.wait(end-now);
1101 }
1102 catch (InterruptedException e) {
1103 }
1104 now = SystemClock.uptimeMillis();
1105 if (begin > now) {
1106 begin = now;
1107 }
1108 }
1109 if (mFirst.next == mLast) {
1110 return null;
1111 }
1112 QueuedEvent p = mFirst.next;
1113 mFirst.next = p.next;
1114 mFirst.next.prev = mFirst;
1115 p.inQueue = false;
1116 return p;
1117 }
1118 }
1119
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001120 /**
1121 * Return true if the queue has an up event pending that corresponds
1122 * to the same key as the given key event.
1123 */
1124 boolean hasKeyUpEvent(KeyEvent origEvent) {
1125 synchronized (mFirst) {
1126 final int keyCode = origEvent.getKeyCode();
1127 QueuedEvent cur = mLast.prev;
1128 while (cur.prev != null) {
1129 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1130 KeyEvent ke = (KeyEvent)cur.event;
1131 if (ke.getAction() == KeyEvent.ACTION_UP
1132 && ke.getKeyCode() == keyCode) {
1133 return true;
1134 }
1135 }
1136 cur = cur.prev;
1137 }
1138 }
1139
1140 return false;
1141 }
1142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 void recycleEvent(QueuedEvent ev) {
1144 synchronized (mFirst) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001145 //Slog.i(TAG, "Recycle event: " + ev);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1147 ev.inputDevice.mAbs.currentMove = null;
1148 }
1149 if (ev.event == ev.inputDevice.mRel.currentMove) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001150 if (false) Slog.i(TAG, "Detach rel " + ev.event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -07001152 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1153 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 }
1155 recycleLocked(ev);
1156 }
1157 }
1158
1159 void filterQueue(FilterCallback cb) {
1160 synchronized (mFirst) {
1161 QueuedEvent cur = mLast.prev;
1162 while (cur.prev != null) {
1163 switch (cb.filterEvent(cur)) {
1164 case FILTER_REMOVE:
1165 cur.prev.next = cur.next;
1166 cur.next.prev = cur.prev;
1167 break;
1168 case FILTER_ABORT:
1169 return;
1170 }
1171 cur = cur.prev;
1172 }
1173 }
1174 }
1175
Michael Chan53071d62009-05-13 17:29:48 -07001176 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 int flags, int classType, Object event) {
1178 QueuedEvent ev;
1179 if (mCacheCount == 0) {
1180 ev = new QueuedEvent();
1181 } else {
1182 ev = mCache;
1183 ev.inQueue = false;
1184 mCache = ev.next;
1185 mCacheCount--;
1186 }
1187 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -07001188 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 ev.flags = flags;
1190 ev.classType = classType;
1191 ev.event = event;
1192 return ev;
1193 }
1194
1195 private void recycleLocked(QueuedEvent ev) {
1196 if (ev.inQueue) {
1197 throw new RuntimeException("Event already in queue!");
1198 }
1199 if (mCacheCount < 10) {
1200 mCacheCount++;
1201 ev.next = mCache;
1202 mCache = ev;
1203 ev.inQueue = true;
1204 }
1205 }
1206
Michael Chan53071d62009-05-13 17:29:48 -07001207 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 int classType, Object event) {
1209 boolean poke = mFirst.next == mLast;
1210
Michael Chan53071d62009-05-13 17:29:48 -07001211 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -07001213 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 p = p.prev;
1215 }
1216
1217 ev.next = p.next;
1218 ev.prev = p;
1219 p.next = ev;
1220 ev.next.prev = ev;
1221 ev.inQueue = true;
1222
1223 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -07001224 long time;
1225 if (MEASURE_LATENCY) {
1226 time = System.nanoTime();
1227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 mFirst.notify();
1229 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -07001230 if (MEASURE_LATENCY) {
1231 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 }
1234 }
1235
1236 private InputDevice newInputDevice(int deviceId) {
1237 int classes = getDeviceClasses(deviceId);
1238 String name = getDeviceName(deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -07001239 InputDevice.AbsoluteInfo absX = null;
1240 InputDevice.AbsoluteInfo absY = null;
1241 InputDevice.AbsoluteInfo absPressure = null;
1242 InputDevice.AbsoluteInfo absSize = null;
1243 if (classes != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001244 Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
Dianne Hackborna8f60182009-09-01 19:01:50 -07001245 + ", name=" + name
1246 + ", classes=" + Integer.toHexString(classes));
1247 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1248 absX = loadAbsoluteInfo(deviceId,
1249 RawInputEvent.ABS_MT_POSITION_X, "X");
1250 absY = loadAbsoluteInfo(deviceId,
1251 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1252 absPressure = loadAbsoluteInfo(deviceId,
1253 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1254 absSize = loadAbsoluteInfo(deviceId,
1255 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1256 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1257 absX = loadAbsoluteInfo(deviceId,
1258 RawInputEvent.ABS_X, "X");
1259 absY = loadAbsoluteInfo(deviceId,
1260 RawInputEvent.ABS_Y, "Y");
1261 absPressure = loadAbsoluteInfo(deviceId,
1262 RawInputEvent.ABS_PRESSURE, "Pressure");
1263 absSize = loadAbsoluteInfo(deviceId,
1264 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1265 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 }
1267
1268 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1269 }
1270
1271 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1272 String name) {
1273 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1274 if (getAbsoluteInfo(id, channel, info)
1275 && info.minValue != info.maxValue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001276 Slog.i(TAG, " " + name + ": min=" + info.minValue
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 + " max=" + info.maxValue
1278 + " flat=" + info.flat
1279 + " fuzz=" + info.fuzz);
1280 info.range = info.maxValue-info.minValue;
1281 return info;
1282 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001283 Slog.i(TAG, " " + name + ": unknown values");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284 return null;
1285 }
1286 private static native boolean readEvent(RawInputEvent outEvent);
Dianne Hackborna2e92262010-03-02 17:19:29 -08001287
1288 void dump(PrintWriter pw, String prefix) {
1289 synchronized (mFirst) {
1290 for (int i=0; i<mDevices.size(); i++) {
1291 InputDevice dev = mDevices.valueAt(i);
1292 pw.print(prefix); pw.print("Device #");
1293 pw.print(mDevices.keyAt(i)); pw.print(" ");
1294 pw.print(dev.name); pw.print(" (classes=0x");
1295 pw.print(Integer.toHexString(dev.classes));
1296 pw.println("):");
1297 pw.print(prefix); pw.print(" mKeyDownTime=");
1298 pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
1299 pw.println(dev.mMetaKeysState);
1300 if (dev.absX != null) {
1301 pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
1302 pw.println("");
1303 }
1304 if (dev.absY != null) {
1305 pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
1306 pw.println("");
1307 }
1308 if (dev.absPressure != null) {
1309 pw.print(prefix); pw.print(" absPressure: ");
1310 dev.absPressure.dump(pw); pw.println("");
1311 }
1312 if (dev.absSize != null) {
1313 pw.print(prefix); pw.print(" absSize: ");
1314 dev.absSize.dump(pw); pw.println("");
1315 }
1316 if (dev.mAbs.everChanged) {
1317 pw.print(prefix); pw.println(" mAbs:");
1318 dev.mAbs.dump(pw, prefix + " ");
1319 }
1320 if (dev.mRel.everChanged) {
1321 pw.print(prefix); pw.println(" mRel:");
1322 dev.mRel.dump(pw, prefix + " ");
1323 }
1324 }
1325 pw.println(" ");
1326 for (int i=0; i<mIgnoredDevices.size(); i++) {
1327 InputDevice dev = mIgnoredDevices.valueAt(i);
1328 pw.print(prefix); pw.print("Ignored Device #");
1329 pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
1330 pw.print(dev.name); pw.print(" (classes=0x");
1331 pw.print(Integer.toHexString(dev.classes));
1332 pw.println(")");
1333 }
1334 pw.println(" ");
1335 for (int i=0; i<mVirtualKeys.size(); i++) {
1336 VirtualKey vk = mVirtualKeys.get(i);
1337 pw.print(prefix); pw.print("Virtual Key #");
1338 pw.print(i); pw.println(":");
1339 pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
1340 pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
1341 pw.print(" centery="); pw.print(vk.centery);
1342 pw.print(" width="); pw.print(vk.width);
1343 pw.print(" height="); pw.println(vk.height);
1344 pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
1345 pw.print(" hitTop="); pw.print(vk.hitTop);
1346 pw.print(" hitRight="); pw.print(vk.hitRight);
1347 pw.print(" hitBottom="); pw.println(vk.hitBottom);
1348 if (vk.lastDevice != null) {
1349 pw.print(prefix); pw.print(" lastDevice=#");
1350 pw.println(vk.lastDevice.id);
1351 }
1352 if (vk.lastKeycode != 0) {
1353 pw.print(prefix); pw.print(" lastKeycode=");
1354 pw.println(vk.lastKeycode);
1355 }
1356 }
1357 pw.println(" ");
1358 pw.print(prefix); pw.print(" Default keyboard: ");
1359 pw.println(SystemProperties.get("hw.keyboards.0.devname"));
1360 pw.print(prefix); pw.print(" mGlobalMetaState=");
1361 pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
1362 pw.println(mHaveGlobalMetaState);
1363 pw.print(prefix); pw.print(" mDisplayWidth=");
1364 pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
1365 pw.println(mDisplayHeight);
1366 pw.print(prefix); pw.print(" mOrientation=");
1367 pw.println(mOrientation);
1368 if (mPressedVirtualKey != null) {
1369 pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
1370 pw.println(mPressedVirtualKey.scancode);
1371 }
1372 }
1373 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374}