blob: 72c4166f28c06aaa7b374c5dbc17ea2e8339013b [file] [log] [blame]
Jeff Brown46b9ac0a2010-04-22 18:58:52 -07001/*
2 * Copyright (C) 2010 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 com.android.internal.util.XmlUtils;
20import com.android.server.KeyInputQueue.VirtualKey;
21
22import org.xmlpull.v1.XmlPullParser;
23
24import android.content.Context;
25import android.content.res.Configuration;
26import android.os.Environment;
27import android.os.LocalPowerManager;
28import android.os.PowerManager;
29import android.util.Log;
30import android.util.Slog;
31import android.util.Xml;
32import android.view.InputChannel;
33import android.view.InputTarget;
34import android.view.KeyEvent;
35import android.view.MotionEvent;
36import android.view.RawInputEvent;
37import android.view.Surface;
38import android.view.WindowManagerPolicy;
39
40import java.io.BufferedReader;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.FileNotFoundException;
44import java.io.FileReader;
45import java.io.IOException;
46import java.io.InputStreamReader;
47import java.io.PrintWriter;
48import java.util.ArrayList;
49
50/*
51 * Wraps the C++ InputManager and provides its callbacks.
52 *
53 * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
54 * improve separation of concerns with respect to the window manager.
55 */
56public class InputManager {
57 static final String TAG = "InputManager";
58
59 private final Callbacks mCallbacks;
60 private final Context mContext;
61 private final WindowManagerService mWindowManagerService;
62 private final WindowManagerPolicy mWindowManagerPolicy;
63 private final PowerManager mPowerManager;
64 private final PowerManagerService mPowerManagerService;
65
66 private int mTouchScreenConfig;
67 private int mKeyboardConfig;
68 private int mNavigationConfig;
69
70 private static native void nativeInit(Callbacks callbacks);
71 private static native void nativeStart();
72 private static native void nativeSetDisplaySize(int displayId, int width, int height);
73 private static native void nativeSetDisplayOrientation(int displayId, int rotation);
74
75 private static native int nativeGetScanCodeState(int deviceId, int deviceClasses,
76 int scanCode);
77 private static native int nativeGetKeyCodeState(int deviceId, int deviceClasses,
78 int keyCode);
79 private static native int nativeGetSwitchState(int deviceId, int deviceClasses,
80 int sw);
81 private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
82 private static native void nativeRegisterInputChannel(InputChannel inputChannel);
83 private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
84
85 // Device class as defined by EventHub.
86 private static final int CLASS_KEYBOARD = 0x00000001;
87 private static final int CLASS_ALPHAKEY = 0x00000002;
88 private static final int CLASS_TOUCHSCREEN = 0x00000004;
89 private static final int CLASS_TRACKBALL = 0x00000008;
90 private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
91 private static final int CLASS_DPAD = 0x00000020;
92
93 public InputManager(Context context,
94 WindowManagerService windowManagerService,
95 WindowManagerPolicy windowManagerPolicy,
96 PowerManager powerManager,
97 PowerManagerService powerManagerService) {
98 this.mContext = context;
99 this.mWindowManagerService = windowManagerService;
100 this.mWindowManagerPolicy = windowManagerPolicy;
101 this.mPowerManager = powerManager;
102 this.mPowerManagerService = powerManagerService;
103
104 this.mCallbacks = new Callbacks();
105
106 mTouchScreenConfig = Configuration.TOUCHSCREEN_NOTOUCH;
107 mKeyboardConfig = Configuration.KEYBOARD_NOKEYS;
108 mNavigationConfig = Configuration.NAVIGATION_NONAV;
109
110 init();
111 }
112
113 private void init() {
114 Slog.i(TAG, "Initializing input manager");
115 nativeInit(mCallbacks);
116 }
117
118 public void start() {
119 Slog.i(TAG, "Starting input manager");
120 nativeStart();
121 }
122
123 public void setDisplaySize(int displayId, int width, int height) {
124 if (width <= 0 || height <= 0) {
125 throw new IllegalArgumentException("Invalid display id or dimensions.");
126 }
127
128 Slog.i(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
129 nativeSetDisplaySize(displayId, width, height);
130 }
131
132 public void setDisplayOrientation(int displayId, int rotation) {
133 if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
134 throw new IllegalArgumentException("Invalid rotation.");
135 }
136
137 Slog.i(TAG, "Setting display #" + displayId + " orientation to " + rotation);
138 nativeSetDisplayOrientation(displayId, rotation);
139 }
140
141 public void getInputConfiguration(Configuration config) {
142 if (config == null) {
143 throw new IllegalArgumentException("config must not be null.");
144 }
145
146 config.touchscreen = mTouchScreenConfig;
147 config.keyboard = mKeyboardConfig;
148 config.navigation = mNavigationConfig;
149 }
150
151 public int getScancodeState(int code) {
152 return nativeGetScanCodeState(0, -1, code);
153 }
154
155 public int getScancodeState(int deviceId, int code) {
156 return nativeGetScanCodeState(deviceId, -1, code);
157 }
158
159 public int getTrackballScancodeState(int code) {
160 return nativeGetScanCodeState(-1, CLASS_TRACKBALL, code);
161 }
162
163 public int getDPadScancodeState(int code) {
164 return nativeGetScanCodeState(-1, CLASS_DPAD, code);
165 }
166
167 public int getKeycodeState(int code) {
168 return nativeGetKeyCodeState(0, -1, code);
169 }
170
171 public int getKeycodeState(int deviceId, int code) {
172 return nativeGetKeyCodeState(deviceId, -1, code);
173 }
174
175 public int getTrackballKeycodeState(int code) {
176 return nativeGetKeyCodeState(-1, CLASS_TRACKBALL, code);
177 }
178
179 public int getDPadKeycodeState(int code) {
180 return nativeGetKeyCodeState(-1, CLASS_DPAD, code);
181 }
182
183 public int getSwitchState(int sw) {
184 return nativeGetSwitchState(-1, -1, sw);
185 }
186
187 public int getSwitchState(int deviceId, int sw) {
188 return nativeGetSwitchState(deviceId, -1, sw);
189 }
190
191 public boolean hasKeys(int[] keyCodes, boolean[] keyExists) {
192 if (keyCodes == null) {
193 throw new IllegalArgumentException("keyCodes must not be null.");
194 }
195 if (keyExists == null) {
196 throw new IllegalArgumentException("keyExists must not be null.");
197 }
198
199 return nativeHasKeys(keyCodes, keyExists);
200 }
201
202 public void registerInputChannel(InputChannel inputChannel) {
203 if (inputChannel == null) {
204 throw new IllegalArgumentException("inputChannel must not be null.");
205 }
206
207 nativeRegisterInputChannel(inputChannel);
208 }
209
210 public void unregisterInputChannel(InputChannel inputChannel) {
211 if (inputChannel == null) {
212 throw new IllegalArgumentException("inputChannel must not be null.");
213 }
214
215 nativeUnregisterInputChannel(inputChannel);
216 }
217
218 // TBD where this really belongs, duplicate copy in WindowManagerService
219 static final int INJECT_FAILED = 0;
220 static final int INJECT_SUCCEEDED = 1;
221 static final int INJECT_NO_PERMISSION = -1;
222
223 /**
224 * Injects a key event into the event system on behalf of an application.
225 * @param event The event to inject.
226 * @param nature The nature of the event.
227 * @param sync If true, waits for the event to be completed before returning.
228 * @param pid The pid of the injecting application.
229 * @param uid The uid of the injecting application.
230 * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
231 */
232 public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
233 // TODO
234 return INJECT_FAILED;
235 }
236
237 /**
238 * Injects a motion event into the event system on behalf of an application.
239 * @param event The event to inject.
240 * @param nature The nature of the event.
241 * @param sync If true, waits for the event to be completed before returning.
242 * @param pid The pid of the injecting application.
243 * @param uid The uid of the injecting application.
244 * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
245 */
246 public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
247 // TODO
248 return INJECT_FAILED;
249 }
250
251 public void dump(PrintWriter pw) {
252 // TODO
253 }
254
255 private static final class VirtualKeyDefinition {
256 public int scanCode;
257
258 // configured position data, specified in display coords
259 public int centerX;
260 public int centerY;
261 public int width;
262 public int height;
263 }
264
265 /*
266 * Callbacks from native.
267 */
268 private class Callbacks {
269 static final String TAG = "InputManager-Callbacks";
270
271 private static final boolean DEBUG_VIRTUAL_KEYS = false;
272 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
273
274 private final InputTargetList mReusableInputTargetList = new InputTargetList();
275
276 @SuppressWarnings("unused")
277 public boolean isScreenOn() {
278 return mPowerManagerService.isScreenOn();
279 }
280
281 @SuppressWarnings("unused")
282 public boolean isScreenBright() {
283 return mPowerManagerService.isScreenBright();
284 }
285
286 @SuppressWarnings("unused")
287 public void virtualKeyFeedback(long whenNanos, int deviceId, int action, int flags,
288 int keyCode, int scanCode, int metaState, long downTimeNanos) {
289 KeyEvent keyEvent = new KeyEvent(downTimeNanos / 1000000,
290 whenNanos / 1000000, action, keyCode, 0, metaState, scanCode, deviceId,
291 flags);
292
293 mWindowManagerService.virtualKeyFeedback(keyEvent);
294 }
295
296 @SuppressWarnings("unused")
297 public void notifyConfigurationChanged(long whenNanos,
298 int touchScreenConfig, int keyboardConfig, int navigationConfig) {
299 mTouchScreenConfig = touchScreenConfig;
300 mKeyboardConfig = keyboardConfig;
301 mNavigationConfig = navigationConfig;
302
303 mWindowManagerService.sendNewConfiguration();
304 }
305
306 @SuppressWarnings("unused")
307 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
308 mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
309 }
310
311 @SuppressWarnings("unused")
312 public int hackInterceptKey(int deviceId, int type, int scanCode,
313 int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
314 RawInputEvent event = new RawInputEvent();
315 event.deviceId = deviceId;
316 event.type = type;
317 event.scancode = scanCode;
318 event.keycode = keyCode;
319 event.flags = policyFlags;
320 event.value = value;
321 event.when = whenNanos / 1000000;
322
323 return mWindowManagerPolicy.interceptKeyTq(event, isScreenOn);
324 }
325
326 @SuppressWarnings("unused")
327 public void goToSleep(long whenNanos) {
328 long when = whenNanos / 1000000;
329 mPowerManager.goToSleep(when);
330 }
331
332 @SuppressWarnings("unused")
333 public void pokeUserActivityForKey(long whenNanos) {
334 long when = whenNanos / 1000000;
335 mPowerManagerService.userActivity(when, false,
336 LocalPowerManager.BUTTON_EVENT, false);
337 }
338
339 @SuppressWarnings("unused")
340 public void notifyAppSwitchComing() {
341 mWindowManagerService.mKeyWaiter.appSwitchComing();
342 }
343
344 @SuppressWarnings("unused")
345 public boolean filterTouchEvents() {
346 return mContext.getResources().getBoolean(
347 com.android.internal.R.bool.config_filterTouchEvents);
348 }
349
350 @SuppressWarnings("unused")
351 public boolean filterJumpyTouchEvents() {
352 return mContext.getResources().getBoolean(
353 com.android.internal.R.bool.config_filterJumpyTouchEvents);
354 }
355
356 @SuppressWarnings("unused")
357 public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
358 ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
359
360 try {
361 FileInputStream fis = new FileInputStream(
362 "/sys/board_properties/virtualkeys." + deviceName);
363 InputStreamReader isr = new InputStreamReader(fis);
364 BufferedReader br = new BufferedReader(isr, 2048);
365 String str = br.readLine();
366 if (str != null) {
367 String[] it = str.split(":");
368 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
369 final int N = it.length-6;
370 for (int i=0; i<=N; i+=6) {
371 if (!"0x01".equals(it[i])) {
372 Slog.w(TAG, "Unknown virtual key type at elem #" + i
373 + ": " + it[i]);
374 continue;
375 }
376 try {
377 VirtualKeyDefinition key = new VirtualKeyDefinition();
378 key.scanCode = Integer.parseInt(it[i+1]);
379 key.centerX = Integer.parseInt(it[i+2]);
380 key.centerY = Integer.parseInt(it[i+3]);
381 key.width = Integer.parseInt(it[i+4]);
382 key.height = Integer.parseInt(it[i+5]);
383 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
384 + key.scanCode + ": center=" + key.centerX + ","
385 + key.centerY + " size=" + key.width + "x"
386 + key.height);
387 keys.add(key);
388 } catch (NumberFormatException e) {
389 Slog.w(TAG, "Bad number at region " + i + " in: "
390 + str, e);
391 }
392 }
393 }
394 br.close();
395 } catch (FileNotFoundException e) {
396 Slog.i(TAG, "No virtual keys found");
397 } catch (IOException e) {
398 Slog.w(TAG, "Error reading virtual keys", e);
399 }
400
401 return keys.toArray(new VirtualKeyDefinition[keys.size()]);
402 }
403
404 @SuppressWarnings("unused")
405 public String[] getExcludedDeviceNames() {
406 ArrayList<String> names = new ArrayList<String>();
407
408 // Read partner-provided list of excluded input devices
409 XmlPullParser parser = null;
410 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
411 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
412 FileReader confreader = null;
413 try {
414 confreader = new FileReader(confFile);
415 parser = Xml.newPullParser();
416 parser.setInput(confreader);
417 XmlUtils.beginDocument(parser, "devices");
418
419 while (true) {
420 XmlUtils.nextElement(parser);
421 if (!"device".equals(parser.getName())) {
422 break;
423 }
424 String name = parser.getAttributeValue(null, "name");
425 if (name != null) {
426 names.add(name);
427 }
428 }
429 } catch (FileNotFoundException e) {
430 // It's ok if the file does not exist.
431 } catch (Exception e) {
432 Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
433 } finally {
434 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
435 }
436
437 return names.toArray(new String[names.size()]);
438 }
439
440 @SuppressWarnings("unused")
441 public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
442 mReusableInputTargetList.clear();
443
444 mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
445 event, nature, policyFlags);
446
447 return mReusableInputTargetList.toNullTerminatedArray();
448 }
449
450 @SuppressWarnings("unused")
451 public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
452 mReusableInputTargetList.clear();
453
454 mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
455 event, nature, policyFlags);
456
457 return mReusableInputTargetList.toNullTerminatedArray();
458 }
459 }
460}