blob: 9e94b52fbaf553d2713e31ab6dccc14056c28001 [file] [log] [blame]
Jeff Brown46b9ac02010-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
Jeff Brown4532e612012-04-05 14:27:12 -070017package com.android.server.input;
Jeff Brown46b9ac02010-04-22 18:58:52 -070018
Jeff Browna3bc5652012-04-17 11:42:25 -070019import com.android.internal.os.AtomicFile;
20import com.android.internal.util.FastXmlSerializer;
Jeff Brown46b9ac02010-04-22 18:58:52 -070021import com.android.internal.util.XmlUtils;
Jeff Brown89ef0722011-08-10 16:25:21 -070022import com.android.server.Watchdog;
Jeff Brown46b9ac02010-04-22 18:58:52 -070023
24import org.xmlpull.v1.XmlPullParser;
Jeff Browna3bc5652012-04-17 11:42:25 -070025import org.xmlpull.v1.XmlPullParserException;
26import org.xmlpull.v1.XmlSerializer;
Jeff Brown46b9ac02010-04-22 18:58:52 -070027
Jeff Browna3bc5652012-04-17 11:42:25 -070028import android.Manifest;
Jeff Brown5bbd4b42012-04-20 19:28:00 -070029import android.bluetooth.BluetoothAdapter;
30import android.bluetooth.BluetoothDevice;
Jeff Brown6ec6f792012-04-17 16:52:41 -070031import android.content.BroadcastReceiver;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070032import android.content.ComponentName;
Jeff Brown46b9ac02010-04-22 18:58:52 -070033import android.content.Context;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070034import android.content.Intent;
Jeff Brown6ec6f792012-04-17 16:52:41 -070035import android.content.IntentFilter;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070036import android.content.pm.ActivityInfo;
Jeff Brown349703e2010-06-22 01:27:15 -070037import android.content.pm.PackageManager;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070038import android.content.pm.ResolveInfo;
39import android.content.pm.PackageManager.NameNotFoundException;
Jeff Brown46b9ac02010-04-22 18:58:52 -070040import android.content.res.Configuration;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070041import android.content.res.Resources;
Jeff Brown6ec6f792012-04-17 16:52:41 -070042import android.content.res.Resources.NotFoundException;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070043import android.content.res.TypedArray;
44import android.content.res.XmlResourceParser;
Jeff Brown1a84fd12011-06-02 01:26:32 -070045import android.database.ContentObserver;
Jeff Brown4532e612012-04-05 14:27:12 -070046import android.hardware.input.IInputManager;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070047import android.hardware.input.IInputDevicesChangedListener;
Jeff Brownac143512012-04-05 18:57:33 -070048import android.hardware.input.InputManager;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070049import android.hardware.input.KeyboardLayout;
Jeff Brown4532e612012-04-05 14:27:12 -070050import android.os.Binder;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070051import android.os.Bundle;
Jeff Brown46b9ac02010-04-22 18:58:52 -070052import android.os.Environment;
Jeff Brown4532e612012-04-05 14:27:12 -070053import android.os.Handler;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070054import android.os.IBinder;
55import android.os.Message;
Jeff Brown05dc66a2011-03-02 14:41:58 -080056import android.os.MessageQueue;
Jeff Brownac143512012-04-05 18:57:33 -070057import android.os.Process;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070058import android.os.RemoteException;
Jeff Brown1a84fd12011-06-02 01:26:32 -070059import android.provider.Settings;
60import android.provider.Settings.SettingNotFoundException;
Jeff Brown5bbd4b42012-04-20 19:28:00 -070061import android.server.BluetoothService;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070062import android.util.Log;
Jeff Brown46b9ac02010-04-22 18:58:52 -070063import android.util.Slog;
Jeff Brownaf9e8d32012-04-12 17:32:48 -070064import android.util.SparseArray;
Jeff Brown46b9ac02010-04-22 18:58:52 -070065import android.util.Xml;
66import android.view.InputChannel;
Jeff Brown8d608662010-08-30 03:02:23 -070067import android.view.InputDevice;
Jeff Brown6ec402b2010-07-28 15:48:59 -070068import android.view.InputEvent;
Jeff Brown1f245102010-11-18 20:53:46 -080069import android.view.KeyEvent;
Jeff Brown2352b972011-04-12 22:39:53 -070070import android.view.PointerIcon;
Jeff Brown46b9ac02010-04-22 18:58:52 -070071import android.view.Surface;
Jeff Browna4547672011-03-02 21:38:11 -080072import android.view.ViewConfiguration;
Jeff Brown0029c662011-03-30 02:25:18 -070073import android.view.WindowManagerPolicy;
Jeff Brown46b9ac02010-04-22 18:58:52 -070074
Jeff Browna3bc5652012-04-17 11:42:25 -070075import java.io.BufferedInputStream;
76import java.io.BufferedOutputStream;
Jeff Brown46b9ac02010-04-22 18:58:52 -070077import java.io.File;
Jeff Brown4532e612012-04-05 14:27:12 -070078import java.io.FileDescriptor;
Jeff Brown46b9ac02010-04-22 18:58:52 -070079import java.io.FileNotFoundException;
Jeff Browna3bc5652012-04-17 11:42:25 -070080import java.io.FileOutputStream;
Jeff Brown46b9ac02010-04-22 18:58:52 -070081import java.io.FileReader;
82import java.io.IOException;
Jeff Browna3bc5652012-04-17 11:42:25 -070083import java.io.InputStream;
Jeff Brown6ec6f792012-04-17 16:52:41 -070084import java.io.InputStreamReader;
Jeff Brown46b9ac02010-04-22 18:58:52 -070085import java.io.PrintWriter;
86import java.util.ArrayList;
Jeff Brown9f25b7f2012-04-10 14:30:49 -070087import java.util.HashMap;
Jeff Browna3bc5652012-04-17 11:42:25 -070088import java.util.Map;
89
90import libcore.io.IoUtils;
Jeff Brown6ec6f792012-04-17 16:52:41 -070091import libcore.io.Streams;
Jeff Browna3bc5652012-04-17 11:42:25 -070092import libcore.util.Objects;
Jeff Brown46b9ac02010-04-22 18:58:52 -070093
94/*
95 * Wraps the C++ InputManager and provides its callbacks.
Jeff Brown46b9ac02010-04-22 18:58:52 -070096 */
Jeff Brown4532e612012-04-05 14:27:12 -070097public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
Jeff Brown46b9ac02010-04-22 18:58:52 -070098 static final String TAG = "InputManager";
Jeff Brown6ec6f792012-04-17 16:52:41 -070099 static final boolean DEBUG = true;
Jeff Brownb6997262010-10-08 22:31:17 -0700100
Jeff Brown4532e612012-04-05 14:27:12 -0700101 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
102
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700103 private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
104
Jeff Brown4532e612012-04-05 14:27:12 -0700105 // Pointer to native input manager service object.
106 private final int mPtr;
107
Jeff Brown46b9ac02010-04-22 18:58:52 -0700108 private final Context mContext;
Jeff Brown4532e612012-04-05 14:27:12 -0700109 private final Callbacks mCallbacks;
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700110 private final InputManagerHandler mHandler;
Jeff Brown6ec6f792012-04-17 16:52:41 -0700111 private boolean mSystemReady;
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700112 private BluetoothService mBluetoothService;
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700113
Jeff Browna3bc5652012-04-17 11:42:25 -0700114 // Persistent data store. Must be locked each time during use.
115 private final PersistentDataStore mDataStore = new PersistentDataStore();
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700116
117 // List of currently registered input devices changed listeners by process id.
118 private Object mInputDevicesLock = new Object();
119 private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
120 private InputDevice[] mInputDevices = new InputDevice[0];
121 private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
122 new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
123 private final ArrayList<InputDevicesChangedListenerRecord>
124 mTempInputDevicesChangedListenersToNotify =
125 new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
126
Jeff Browna47425a2012-04-13 04:09:27 -0700127 // State for vibrator tokens.
128 private Object mVibratorLock = new Object();
129 private HashMap<IBinder, VibratorToken> mVibratorTokens =
130 new HashMap<IBinder, VibratorToken>();
131 private int mNextVibratorTokenValue;
132
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700133 // State for the currently installed input filter.
134 final Object mInputFilterLock = new Object();
135 InputFilter mInputFilter; // guarded by mInputFilterLock
136 InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
Jeff Brown1a84fd12011-06-02 01:26:32 -0700137
Jeff Brown4532e612012-04-05 14:27:12 -0700138 private static native int nativeInit(InputManagerService service,
139 Context context, MessageQueue messageQueue);
140 private static native void nativeStart(int ptr);
141 private static native void nativeSetDisplaySize(int ptr, int displayId,
142 int width, int height, int externalWidth, int externalHeight);
Jeff Brown93de7462012-05-02 16:29:42 -0700143 private static native void nativeSetDisplayOrientation(int ptr, int displayId,
144 int rotation, int externalRotation);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700145
Jeff Brown4532e612012-04-05 14:27:12 -0700146 private static native int nativeGetScanCodeState(int ptr,
147 int deviceId, int sourceMask, int scanCode);
148 private static native int nativeGetKeyCodeState(int ptr,
149 int deviceId, int sourceMask, int keyCode);
150 private static native int nativeGetSwitchState(int ptr,
151 int deviceId, int sourceMask, int sw);
152 private static native boolean nativeHasKeys(int ptr,
153 int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
154 private static native void nativeRegisterInputChannel(int ptr, InputChannel inputChannel,
Jeff Brown928e0542011-01-10 11:17:36 -0800155 InputWindowHandle inputWindowHandle, boolean monitor);
Jeff Brown4532e612012-04-05 14:27:12 -0700156 private static native void nativeUnregisterInputChannel(int ptr, InputChannel inputChannel);
157 private static native void nativeSetInputFilterEnabled(int ptr, boolean enable);
158 private static native int nativeInjectInputEvent(int ptr, InputEvent event,
Jeff Brown0029c662011-03-30 02:25:18 -0700159 int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
160 int policyFlags);
Jeff Brown4532e612012-04-05 14:27:12 -0700161 private static native void nativeSetInputWindows(int ptr, InputWindowHandle[] windowHandles);
162 private static native void nativeSetInputDispatchMode(int ptr, boolean enabled, boolean frozen);
163 private static native void nativeSetSystemUiVisibility(int ptr, int visibility);
164 private static native void nativeSetFocusedApplication(int ptr,
165 InputApplicationHandle application);
Jeff Brown4532e612012-04-05 14:27:12 -0700166 private static native boolean nativeTransferTouchFocus(int ptr,
167 InputChannel fromChannel, InputChannel toChannel);
168 private static native void nativeSetPointerSpeed(int ptr, int speed);
169 private static native void nativeSetShowTouches(int ptr, boolean enabled);
Jeff Browna47425a2012-04-13 04:09:27 -0700170 private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
171 int repeat, int token);
172 private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
Jeff Brown6ec6f792012-04-17 16:52:41 -0700173 private static native void nativeReloadKeyboardLayouts(int ptr);
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700174 private static native void nativeReloadDeviceAliases(int ptr);
Jeff Brown4532e612012-04-05 14:27:12 -0700175 private static native String nativeDump(int ptr);
176 private static native void nativeMonitor(int ptr);
Jeff Brown4532e612012-04-05 14:27:12 -0700177
Jeff Brownac143512012-04-05 18:57:33 -0700178 // Input event injection constants defined in InputDispatcher.h.
179 private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
180 private static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
181 private static final int INPUT_EVENT_INJECTION_FAILED = 2;
182 private static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
183
184 // Maximum number of milliseconds to wait for input event injection.
185 private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
186
Jeff Brown6d0fec22010-07-23 21:28:06 -0700187 // Key states (may be returned by queries about the current state of a
188 // particular key code, scan code or switch).
189
190 /** The key state is unknown or the requested key itself is not supported. */
191 public static final int KEY_STATE_UNKNOWN = -1;
192
193 /** The key is up. /*/
194 public static final int KEY_STATE_UP = 0;
195
196 /** The key is down. */
197 public static final int KEY_STATE_DOWN = 1;
198
199 /** The key is down but is a virtual key press that is being emulated by the system. */
200 public static final int KEY_STATE_VIRTUAL = 2;
201
Jeff Brownc458ce92012-04-30 14:58:40 -0700202 /** Scan code: Mouse / trackball button. */
203 public static final int BTN_MOUSE = 0x110;
204
205 /** Switch code: Lid switch. When set, lid is shut. */
206 public static final int SW_LID = 0x00;
207
208 /** Switch code: Keypad slide. When set, keyboard is exposed. */
209 public static final int SW_KEYPAD_SLIDE = 0x0a;
210
Jeff Brown4532e612012-04-05 14:27:12 -0700211 public InputManagerService(Context context, Callbacks callbacks) {
Jeff Brown46b9ac02010-04-22 18:58:52 -0700212 this.mContext = context;
Jeff Brown4532e612012-04-05 14:27:12 -0700213 this.mCallbacks = callbacks;
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700214 this.mHandler = new InputManagerHandler();
Jeff Brown05dc66a2011-03-02 14:41:58 -0800215
Jeff Brown46b9ac02010-04-22 18:58:52 -0700216 Slog.i(TAG, "Initializing input manager");
Jeff Brown4532e612012-04-05 14:27:12 -0700217 mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
Jeff Brown46b9ac02010-04-22 18:58:52 -0700218 }
Jeff Brown1a84fd12011-06-02 01:26:32 -0700219
Jeff Brown46b9ac02010-04-22 18:58:52 -0700220 public void start() {
221 Slog.i(TAG, "Starting input manager");
Jeff Brown4532e612012-04-05 14:27:12 -0700222 nativeStart(mPtr);
223
224 // Add ourself to the Watchdog monitors.
225 Watchdog.getInstance().addMonitor(this);
Jeff Brown1a84fd12011-06-02 01:26:32 -0700226
227 registerPointerSpeedSettingObserver();
Jeff Browndaf4a122011-08-26 17:14:14 -0700228 registerShowTouchesSettingObserver();
229
Jeff Brown1a84fd12011-06-02 01:26:32 -0700230 updatePointerSpeedFromSettings();
Jeff Browndaf4a122011-08-26 17:14:14 -0700231 updateShowTouchesFromSettings();
Jeff Brown46b9ac02010-04-22 18:58:52 -0700232 }
Jeff Brown6ec6f792012-04-17 16:52:41 -0700233
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700234 public void systemReady(BluetoothService bluetoothService) {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700235 if (DEBUG) {
236 Slog.d(TAG, "System ready.");
237 }
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700238 mBluetoothService = bluetoothService;
Jeff Brown6ec6f792012-04-17 16:52:41 -0700239 mSystemReady = true;
Jeff Brown6ec6f792012-04-17 16:52:41 -0700240
241 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
242 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
243 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
244 filter.addDataScheme("package");
245 mContext.registerReceiver(new BroadcastReceiver() {
246 @Override
247 public void onReceive(Context context, Intent intent) {
248 if (DEBUG) {
249 Slog.d(TAG, "Packages changed, reloading keyboard layouts.");
250 }
251 reloadKeyboardLayouts();
252 }
253 }, filter, null, mHandler);
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700254
255 filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
256 mContext.registerReceiver(new BroadcastReceiver() {
257 @Override
258 public void onReceive(Context context, Intent intent) {
259 if (DEBUG) {
260 Slog.d(TAG, "Bluetooth alias changed, reloading device names.");
261 }
262 reloadDeviceAliases();
263 }
264 }, filter, null, mHandler);
265
266 reloadKeyboardLayouts();
267 reloadDeviceAliases();
Jeff Brown6ec6f792012-04-17 16:52:41 -0700268 }
269
270 private void reloadKeyboardLayouts() {
271 nativeReloadKeyboardLayouts(mPtr);
272 }
273
Jeff Brown5bbd4b42012-04-20 19:28:00 -0700274 private void reloadDeviceAliases() {
275 nativeReloadDeviceAliases(mPtr);
276 }
277
Jeff Brownbc68a592011-07-25 12:58:12 -0700278 public void setDisplaySize(int displayId, int width, int height,
279 int externalWidth, int externalHeight) {
280 if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
Jeff Brown46b9ac02010-04-22 18:58:52 -0700281 throw new IllegalArgumentException("Invalid display id or dimensions.");
282 }
283
Jeff Brownb6997262010-10-08 22:31:17 -0700284 if (DEBUG) {
Jeff Brownbc68a592011-07-25 12:58:12 -0700285 Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height
286 + " external size " + externalWidth + "x" + externalHeight);
Jeff Brownb6997262010-10-08 22:31:17 -0700287 }
Jeff Brown4532e612012-04-05 14:27:12 -0700288 nativeSetDisplaySize(mPtr, displayId, width, height, externalWidth, externalHeight);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700289 }
290
Jeff Brown93de7462012-05-02 16:29:42 -0700291 public void setDisplayOrientation(int displayId, int rotation, int externalRotation) {
Jeff Brown46b9ac02010-04-22 18:58:52 -0700292 if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
293 throw new IllegalArgumentException("Invalid rotation.");
294 }
295
Jeff Brownb6997262010-10-08 22:31:17 -0700296 if (DEBUG) {
Jeff Brown93de7462012-05-02 16:29:42 -0700297 Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation
298 + " external rotation " + externalRotation);
Jeff Brownb6997262010-10-08 22:31:17 -0700299 }
Jeff Brown93de7462012-05-02 16:29:42 -0700300 nativeSetDisplayOrientation(mPtr, displayId, rotation, externalRotation);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700301 }
Jeff Brownac143512012-04-05 18:57:33 -0700302
Jeff Brown6d0fec22010-07-23 21:28:06 -0700303 /**
304 * Gets the current state of a key or button by key code.
305 * @param deviceId The input device id, or -1 to consult all devices.
306 * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
307 * consider all input sources. An input device is consulted if at least one of its
308 * non-class input source bits matches the specified source mask.
309 * @param keyCode The key code to check.
310 * @return The key state.
311 */
312 public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
Jeff Brown4532e612012-04-05 14:27:12 -0700313 return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700314 }
315
Jeff Brown6d0fec22010-07-23 21:28:06 -0700316 /**
317 * Gets the current state of a key or button by scan code.
318 * @param deviceId The input device id, or -1 to consult all devices.
319 * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
320 * consider all input sources. An input device is consulted if at least one of its
321 * non-class input source bits matches the specified source mask.
322 * @param scanCode The scan code to check.
323 * @return The key state.
324 */
325 public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
Jeff Brown4532e612012-04-05 14:27:12 -0700326 return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700327 }
328
Jeff Brown6d0fec22010-07-23 21:28:06 -0700329 /**
330 * Gets the current state of a switch by switch code.
331 * @param deviceId The input device id, or -1 to consult all devices.
332 * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
333 * consider all input sources. An input device is consulted if at least one of its
334 * non-class input source bits matches the specified source mask.
335 * @param switchCode The switch code to check.
336 * @return The switch state.
337 */
338 public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
Jeff Brown4532e612012-04-05 14:27:12 -0700339 return nativeGetSwitchState(mPtr, deviceId, sourceMask, switchCode);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700340 }
341
Jeff Brown6d0fec22010-07-23 21:28:06 -0700342 /**
343 * Determines whether the specified key codes are supported by a particular device.
344 * @param deviceId The input device id, or -1 to consult all devices.
345 * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
346 * consider all input sources. An input device is consulted if at least one of its
347 * non-class input source bits matches the specified source mask.
348 * @param keyCodes The array of key codes to check.
349 * @param keyExists An array at least as large as keyCodes whose entries will be set
350 * to true or false based on the presence or absence of support for the corresponding
351 * key codes.
352 * @return True if the lookup was successful, false otherwise.
353 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700354 @Override // Binder call
Jeff Brown6d0fec22010-07-23 21:28:06 -0700355 public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
Jeff Brown46b9ac02010-04-22 18:58:52 -0700356 if (keyCodes == null) {
357 throw new IllegalArgumentException("keyCodes must not be null.");
358 }
Jeff Brown6d0fec22010-07-23 21:28:06 -0700359 if (keyExists == null || keyExists.length < keyCodes.length) {
360 throw new IllegalArgumentException("keyExists must not be null and must be at "
361 + "least as large as keyCodes.");
Jeff Brown46b9ac02010-04-22 18:58:52 -0700362 }
363
Jeff Brown4532e612012-04-05 14:27:12 -0700364 return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700365 }
366
Jeff Browna41ca772010-08-11 14:46:32 -0700367 /**
368 * Creates an input channel that will receive all input from the input dispatcher.
369 * @param inputChannelName The input channel name.
370 * @return The input channel.
371 */
372 public InputChannel monitorInput(String inputChannelName) {
373 if (inputChannelName == null) {
374 throw new IllegalArgumentException("inputChannelName must not be null.");
375 }
376
377 InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
Jeff Brown4532e612012-04-05 14:27:12 -0700378 nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
Jeff Browna41ca772010-08-11 14:46:32 -0700379 inputChannels[0].dispose(); // don't need to retain the Java object reference
380 return inputChannels[1];
381 }
382
383 /**
384 * Registers an input channel so that it can be used as an input event target.
385 * @param inputChannel The input channel to register.
Jeff Brown928e0542011-01-10 11:17:36 -0800386 * @param inputWindowHandle The handle of the input window associated with the
387 * input channel, or null if none.
Jeff Browna41ca772010-08-11 14:46:32 -0700388 */
Jeff Brown928e0542011-01-10 11:17:36 -0800389 public void registerInputChannel(InputChannel inputChannel,
390 InputWindowHandle inputWindowHandle) {
Jeff Brown46b9ac02010-04-22 18:58:52 -0700391 if (inputChannel == null) {
392 throw new IllegalArgumentException("inputChannel must not be null.");
393 }
394
Jeff Brown4532e612012-04-05 14:27:12 -0700395 nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700396 }
397
Jeff Browna41ca772010-08-11 14:46:32 -0700398 /**
399 * Unregisters an input channel.
400 * @param inputChannel The input channel to unregister.
401 */
Jeff Brown46b9ac02010-04-22 18:58:52 -0700402 public void unregisterInputChannel(InputChannel inputChannel) {
403 if (inputChannel == null) {
404 throw new IllegalArgumentException("inputChannel must not be null.");
405 }
406
Jeff Brown4532e612012-04-05 14:27:12 -0700407 nativeUnregisterInputChannel(mPtr, inputChannel);
Jeff Brown46b9ac02010-04-22 18:58:52 -0700408 }
Jeff Brown0029c662011-03-30 02:25:18 -0700409
410 /**
411 * Sets an input filter that will receive all input events before they are dispatched.
412 * The input filter may then reinterpret input events or inject new ones.
413 *
414 * To ensure consistency, the input dispatcher automatically drops all events
415 * in progress whenever an input filter is installed or uninstalled. After an input
416 * filter is uninstalled, it can no longer send input events unless it is reinstalled.
417 * Any events it attempts to send after it has been uninstalled will be dropped.
418 *
419 * @param filter The input filter, or null to remove the current filter.
420 */
421 public void setInputFilter(InputFilter filter) {
422 synchronized (mInputFilterLock) {
423 final InputFilter oldFilter = mInputFilter;
424 if (oldFilter == filter) {
425 return; // nothing to do
426 }
427
428 if (oldFilter != null) {
429 mInputFilter = null;
430 mInputFilterHost.disconnectLocked();
431 mInputFilterHost = null;
432 oldFilter.uninstall();
433 }
434
435 if (filter != null) {
436 mInputFilter = filter;
437 mInputFilterHost = new InputFilterHost();
438 filter.install(mInputFilterHost);
439 }
440
Jeff Brown4532e612012-04-05 14:27:12 -0700441 nativeSetInputFilterEnabled(mPtr, filter != null);
Jeff Brown0029c662011-03-30 02:25:18 -0700442 }
443 }
444
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700445 @Override // Binder call
Jeff Brownac143512012-04-05 18:57:33 -0700446 public boolean injectInputEvent(InputEvent event, int mode) {
Jeff Brown7fbdc842010-06-17 20:52:56 -0700447 if (event == null) {
448 throw new IllegalArgumentException("event must not be null");
449 }
Jeff Brownac143512012-04-05 18:57:33 -0700450 if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
451 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
452 && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
453 throw new IllegalArgumentException("mode is invalid");
Jeff Brown7fbdc842010-06-17 20:52:56 -0700454 }
Jeff Brown6ec402b2010-07-28 15:48:59 -0700455
Jeff Brownac143512012-04-05 18:57:33 -0700456 final int pid = Binder.getCallingPid();
457 final int uid = Binder.getCallingUid();
458 final long ident = Binder.clearCallingIdentity();
459 final int result;
460 try {
461 result = nativeInjectInputEvent(mPtr, event, pid, uid, mode,
462 INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
463 } finally {
464 Binder.restoreCallingIdentity(ident);
465 }
466 switch (result) {
467 case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
468 Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
469 throw new SecurityException(
470 "Injecting to another application requires INJECT_EVENTS permission");
471 case INPUT_EVENT_INJECTION_SUCCEEDED:
472 return true;
473 case INPUT_EVENT_INJECTION_TIMED_OUT:
474 Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
475 return false;
476 case INPUT_EVENT_INJECTION_FAILED:
477 default:
478 Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
479 return false;
480 }
Jeff Brown46b9ac02010-04-22 18:58:52 -0700481 }
Jeff Brown0029c662011-03-30 02:25:18 -0700482
Jeff Brown8d608662010-08-30 03:02:23 -0700483 /**
484 * Gets information about the input device with the specified id.
485 * @param id The device id.
486 * @return The input device or null if not found.
487 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700488 @Override // Binder call
Jeff Brown8d608662010-08-30 03:02:23 -0700489 public InputDevice getInputDevice(int deviceId) {
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700490 synchronized (mInputDevicesLock) {
491 final int count = mInputDevices.length;
492 for (int i = 0; i < count; i++) {
493 final InputDevice inputDevice = mInputDevices[i];
494 if (inputDevice.getId() == deviceId) {
495 return inputDevice;
496 }
497 }
498 }
499 return null;
Jeff Brown8d608662010-08-30 03:02:23 -0700500 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700501
Jeff Brown8d608662010-08-30 03:02:23 -0700502 /**
503 * Gets the ids of all input devices in the system.
504 * @return The input device ids.
505 */
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700506 @Override // Binder call
Jeff Brown8d608662010-08-30 03:02:23 -0700507 public int[] getInputDeviceIds() {
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700508 synchronized (mInputDevicesLock) {
509 final int count = mInputDevices.length;
510 int[] ids = new int[count];
511 for (int i = 0; i < count; i++) {
512 ids[i] = mInputDevices[i].getId();
513 }
514 return ids;
515 }
516 }
517
Jeff Browndaa37532012-05-01 15:54:03 -0700518 /**
519 * Gets all input devices in the system.
520 * @return The array of input devices.
521 */
522 public InputDevice[] getInputDevices() {
523 synchronized (mInputDevicesLock) {
524 return mInputDevices;
525 }
526 }
527
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700528 @Override // Binder call
529 public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
530 if (listener == null) {
531 throw new IllegalArgumentException("listener must not be null");
532 }
533
534 synchronized (mInputDevicesLock) {
535 int callingPid = Binder.getCallingPid();
536 if (mInputDevicesChangedListeners.get(callingPid) != null) {
537 throw new SecurityException("The calling process has already "
538 + "registered an InputDevicesChangedListener.");
539 }
540
541 InputDevicesChangedListenerRecord record =
542 new InputDevicesChangedListenerRecord(callingPid, listener);
543 try {
544 IBinder binder = listener.asBinder();
545 binder.linkToDeath(record, 0);
546 } catch (RemoteException ex) {
547 // give up
548 throw new RuntimeException(ex);
549 }
550
551 mInputDevicesChangedListeners.put(callingPid, record);
552 }
553 }
554
555 private void onInputDevicesChangedListenerDied(int pid) {
556 synchronized (mInputDevicesLock) {
557 mInputDevicesChangedListeners.remove(pid);
558 }
559 }
560
561 // Must be called on handler.
562 private void deliverInputDevicesChanged() {
563 mTempInputDevicesChangedListenersToNotify.clear();
564
565 final int numListeners;
566 final int[] deviceIdAndGeneration;
567 synchronized (mInputDevicesLock) {
568 if (!mInputDevicesChangedPending) {
569 return;
570 }
571 mInputDevicesChangedPending = false;
572
573 numListeners = mInputDevicesChangedListeners.size();
574 for (int i = 0; i < numListeners; i++) {
575 mTempInputDevicesChangedListenersToNotify.add(
576 mInputDevicesChangedListeners.valueAt(i));
577 }
578
579 final int numDevices = mInputDevices.length;
580 deviceIdAndGeneration = new int[numDevices * 2];
581 for (int i = 0; i < numDevices; i++) {
582 final InputDevice inputDevice = mInputDevices[i];
583 deviceIdAndGeneration[i * 2] = inputDevice.getId();
584 deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
585 }
586 }
587
588 for (int i = 0; i < numListeners; i++) {
589 mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
590 deviceIdAndGeneration);
591 }
Jeff Brown8d608662010-08-30 03:02:23 -0700592 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700593
594 @Override // Binder call
595 public KeyboardLayout[] getKeyboardLayouts() {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700596 final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
597 visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
598 @Override
599 public void visitKeyboardLayout(Resources resources,
Jeff Brown2f095762012-05-10 21:29:33 -0700600 String descriptor, String label, int keyboardLayoutResId) {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700601 list.add(new KeyboardLayout(descriptor, label));
602 }
603 });
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700604 return list.toArray(new KeyboardLayout[list.size()]);
605 }
606
607 @Override // Binder call
608 public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
609 if (keyboardLayoutDescriptor == null) {
610 throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
611 }
612
Jeff Brown6ec6f792012-04-17 16:52:41 -0700613 final KeyboardLayout[] result = new KeyboardLayout[1];
614 visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
615 @Override
616 public void visitKeyboardLayout(Resources resources,
Jeff Brown2f095762012-05-10 21:29:33 -0700617 String descriptor, String label, int keyboardLayoutResId) {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700618 result[0] = new KeyboardLayout(descriptor, label);
619 }
620 });
621 if (result[0] == null) {
622 Log.w(TAG, "Could not get keyboard layout with descriptor '"
623 + keyboardLayoutDescriptor + "'.");
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700624 }
Jeff Brown6ec6f792012-04-17 16:52:41 -0700625 return result[0];
626 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700627
Jeff Brown6ec6f792012-04-17 16:52:41 -0700628 private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700629 final PackageManager pm = mContext.getPackageManager();
Jeff Brown6ec6f792012-04-17 16:52:41 -0700630 Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
631 for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
632 PackageManager.GET_META_DATA)) {
633 visitKeyboardLayoutsInPackage(pm, resolveInfo.activityInfo, null, visitor);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700634 }
635 }
636
Jeff Brown6ec6f792012-04-17 16:52:41 -0700637 private void visitKeyboardLayout(String keyboardLayoutDescriptor,
638 KeyboardLayoutVisitor visitor) {
639 KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
640 if (d != null) {
641 final PackageManager pm = mContext.getPackageManager();
642 try {
643 ActivityInfo receiver = pm.getReceiverInfo(
644 new ComponentName(d.packageName, d.receiverName),
645 PackageManager.GET_META_DATA);
646 visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, visitor);
647 } catch (NameNotFoundException ex) {
648 }
649 }
650 }
651
652 private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
653 String keyboardName, KeyboardLayoutVisitor visitor) {
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700654 Bundle metaData = receiver.metaData;
655 if (metaData == null) {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700656 return;
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700657 }
658
659 int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
660 if (configResId == 0) {
661 Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
662 + "' on receiver " + receiver.packageName + "/" + receiver.name);
Jeff Brown6ec6f792012-04-17 16:52:41 -0700663 return;
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700664 }
665
666 try {
667 Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
668 XmlResourceParser parser = resources.getXml(configResId);
669 try {
670 XmlUtils.beginDocument(parser, "keyboard-layouts");
671
672 for (;;) {
673 XmlUtils.nextElement(parser);
674 String element = parser.getName();
675 if (element == null) {
676 break;
677 }
678 if (element.equals("keyboard-layout")) {
679 TypedArray a = resources.obtainAttributes(
680 parser, com.android.internal.R.styleable.KeyboardLayout);
681 try {
682 String name = a.getString(
683 com.android.internal.R.styleable.KeyboardLayout_name);
684 String label = a.getString(
685 com.android.internal.R.styleable.KeyboardLayout_label);
Jeff Brown2f095762012-05-10 21:29:33 -0700686 int keyboardLayoutResId = a.getResourceId(
687 com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
688 0);
689 if (name == null || label == null || keyboardLayoutResId == 0) {
690 Log.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700691 + "attributes in keyboard layout "
692 + "resource from receiver "
693 + receiver.packageName + "/" + receiver.name);
694 } else {
695 String descriptor = KeyboardLayoutDescriptor.format(
696 receiver.packageName, receiver.name, name);
Jeff Brown6ec6f792012-04-17 16:52:41 -0700697 if (keyboardName == null || name.equals(keyboardName)) {
698 visitor.visitKeyboardLayout(resources, descriptor,
Jeff Brown2f095762012-05-10 21:29:33 -0700699 label, keyboardLayoutResId);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700700 }
701 }
702 } finally {
703 a.recycle();
704 }
705 } else {
706 Log.w(TAG, "Skipping unrecognized element '" + element
707 + "' in keyboard layout resource from receiver "
708 + receiver.packageName + "/" + receiver.name);
709 }
710 }
711 } finally {
712 parser.close();
713 }
714 } catch (Exception ex) {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700715 Log.w(TAG, "Could not parse keyboard layout resource from receiver "
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700716 + receiver.packageName + "/" + receiver.name, ex);
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700717 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700718 }
719
720 @Override // Binder call
721 public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
722 if (inputDeviceDescriptor == null) {
723 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
724 }
725
Jeff Browna3bc5652012-04-17 11:42:25 -0700726 synchronized (mDataStore) {
727 return mDataStore.getKeyboardLayout(inputDeviceDescriptor);
728 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700729 }
730
731 @Override // Binder call
732 public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
733 String keyboardLayoutDescriptor) {
734 if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
735 "setKeyboardLayoutForInputDevice()")) {
736 throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
737 }
738
739 if (inputDeviceDescriptor == null) {
740 throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
741 }
742
Jeff Brown6ec6f792012-04-17 16:52:41 -0700743 final boolean changed;
Jeff Browna3bc5652012-04-17 11:42:25 -0700744 synchronized (mDataStore) {
745 try {
Jeff Brown6ec6f792012-04-17 16:52:41 -0700746 changed = mDataStore.setKeyboardLayout(
747 inputDeviceDescriptor, keyboardLayoutDescriptor);
Jeff Browna3bc5652012-04-17 11:42:25 -0700748 } finally {
749 mDataStore.saveIfNeeded();
750 }
751 }
Jeff Brown6ec6f792012-04-17 16:52:41 -0700752
753 if (changed) {
754 if (DEBUG) {
755 Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
756 }
757 reloadKeyboardLayouts();
758 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700759 }
760
Jeff Brown9302c872011-07-13 22:51:29 -0700761 public void setInputWindows(InputWindowHandle[] windowHandles) {
Jeff Brown4532e612012-04-05 14:27:12 -0700762 nativeSetInputWindows(mPtr, windowHandles);
Jeff Brown349703e2010-06-22 01:27:15 -0700763 }
764
Jeff Brown9302c872011-07-13 22:51:29 -0700765 public void setFocusedApplication(InputApplicationHandle application) {
Jeff Brown4532e612012-04-05 14:27:12 -0700766 nativeSetFocusedApplication(mPtr, application);
Jeff Brown349703e2010-06-22 01:27:15 -0700767 }
768
Jeff Brown349703e2010-06-22 01:27:15 -0700769 public void setInputDispatchMode(boolean enabled, boolean frozen) {
Jeff Brown4532e612012-04-05 14:27:12 -0700770 nativeSetInputDispatchMode(mPtr, enabled, frozen);
Jeff Brown349703e2010-06-22 01:27:15 -0700771 }
Jeff Brown05dc66a2011-03-02 14:41:58 -0800772
773 public void setSystemUiVisibility(int visibility) {
Jeff Brown4532e612012-04-05 14:27:12 -0700774 nativeSetSystemUiVisibility(mPtr, visibility);
Jeff Brown05dc66a2011-03-02 14:41:58 -0800775 }
776
Jeff Browne6504122010-09-27 14:52:15 -0700777 /**
778 * Atomically transfers touch focus from one window to another as identified by
779 * their input channels. It is possible for multiple windows to have
780 * touch focus if they support split touch dispatch
781 * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
782 * method only transfers touch focus of the specified window without affecting
783 * other windows that may also have touch focus at the same time.
784 * @param fromChannel The channel of a window that currently has touch focus.
785 * @param toChannel The channel of the window that should receive touch focus in
786 * place of the first.
787 * @return True if the transfer was successful. False if the window with the
788 * specified channel did not actually have touch focus at the time of the request.
789 */
790 public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
791 if (fromChannel == null) {
792 throw new IllegalArgumentException("fromChannel must not be null.");
793 }
794 if (toChannel == null) {
795 throw new IllegalArgumentException("toChannel must not be null.");
796 }
Jeff Brown4532e612012-04-05 14:27:12 -0700797 return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
Jeff Browne6504122010-09-27 14:52:15 -0700798 }
Jeff Brown05dc66a2011-03-02 14:41:58 -0800799
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700800 @Override // Binder call
Jeff Brownac143512012-04-05 18:57:33 -0700801 public void tryPointerSpeed(int speed) {
802 if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
803 "tryPointerSpeed()")) {
804 throw new SecurityException("Requires SET_POINTER_SPEED permission");
805 }
806
Jeff Brown9f25b7f2012-04-10 14:30:49 -0700807 if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
808 throw new IllegalArgumentException("speed out of range");
809 }
810
Jeff Brownac143512012-04-05 18:57:33 -0700811 setPointerSpeedUnchecked(speed);
Jeff Brown1a84fd12011-06-02 01:26:32 -0700812 }
813
814 public void updatePointerSpeedFromSettings() {
Jeff Brownac143512012-04-05 18:57:33 -0700815 int speed = getPointerSpeedSetting();
816 setPointerSpeedUnchecked(speed);
817 }
818
819 private void setPointerSpeedUnchecked(int speed) {
820 speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
821 InputManager.MAX_POINTER_SPEED);
822 nativeSetPointerSpeed(mPtr, speed);
Jeff Brown1a84fd12011-06-02 01:26:32 -0700823 }
824
825 private void registerPointerSpeedSettingObserver() {
826 mContext.getContentResolver().registerContentObserver(
827 Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
Jeff Brown4532e612012-04-05 14:27:12 -0700828 new ContentObserver(mHandler) {
Jeff Brown1a84fd12011-06-02 01:26:32 -0700829 @Override
830 public void onChange(boolean selfChange) {
831 updatePointerSpeedFromSettings();
832 }
833 });
834 }
835
Jeff Brownac143512012-04-05 18:57:33 -0700836 private int getPointerSpeedSetting() {
837 int speed = InputManager.DEFAULT_POINTER_SPEED;
Jeff Brown1a84fd12011-06-02 01:26:32 -0700838 try {
839 speed = Settings.System.getInt(mContext.getContentResolver(),
840 Settings.System.POINTER_SPEED);
841 } catch (SettingNotFoundException snfe) {
842 }
843 return speed;
844 }
845
Jeff Browndaf4a122011-08-26 17:14:14 -0700846 public void updateShowTouchesFromSettings() {
847 int setting = getShowTouchesSetting(0);
Jeff Brown4532e612012-04-05 14:27:12 -0700848 nativeSetShowTouches(mPtr, setting != 0);
Jeff Browndaf4a122011-08-26 17:14:14 -0700849 }
850
851 private void registerShowTouchesSettingObserver() {
852 mContext.getContentResolver().registerContentObserver(
853 Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
Jeff Brown4532e612012-04-05 14:27:12 -0700854 new ContentObserver(mHandler) {
Jeff Browndaf4a122011-08-26 17:14:14 -0700855 @Override
856 public void onChange(boolean selfChange) {
857 updateShowTouchesFromSettings();
858 }
859 });
860 }
861
862 private int getShowTouchesSetting(int defaultValue) {
863 int result = defaultValue;
864 try {
865 result = Settings.System.getInt(mContext.getContentResolver(),
866 Settings.System.SHOW_TOUCHES);
867 } catch (SettingNotFoundException snfe) {
868 }
869 return result;
870 }
871
Jeff Browna47425a2012-04-13 04:09:27 -0700872 // Binder call
873 @Override
874 public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
875 if (repeat >= pattern.length) {
876 throw new ArrayIndexOutOfBoundsException();
877 }
878
879 VibratorToken v;
880 synchronized (mVibratorLock) {
881 v = mVibratorTokens.get(token);
882 if (v == null) {
883 v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
884 try {
885 token.linkToDeath(v, 0);
886 } catch (RemoteException ex) {
887 // give up
888 throw new RuntimeException(ex);
889 }
890 mVibratorTokens.put(token, v);
891 }
892 }
893
894 synchronized (v) {
895 v.mVibrating = true;
896 nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
897 }
898 }
899
900 // Binder call
901 @Override
902 public void cancelVibrate(int deviceId, IBinder token) {
903 VibratorToken v;
904 synchronized (mVibratorLock) {
905 v = mVibratorTokens.get(token);
906 if (v == null || v.mDeviceId != deviceId) {
907 return; // nothing to cancel
908 }
909 }
910
911 cancelVibrateIfNeeded(v);
912 }
913
914 void onVibratorTokenDied(VibratorToken v) {
915 synchronized (mVibratorLock) {
916 mVibratorTokens.remove(v.mToken);
917 }
918
919 cancelVibrateIfNeeded(v);
920 }
921
922 private void cancelVibrateIfNeeded(VibratorToken v) {
923 synchronized (v) {
924 if (v.mVibrating) {
925 nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
926 v.mVibrating = false;
927 }
928 }
929 }
930
Jeff Brown4532e612012-04-05 14:27:12 -0700931 @Override
932 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Browna3bc5652012-04-17 11:42:25 -0700933 if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
Jeff Brown4532e612012-04-05 14:27:12 -0700934 != PackageManager.PERMISSION_GRANTED) {
935 pw.println("Permission Denial: can't dump InputManager from from pid="
936 + Binder.getCallingPid()
937 + ", uid=" + Binder.getCallingUid());
938 return;
939 }
940
941 pw.println("INPUT MANAGER (dumpsys input)\n");
942 String dumpStr = nativeDump(mPtr);
Jeff Browne33348b2010-07-15 23:54:05 -0700943 if (dumpStr != null) {
944 pw.println(dumpStr);
945 }
Jeff Brown46b9ac02010-04-22 18:58:52 -0700946 }
Jeff Brownb4ff35d2011-01-02 16:37:43 -0800947
Jeff Brownac143512012-04-05 18:57:33 -0700948 private boolean checkCallingPermission(String permission, String func) {
949 // Quick check: if the calling permission is me, it's all okay.
950 if (Binder.getCallingPid() == Process.myPid()) {
951 return true;
952 }
953
954 if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
955 return true;
956 }
957 String msg = "Permission Denial: " + func + " from pid="
958 + Binder.getCallingPid()
959 + ", uid=" + Binder.getCallingUid()
960 + " requires " + permission;
961 Slog.w(TAG, msg);
962 return false;
963 }
964
Jeff Brown4532e612012-04-05 14:27:12 -0700965 // Called by the heartbeat to ensure locks are not held indefinitely (for deadlock detection).
Jeff Brown89ef0722011-08-10 16:25:21 -0700966 public void monitor() {
967 synchronized (mInputFilterLock) { }
Jeff Brown4532e612012-04-05 14:27:12 -0700968 nativeMonitor(mPtr);
Jeff Brown89ef0722011-08-10 16:25:21 -0700969 }
970
Jeff Brown4532e612012-04-05 14:27:12 -0700971 // Native callback.
972 private void notifyConfigurationChanged(long whenNanos) {
973 mCallbacks.notifyConfigurationChanged();
974 }
975
976 // Native callback.
Jeff Brownaf9e8d32012-04-12 17:32:48 -0700977 private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
978 synchronized (mInputDevicesLock) {
979 mInputDevices = inputDevices;
980
981 if (!mInputDevicesChangedPending) {
982 mInputDevicesChangedPending = true;
983 mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED);
984 }
985 }
986 }
987
988 // Native callback.
Jeff Brown4532e612012-04-05 14:27:12 -0700989 private void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
990 mCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
991 }
992
993 // Native callback.
994 private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
995 mCallbacks.notifyInputChannelBroken(inputWindowHandle);
996 }
997
998 // Native callback.
999 private long notifyANR(InputApplicationHandle inputApplicationHandle,
1000 InputWindowHandle inputWindowHandle) {
1001 return mCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
1002 }
1003
1004 // Native callback.
1005 final boolean filterInputEvent(InputEvent event, int policyFlags) {
1006 synchronized (mInputFilterLock) {
1007 if (mInputFilter != null) {
1008 mInputFilter.filterInputEvent(event, policyFlags);
1009 return false;
1010 }
1011 }
1012 event.recycle();
1013 return true;
1014 }
1015
1016 // Native callback.
1017 private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
1018 return mCallbacks.interceptKeyBeforeQueueing(
1019 event, policyFlags, isScreenOn);
1020 }
1021
1022 // Native callback.
1023 private int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
1024 return mCallbacks.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
1025 }
1026
1027 // Native callback.
1028 private long interceptKeyBeforeDispatching(InputWindowHandle focus,
1029 KeyEvent event, int policyFlags) {
1030 return mCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
1031 }
1032
1033 // Native callback.
1034 private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
1035 KeyEvent event, int policyFlags) {
1036 return mCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
1037 }
1038
1039 // Native callback.
1040 private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
1041 return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
1042 injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
1043 }
1044
1045 // Native callback.
1046 private int getVirtualKeyQuietTimeMillis() {
1047 return mContext.getResources().getInteger(
1048 com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
1049 }
1050
1051 // Native callback.
1052 private String[] getExcludedDeviceNames() {
1053 ArrayList<String> names = new ArrayList<String>();
1054
1055 // Read partner-provided list of excluded input devices
1056 XmlPullParser parser = null;
1057 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
1058 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
1059 FileReader confreader = null;
1060 try {
1061 confreader = new FileReader(confFile);
1062 parser = Xml.newPullParser();
1063 parser.setInput(confreader);
1064 XmlUtils.beginDocument(parser, "devices");
1065
1066 while (true) {
1067 XmlUtils.nextElement(parser);
1068 if (!"device".equals(parser.getName())) {
1069 break;
1070 }
1071 String name = parser.getAttributeValue(null, "name");
1072 if (name != null) {
1073 names.add(name);
1074 }
1075 }
1076 } catch (FileNotFoundException e) {
1077 // It's ok if the file does not exist.
1078 } catch (Exception e) {
1079 Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
1080 } finally {
1081 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
1082 }
1083
1084 return names.toArray(new String[names.size()]);
1085 }
1086
1087 // Native callback.
1088 private int getKeyRepeatTimeout() {
1089 return ViewConfiguration.getKeyRepeatTimeout();
1090 }
1091
1092 // Native callback.
1093 private int getKeyRepeatDelay() {
1094 return ViewConfiguration.getKeyRepeatDelay();
1095 }
1096
1097 // Native callback.
1098 private int getHoverTapTimeout() {
1099 return ViewConfiguration.getHoverTapTimeout();
1100 }
1101
1102 // Native callback.
1103 private int getHoverTapSlop() {
1104 return ViewConfiguration.getHoverTapSlop();
1105 }
1106
1107 // Native callback.
1108 private int getDoubleTapTimeout() {
1109 return ViewConfiguration.getDoubleTapTimeout();
1110 }
1111
1112 // Native callback.
1113 private int getLongPressTimeout() {
1114 return ViewConfiguration.getLongPressTimeout();
1115 }
1116
1117 // Native callback.
1118 private int getPointerLayer() {
1119 return mCallbacks.getPointerLayer();
1120 }
1121
1122 // Native callback.
1123 private PointerIcon getPointerIcon() {
1124 return PointerIcon.getDefaultIcon(mContext);
1125 }
1126
Jeff Brown6ec6f792012-04-17 16:52:41 -07001127 // Native callback.
1128 private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
1129 if (!mSystemReady) {
1130 return null;
1131 }
1132
1133 String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
1134 if (keyboardLayoutDescriptor == null) {
1135 return null;
1136 }
1137
1138 final String[] result = new String[2];
1139 visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
1140 @Override
1141 public void visitKeyboardLayout(Resources resources,
Jeff Brown2f095762012-05-10 21:29:33 -07001142 String descriptor, String label, int keyboardLayoutResId) {
Jeff Brown6ec6f792012-04-17 16:52:41 -07001143 try {
1144 result[0] = descriptor;
1145 result[1] = Streams.readFully(new InputStreamReader(
Jeff Brown2f095762012-05-10 21:29:33 -07001146 resources.openRawResource(keyboardLayoutResId)));
Jeff Brown6ec6f792012-04-17 16:52:41 -07001147 } catch (IOException ex) {
1148 } catch (NotFoundException ex) {
1149 }
1150 }
1151 });
1152 if (result[0] == null) {
1153 Log.w(TAG, "Could not get keyboard layout with descriptor '"
1154 + keyboardLayoutDescriptor + "'.");
1155 return null;
1156 }
1157 return result;
1158 }
1159
Jeff Brown5bbd4b42012-04-20 19:28:00 -07001160 // Native callback.
1161 private String getDeviceAlias(String uniqueId) {
1162 if (mBluetoothService != null &&
1163 BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
1164 return mBluetoothService.getRemoteAlias(uniqueId);
1165 }
1166 return null;
1167 }
1168
Jeff Brown6ec6f792012-04-17 16:52:41 -07001169
Jeff Brown4532e612012-04-05 14:27:12 -07001170 /**
1171 * Callback interface implemented by the Window Manager.
1172 */
1173 public interface Callbacks {
1174 public void notifyConfigurationChanged();
1175
1176 public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
1177
1178 public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
1179
1180 public long notifyANR(InputApplicationHandle inputApplicationHandle,
1181 InputWindowHandle inputWindowHandle);
1182
1183 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
1184
1185 public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags);
1186
1187 public long interceptKeyBeforeDispatching(InputWindowHandle focus,
1188 KeyEvent event, int policyFlags);
1189
1190 public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
1191 KeyEvent event, int policyFlags);
1192
1193 public int getPointerLayer();
1194 }
1195
1196 /**
Jeff Brownaf9e8d32012-04-12 17:32:48 -07001197 * Private handler for the input manager.
1198 */
1199 private final class InputManagerHandler extends Handler {
1200 @Override
1201 public void handleMessage(Message msg) {
1202 switch (msg.what) {
1203 case MSG_DELIVER_INPUT_DEVICES_CHANGED:
1204 deliverInputDevicesChanged();
1205 break;
1206 }
1207 }
1208 }
1209
1210 /**
Jeff Brown4532e612012-04-05 14:27:12 -07001211 * Hosting interface for input filters to call back into the input manager.
1212 */
Jeff Brown0029c662011-03-30 02:25:18 -07001213 private final class InputFilterHost implements InputFilter.Host {
1214 private boolean mDisconnected;
1215
1216 public void disconnectLocked() {
1217 mDisconnected = true;
1218 }
1219
1220 public void sendInputEvent(InputEvent event, int policyFlags) {
1221 if (event == null) {
1222 throw new IllegalArgumentException("event must not be null");
1223 }
1224
1225 synchronized (mInputFilterLock) {
1226 if (!mDisconnected) {
Jeff Brownac143512012-04-05 18:57:33 -07001227 nativeInjectInputEvent(mPtr, event, 0, 0,
1228 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
Jeff Brown0029c662011-03-30 02:25:18 -07001229 policyFlags | WindowManagerPolicy.FLAG_FILTERED);
1230 }
1231 }
1232 }
1233 }
Jeff Brown9f25b7f2012-04-10 14:30:49 -07001234
1235 private static final class KeyboardLayoutDescriptor {
1236 public String packageName;
1237 public String receiverName;
1238 public String keyboardLayoutName;
1239
1240 public static String format(String packageName,
1241 String receiverName, String keyboardName) {
1242 return packageName + "/" + receiverName + "/" + keyboardName;
1243 }
1244
1245 public static KeyboardLayoutDescriptor parse(String descriptor) {
1246 int pos = descriptor.indexOf('/');
1247 if (pos < 0 || pos + 1 == descriptor.length()) {
1248 return null;
1249 }
1250 int pos2 = descriptor.indexOf('/', pos + 1);
1251 if (pos2 < pos + 2 || pos2 + 1 == descriptor.length()) {
1252 return null;
1253 }
1254
1255 KeyboardLayoutDescriptor result = new KeyboardLayoutDescriptor();
1256 result.packageName = descriptor.substring(0, pos);
1257 result.receiverName = descriptor.substring(pos + 1, pos2);
1258 result.keyboardLayoutName = descriptor.substring(pos2 + 1);
1259 return result;
1260 }
1261 }
Jeff Brownaf9e8d32012-04-12 17:32:48 -07001262
Jeff Brown6ec6f792012-04-17 16:52:41 -07001263 private interface KeyboardLayoutVisitor {
1264 void visitKeyboardLayout(Resources resources,
Jeff Brown2f095762012-05-10 21:29:33 -07001265 String descriptor, String label, int keyboardLayoutResId);
Jeff Brown6ec6f792012-04-17 16:52:41 -07001266 }
1267
Jeff Brownaf9e8d32012-04-12 17:32:48 -07001268 private final class InputDevicesChangedListenerRecord implements DeathRecipient {
1269 private final int mPid;
1270 private final IInputDevicesChangedListener mListener;
1271
1272 public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) {
1273 mPid = pid;
1274 mListener = listener;
1275 }
1276
1277 @Override
1278 public void binderDied() {
1279 if (DEBUG) {
1280 Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died.");
1281 }
1282 onInputDevicesChangedListenerDied(mPid);
1283 }
1284
1285 public void notifyInputDevicesChanged(int[] info) {
1286 try {
1287 mListener.onInputDevicesChanged(info);
1288 } catch (RemoteException ex) {
1289 Slog.w(TAG, "Failed to notify process "
1290 + mPid + " that input devices changed, assuming it died.", ex);
1291 binderDied();
1292 }
1293 }
1294 }
Jeff Browna47425a2012-04-13 04:09:27 -07001295
1296 private final class VibratorToken implements DeathRecipient {
1297 public final int mDeviceId;
1298 public final IBinder mToken;
1299 public final int mTokenValue;
1300
1301 public boolean mVibrating;
1302
1303 public VibratorToken(int deviceId, IBinder token, int tokenValue) {
1304 mDeviceId = deviceId;
1305 mToken = token;
1306 mTokenValue = tokenValue;
1307 }
1308
1309 @Override
1310 public void binderDied() {
1311 if (DEBUG) {
1312 Slog.d(TAG, "Vibrator token died.");
1313 }
1314 onVibratorTokenDied(this);
1315 }
1316 }
Jeff Browna3bc5652012-04-17 11:42:25 -07001317
1318 /**
1319 * Manages persistent state recorded by the input manager service as an XML file.
1320 * Caller must acquire lock on the data store before accessing it.
1321 *
1322 * File format:
1323 * <code>
1324 * &lt;input-mananger-state>
1325 * &lt;input-devices>
1326 * &lt;input-device descriptor="xxxxx" keyboard-layout="yyyyy" />
1327 * &gt;input-devices>
1328 * &gt;/input-manager-state>
1329 * </code>
1330 */
1331 private static final class PersistentDataStore {
1332 // Input device state by descriptor.
1333 private final HashMap<String, InputDeviceState> mInputDevices =
1334 new HashMap<String, InputDeviceState>();
1335 private final AtomicFile mAtomicFile;
1336
1337 // True if the data has been loaded.
1338 private boolean mLoaded;
1339
1340 // True if there are changes to be saved.
1341 private boolean mDirty;
1342
1343 public PersistentDataStore() {
1344 mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml"));
1345 }
1346
1347 public void saveIfNeeded() {
1348 if (mDirty) {
1349 save();
1350 mDirty = false;
1351 }
1352 }
1353
1354 public String getKeyboardLayout(String inputDeviceDescriptor) {
1355 InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
1356 return state != null ? state.keyboardLayoutDescriptor : null;
1357 }
1358
1359 public boolean setKeyboardLayout(String inputDeviceDescriptor,
1360 String keyboardLayoutDescriptor) {
1361 InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
1362 if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) {
1363 state.keyboardLayoutDescriptor = keyboardLayoutDescriptor;
1364 setDirty();
1365 return true;
1366 }
1367 return false;
1368 }
1369
1370 private InputDeviceState getInputDeviceState(String inputDeviceDescriptor,
1371 boolean createIfAbsent) {
1372 loadIfNeeded();
1373 InputDeviceState state = mInputDevices.get(inputDeviceDescriptor);
1374 if (state == null && createIfAbsent) {
1375 state = new InputDeviceState();
1376 mInputDevices.put(inputDeviceDescriptor, state);
1377 setDirty();
1378 }
1379 return state;
1380 }
1381
1382 private void loadIfNeeded() {
1383 if (!mLoaded) {
1384 load();
1385 mLoaded = true;
1386 }
1387 }
1388
1389 private void setDirty() {
1390 mDirty = true;
1391 }
1392
1393 private void clearState() {
1394 mInputDevices.clear();
1395 }
1396
1397 private void load() {
1398 clearState();
1399
1400 final InputStream is;
1401 try {
1402 is = mAtomicFile.openRead();
1403 } catch (FileNotFoundException ex) {
1404 return;
1405 }
1406
1407 XmlPullParser parser;
1408 try {
1409 parser = Xml.newPullParser();
1410 parser.setInput(new BufferedInputStream(is), null);
1411 loadFromXml(parser);
1412 } catch (IOException ex) {
1413 Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
1414 clearState();
1415 } catch (XmlPullParserException ex) {
1416 Slog.w(TAG, "Failed to load input manager persistent store data.", ex);
1417 clearState();
1418 } finally {
1419 IoUtils.closeQuietly(is);
1420 }
1421 }
1422
1423 private void save() {
1424 final FileOutputStream os;
1425 try {
1426 os = mAtomicFile.startWrite();
1427 boolean success = false;
1428 try {
1429 XmlSerializer serializer = new FastXmlSerializer();
1430 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
1431 saveToXml(serializer);
1432 serializer.flush();
1433 success = true;
1434 } finally {
1435 if (success) {
1436 mAtomicFile.finishWrite(os);
1437 } else {
1438 mAtomicFile.failWrite(os);
1439 }
1440 }
1441 } catch (IOException ex) {
1442 Slog.w(TAG, "Failed to save input manager persistent store data.", ex);
1443 }
1444 }
1445
1446 private void loadFromXml(XmlPullParser parser)
1447 throws IOException, XmlPullParserException {
1448 XmlUtils.beginDocument(parser, "input-manager-state");
1449 final int outerDepth = parser.getDepth();
1450 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1451 if (parser.getName().equals("input-devices")) {
1452 loadInputDevicesFromXml(parser);
1453 }
1454 }
1455 }
1456
1457 private void loadInputDevicesFromXml(XmlPullParser parser)
1458 throws IOException, XmlPullParserException {
1459 final int outerDepth = parser.getDepth();
1460 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1461 if (parser.getName().equals("input-device")) {
1462 String descriptor = parser.getAttributeValue(null, "descriptor");
1463 if (descriptor == null) {
1464 throw new XmlPullParserException(
1465 "Missing descriptor attribute on input-device");
1466 }
1467 InputDeviceState state = new InputDeviceState();
1468 state.keyboardLayoutDescriptor =
1469 parser.getAttributeValue(null, "keyboard-layout");
1470 mInputDevices.put(descriptor, state);
1471 }
1472 }
1473 }
1474
1475 private void saveToXml(XmlSerializer serializer) throws IOException {
1476 serializer.startDocument(null, true);
1477 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
1478 serializer.startTag(null, "input-manager-state");
1479 serializer.startTag(null, "input-devices");
1480 for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
1481 final String descriptor = entry.getKey();
1482 final InputDeviceState state = entry.getValue();
1483 serializer.startTag(null, "input-device");
1484 serializer.attribute(null, "descriptor", descriptor);
1485 if (state.keyboardLayoutDescriptor != null) {
1486 serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor);
1487 }
1488 serializer.endTag(null, "input-device");
1489 }
1490 serializer.endTag(null, "input-devices");
1491 serializer.endTag(null, "input-manager-state");
1492 serializer.endDocument();
1493 }
1494 }
1495
1496 private static final class InputDeviceState {
1497 public String keyboardLayoutDescriptor;
1498 }
Jeff Brown46b9ac02010-04-22 18:58:52 -07001499}