InputManagerService.java revision 5338428ddbe662283bf88171c7ca361d51d78da8
1/*
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.input;
18
19import com.android.internal.R;
20import com.android.internal.util.XmlUtils;
21import com.android.server.Watchdog;
22
23import org.xmlpull.v1.XmlPullParser;
24
25import android.Manifest;
26import android.app.Notification;
27import android.app.NotificationManager;
28import android.app.PendingIntent;
29import android.bluetooth.BluetoothAdapter;
30import android.bluetooth.BluetoothDevice;
31import android.content.BroadcastReceiver;
32import android.content.ComponentName;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
39import android.content.pm.PackageManager.NameNotFoundException;
40import android.content.res.Resources;
41import android.content.res.Resources.NotFoundException;
42import android.content.res.TypedArray;
43import android.content.res.XmlResourceParser;
44import android.database.ContentObserver;
45import android.hardware.input.IInputDevicesChangedListener;
46import android.hardware.input.IInputManager;
47import android.hardware.input.InputManager;
48import android.hardware.input.KeyboardLayout;
49import android.os.Binder;
50import android.os.Bundle;
51import android.os.Environment;
52import android.os.Handler;
53import android.os.IBinder;
54import android.os.Message;
55import android.os.MessageQueue;
56import android.os.Process;
57import android.os.RemoteException;
58import android.provider.Settings;
59import android.provider.Settings.SettingNotFoundException;
60import android.util.Log;
61import android.util.Slog;
62import android.util.SparseArray;
63import android.util.Xml;
64import android.view.IInputFilter;
65import android.view.IInputFilterHost;
66import android.view.InputChannel;
67import android.view.InputDevice;
68import android.view.InputEvent;
69import android.view.KeyEvent;
70import android.view.PointerIcon;
71import android.view.Surface;
72import android.view.ViewConfiguration;
73import android.view.WindowManagerPolicy;
74import android.widget.Toast;
75
76import java.io.File;
77import java.io.FileDescriptor;
78import java.io.FileNotFoundException;
79import java.io.FileReader;
80import java.io.IOException;
81import java.io.InputStreamReader;
82import java.io.PrintWriter;
83import java.util.ArrayList;
84import java.util.HashMap;
85import java.util.HashSet;
86
87import libcore.io.Streams;
88import libcore.util.Objects;
89
90/*
91 * Wraps the C++ InputManager and provides its callbacks.
92 */
93public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
94    static final String TAG = "InputManager";
95    static final boolean DEBUG = false;
96
97    private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
98
99    private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
100    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
101    private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
102    private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
103    private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
104
105    // Pointer to native input manager service object.
106    private final int mPtr;
107
108    private final Context mContext;
109    private final Callbacks mCallbacks;
110    private final InputManagerHandler mHandler;
111    private boolean mSystemReady;
112    private NotificationManager mNotificationManager;
113
114    // Persistent data store.  Must be locked each time during use.
115    private final PersistentDataStore mDataStore = new PersistentDataStore();
116
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    private final ArrayList<InputDevice>
127            mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
128    private boolean mKeyboardLayoutNotificationShown;
129    private PendingIntent mKeyboardLayoutIntent;
130    private Toast mSwitchedKeyboardLayoutToast;
131
132    // State for vibrator tokens.
133    private Object mVibratorLock = new Object();
134    private HashMap<IBinder, VibratorToken> mVibratorTokens =
135            new HashMap<IBinder, VibratorToken>();
136    private int mNextVibratorTokenValue;
137
138    // State for the currently installed input filter.
139    final Object mInputFilterLock = new Object();
140    IInputFilter mInputFilter; // guarded by mInputFilterLock
141    InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
142
143    private static native int nativeInit(InputManagerService service,
144            Context context, MessageQueue messageQueue);
145    private static native void nativeStart(int ptr);
146    private static native void nativeSetDisplaySize(int ptr, int displayId,
147            int width, int height, int externalWidth, int externalHeight);
148    private static native void nativeSetDisplayOrientation(int ptr, int displayId,
149            int rotation, int externalRotation);
150
151    private static native int nativeGetScanCodeState(int ptr,
152            int deviceId, int sourceMask, int scanCode);
153    private static native int nativeGetKeyCodeState(int ptr,
154            int deviceId, int sourceMask, int keyCode);
155    private static native int nativeGetSwitchState(int ptr,
156            int deviceId, int sourceMask, int sw);
157    private static native boolean nativeHasKeys(int ptr,
158            int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
159    private static native void nativeRegisterInputChannel(int ptr, InputChannel inputChannel,
160            InputWindowHandle inputWindowHandle, boolean monitor);
161    private static native void nativeUnregisterInputChannel(int ptr, InputChannel inputChannel);
162    private static native void nativeSetInputFilterEnabled(int ptr, boolean enable);
163    private static native int nativeInjectInputEvent(int ptr, InputEvent event,
164            int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
165            int policyFlags);
166    private static native void nativeSetInputWindows(int ptr, InputWindowHandle[] windowHandles);
167    private static native void nativeSetInputDispatchMode(int ptr, boolean enabled, boolean frozen);
168    private static native void nativeSetSystemUiVisibility(int ptr, int visibility);
169    private static native void nativeSetFocusedApplication(int ptr,
170            InputApplicationHandle application);
171    private static native boolean nativeTransferTouchFocus(int ptr,
172            InputChannel fromChannel, InputChannel toChannel);
173    private static native void nativeSetPointerSpeed(int ptr, int speed);
174    private static native void nativeSetShowTouches(int ptr, boolean enabled);
175    private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
176            int repeat, int token);
177    private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
178    private static native void nativeReloadKeyboardLayouts(int ptr);
179    private static native void nativeReloadDeviceAliases(int ptr);
180    private static native String nativeDump(int ptr);
181    private static native void nativeMonitor(int ptr);
182
183    // Input event injection constants defined in InputDispatcher.h.
184    private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
185    private static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
186    private static final int INPUT_EVENT_INJECTION_FAILED = 2;
187    private static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
188
189    // Maximum number of milliseconds to wait for input event injection.
190    private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
191
192    // Key states (may be returned by queries about the current state of a
193    // particular key code, scan code or switch).
194
195    /** The key state is unknown or the requested key itself is not supported. */
196    public static final int KEY_STATE_UNKNOWN = -1;
197
198    /** The key is up. /*/
199    public static final int KEY_STATE_UP = 0;
200
201    /** The key is down. */
202    public static final int KEY_STATE_DOWN = 1;
203
204    /** The key is down but is a virtual key press that is being emulated by the system. */
205    public static final int KEY_STATE_VIRTUAL = 2;
206
207    /** Scan code: Mouse / trackball button. */
208    public static final int BTN_MOUSE = 0x110;
209
210    /** Switch code: Lid switch.  When set, lid is shut. */
211    public static final int SW_LID = 0x00;
212
213    /** Switch code: Keypad slide.  When set, keyboard is exposed. */
214    public static final int SW_KEYPAD_SLIDE = 0x0a;
215
216    public InputManagerService(Context context, Callbacks callbacks) {
217        this.mContext = context;
218        this.mCallbacks = callbacks;
219        this.mHandler = new InputManagerHandler();
220
221        Slog.i(TAG, "Initializing input manager");
222        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
223    }
224
225    public void start() {
226        Slog.i(TAG, "Starting input manager");
227        nativeStart(mPtr);
228
229        // Add ourself to the Watchdog monitors.
230        Watchdog.getInstance().addMonitor(this);
231
232        registerPointerSpeedSettingObserver();
233        registerShowTouchesSettingObserver();
234
235        updatePointerSpeedFromSettings();
236        updateShowTouchesFromSettings();
237    }
238
239    // TODO(BT) Pass in paramter for bluetooth system
240    public void systemReady() {
241        if (DEBUG) {
242            Slog.d(TAG, "System ready.");
243        }
244        mNotificationManager = (NotificationManager)mContext.getSystemService(
245                Context.NOTIFICATION_SERVICE);
246        mSystemReady = true;
247
248        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
249        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
250        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
251        filter.addDataScheme("package");
252        mContext.registerReceiver(new BroadcastReceiver() {
253            @Override
254            public void onReceive(Context context, Intent intent) {
255                updateKeyboardLayouts();
256            }
257        }, filter, null, mHandler);
258
259        filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
260        mContext.registerReceiver(new BroadcastReceiver() {
261            @Override
262            public void onReceive(Context context, Intent intent) {
263                reloadDeviceAliases();
264            }
265        }, filter, null, mHandler);
266
267        mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
268        mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
269    }
270
271    private void reloadKeyboardLayouts() {
272        if (DEBUG) {
273            Slog.d(TAG, "Reloading keyboard layouts.");
274        }
275        nativeReloadKeyboardLayouts(mPtr);
276    }
277
278    private void reloadDeviceAliases() {
279        if (DEBUG) {
280            Slog.d(TAG, "Reloading device names.");
281        }
282        nativeReloadDeviceAliases(mPtr);
283    }
284
285    public void setDisplaySize(int displayId, int width, int height,
286            int externalWidth, int externalHeight) {
287        if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
288            throw new IllegalArgumentException("Invalid display id or dimensions.");
289        }
290
291        if (DEBUG) {
292            Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height
293                    + " external size " + externalWidth + "x" + externalHeight);
294        }
295        nativeSetDisplaySize(mPtr, displayId, width, height, externalWidth, externalHeight);
296    }
297
298    public void setDisplayOrientation(int displayId, int rotation, int externalRotation) {
299        if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
300            throw new IllegalArgumentException("Invalid rotation.");
301        }
302
303        if (DEBUG) {
304            Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation
305                    + " external rotation " + externalRotation);
306        }
307        nativeSetDisplayOrientation(mPtr, displayId, rotation, externalRotation);
308    }
309
310    /**
311     * Gets the current state of a key or button by key code.
312     * @param deviceId The input device id, or -1 to consult all devices.
313     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
314     * consider all input sources.  An input device is consulted if at least one of its
315     * non-class input source bits matches the specified source mask.
316     * @param keyCode The key code to check.
317     * @return The key state.
318     */
319    public int getKeyCodeState(int deviceId, int sourceMask, int keyCode) {
320        return nativeGetKeyCodeState(mPtr, deviceId, sourceMask, keyCode);
321    }
322
323    /**
324     * Gets the current state of a key or button by scan code.
325     * @param deviceId The input device id, or -1 to consult all devices.
326     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
327     * consider all input sources.  An input device is consulted if at least one of its
328     * non-class input source bits matches the specified source mask.
329     * @param scanCode The scan code to check.
330     * @return The key state.
331     */
332    public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
333        return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
334    }
335
336    /**
337     * Gets the current state of a switch by switch code.
338     * @param deviceId The input device id, or -1 to consult all devices.
339     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
340     * consider all input sources.  An input device is consulted if at least one of its
341     * non-class input source bits matches the specified source mask.
342     * @param switchCode The switch code to check.
343     * @return The switch state.
344     */
345    public int getSwitchState(int deviceId, int sourceMask, int switchCode) {
346        return nativeGetSwitchState(mPtr, deviceId, sourceMask, switchCode);
347    }
348
349    /**
350     * Determines whether the specified key codes are supported by a particular device.
351     * @param deviceId The input device id, or -1 to consult all devices.
352     * @param sourceMask The input sources to consult, or {@link InputDevice#SOURCE_ANY} to
353     * consider all input sources.  An input device is consulted if at least one of its
354     * non-class input source bits matches the specified source mask.
355     * @param keyCodes The array of key codes to check.
356     * @param keyExists An array at least as large as keyCodes whose entries will be set
357     * to true or false based on the presence or absence of support for the corresponding
358     * key codes.
359     * @return True if the lookup was successful, false otherwise.
360     */
361    @Override // Binder call
362    public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
363        if (keyCodes == null) {
364            throw new IllegalArgumentException("keyCodes must not be null.");
365        }
366        if (keyExists == null || keyExists.length < keyCodes.length) {
367            throw new IllegalArgumentException("keyExists must not be null and must be at "
368                    + "least as large as keyCodes.");
369        }
370
371        return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
372    }
373
374    /**
375     * Creates an input channel that will receive all input from the input dispatcher.
376     * @param inputChannelName The input channel name.
377     * @return The input channel.
378     */
379    public InputChannel monitorInput(String inputChannelName) {
380        if (inputChannelName == null) {
381            throw new IllegalArgumentException("inputChannelName must not be null.");
382        }
383
384        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
385        nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
386        inputChannels[0].dispose(); // don't need to retain the Java object reference
387        return inputChannels[1];
388    }
389
390    /**
391     * Registers an input channel so that it can be used as an input event target.
392     * @param inputChannel The input channel to register.
393     * @param inputWindowHandle The handle of the input window associated with the
394     * input channel, or null if none.
395     */
396    public void registerInputChannel(InputChannel inputChannel,
397            InputWindowHandle inputWindowHandle) {
398        if (inputChannel == null) {
399            throw new IllegalArgumentException("inputChannel must not be null.");
400        }
401
402        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
403    }
404
405    /**
406     * Unregisters an input channel.
407     * @param inputChannel The input channel to unregister.
408     */
409    public void unregisterInputChannel(InputChannel inputChannel) {
410        if (inputChannel == null) {
411            throw new IllegalArgumentException("inputChannel must not be null.");
412        }
413
414        nativeUnregisterInputChannel(mPtr, inputChannel);
415    }
416
417    /**
418     * Sets an input filter that will receive all input events before they are dispatched.
419     * The input filter may then reinterpret input events or inject new ones.
420     *
421     * To ensure consistency, the input dispatcher automatically drops all events
422     * in progress whenever an input filter is installed or uninstalled.  After an input
423     * filter is uninstalled, it can no longer send input events unless it is reinstalled.
424     * Any events it attempts to send after it has been uninstalled will be dropped.
425     *
426     * @param filter The input filter, or null to remove the current filter.
427     */
428    public void setInputFilter(IInputFilter filter) {
429        synchronized (mInputFilterLock) {
430            final IInputFilter oldFilter = mInputFilter;
431            if (oldFilter == filter) {
432                return; // nothing to do
433            }
434
435            if (oldFilter != null) {
436                mInputFilter = null;
437                mInputFilterHost.disconnectLocked();
438                mInputFilterHost = null;
439                try {
440                    oldFilter.uninstall();
441                } catch (RemoteException re) {
442                    /* ignore */
443                }
444            }
445
446            if (filter != null) {
447                mInputFilter = filter;
448                mInputFilterHost = new InputFilterHost();
449                try {
450                    filter.install(mInputFilterHost);
451                } catch (RemoteException re) {
452                    /* ignore */
453                }
454            }
455
456            nativeSetInputFilterEnabled(mPtr, filter != null);
457        }
458    }
459
460    @Override // Binder call
461    public boolean injectInputEvent(InputEvent event, int mode) {
462        if (event == null) {
463            throw new IllegalArgumentException("event must not be null");
464        }
465        if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
466                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
467                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
468            throw new IllegalArgumentException("mode is invalid");
469        }
470
471        final int pid = Binder.getCallingPid();
472        final int uid = Binder.getCallingUid();
473        final long ident = Binder.clearCallingIdentity();
474        final int result;
475        try {
476            result = nativeInjectInputEvent(mPtr, event, pid, uid, mode,
477                    INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
478        } finally {
479            Binder.restoreCallingIdentity(ident);
480        }
481        switch (result) {
482            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
483                Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
484                throw new SecurityException(
485                        "Injecting to another application requires INJECT_EVENTS permission");
486            case INPUT_EVENT_INJECTION_SUCCEEDED:
487                return true;
488            case INPUT_EVENT_INJECTION_TIMED_OUT:
489                Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
490                return false;
491            case INPUT_EVENT_INJECTION_FAILED:
492            default:
493                Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
494                return false;
495        }
496    }
497
498    /**
499     * Gets information about the input device with the specified id.
500     * @param id The device id.
501     * @return The input device or null if not found.
502     */
503    @Override // Binder call
504    public InputDevice getInputDevice(int deviceId) {
505        synchronized (mInputDevicesLock) {
506            final int count = mInputDevices.length;
507            for (int i = 0; i < count; i++) {
508                final InputDevice inputDevice = mInputDevices[i];
509                if (inputDevice.getId() == deviceId) {
510                    return inputDevice;
511                }
512            }
513        }
514        return null;
515    }
516
517    /**
518     * Gets the ids of all input devices in the system.
519     * @return The input device ids.
520     */
521    @Override // Binder call
522    public int[] getInputDeviceIds() {
523        synchronized (mInputDevicesLock) {
524            final int count = mInputDevices.length;
525            int[] ids = new int[count];
526            for (int i = 0; i < count; i++) {
527                ids[i] = mInputDevices[i].getId();
528            }
529            return ids;
530        }
531    }
532
533    /**
534     * Gets all input devices in the system.
535     * @return The array of input devices.
536     */
537    public InputDevice[] getInputDevices() {
538        synchronized (mInputDevicesLock) {
539            return mInputDevices;
540        }
541    }
542
543    @Override // Binder call
544    public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
545        if (listener == null) {
546            throw new IllegalArgumentException("listener must not be null");
547        }
548
549        synchronized (mInputDevicesLock) {
550            int callingPid = Binder.getCallingPid();
551            if (mInputDevicesChangedListeners.get(callingPid) != null) {
552                throw new SecurityException("The calling process has already "
553                        + "registered an InputDevicesChangedListener.");
554            }
555
556            InputDevicesChangedListenerRecord record =
557                    new InputDevicesChangedListenerRecord(callingPid, listener);
558            try {
559                IBinder binder = listener.asBinder();
560                binder.linkToDeath(record, 0);
561            } catch (RemoteException ex) {
562                // give up
563                throw new RuntimeException(ex);
564            }
565
566            mInputDevicesChangedListeners.put(callingPid, record);
567        }
568    }
569
570    private void onInputDevicesChangedListenerDied(int pid) {
571        synchronized (mInputDevicesLock) {
572            mInputDevicesChangedListeners.remove(pid);
573        }
574    }
575
576    // Must be called on handler.
577    private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) {
578        // Scan for changes.
579        int numFullKeyboardsAdded = 0;
580        mTempInputDevicesChangedListenersToNotify.clear();
581        mTempFullKeyboards.clear();
582        final int numListeners;
583        final int[] deviceIdAndGeneration;
584        synchronized (mInputDevicesLock) {
585            if (!mInputDevicesChangedPending) {
586                return;
587            }
588            mInputDevicesChangedPending = false;
589
590            numListeners = mInputDevicesChangedListeners.size();
591            for (int i = 0; i < numListeners; i++) {
592                mTempInputDevicesChangedListenersToNotify.add(
593                        mInputDevicesChangedListeners.valueAt(i));
594            }
595
596            final int numDevices = mInputDevices.length;
597            deviceIdAndGeneration = new int[numDevices * 2];
598            for (int i = 0; i < numDevices; i++) {
599                final InputDevice inputDevice = mInputDevices[i];
600                deviceIdAndGeneration[i * 2] = inputDevice.getId();
601                deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
602
603                if (!inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
604                    if (!containsInputDeviceWithDescriptor(oldInputDevices,
605                            inputDevice.getDescriptor())) {
606                        mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
607                    } else {
608                        mTempFullKeyboards.add(inputDevice);
609                    }
610                }
611            }
612        }
613
614        // Notify listeners.
615        for (int i = 0; i < numListeners; i++) {
616            mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
617                    deviceIdAndGeneration);
618        }
619        mTempInputDevicesChangedListenersToNotify.clear();
620
621        // Check for missing keyboard layouts.
622        if (mNotificationManager != null) {
623            final int numFullKeyboards = mTempFullKeyboards.size();
624            boolean missingLayoutForExternalKeyboard = false;
625            boolean missingLayoutForExternalKeyboardAdded = false;
626            synchronized (mDataStore) {
627                for (int i = 0; i < numFullKeyboards; i++) {
628                    final InputDevice inputDevice = mTempFullKeyboards.get(i);
629                    if (mDataStore.getCurrentKeyboardLayout(inputDevice.getDescriptor()) == null) {
630                        missingLayoutForExternalKeyboard = true;
631                        if (i < numFullKeyboardsAdded) {
632                            missingLayoutForExternalKeyboardAdded = true;
633                        }
634                    }
635                }
636            }
637            if (missingLayoutForExternalKeyboard) {
638                if (missingLayoutForExternalKeyboardAdded) {
639                    showMissingKeyboardLayoutNotification();
640                }
641            } else if (mKeyboardLayoutNotificationShown) {
642                hideMissingKeyboardLayoutNotification();
643            }
644        }
645        mTempFullKeyboards.clear();
646    }
647
648    // Must be called on handler.
649    private void showMissingKeyboardLayoutNotification() {
650        if (!mKeyboardLayoutNotificationShown) {
651            if (mKeyboardLayoutIntent == null) {
652                final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS");
653                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
654                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
655                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
656                mKeyboardLayoutIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
657            }
658
659            Resources r = mContext.getResources();
660            Notification notification = new Notification.Builder(mContext)
661                    .setContentTitle(r.getString(
662                            R.string.select_keyboard_layout_notification_title))
663                    .setContentText(r.getString(
664                            R.string.select_keyboard_layout_notification_message))
665                    .setContentIntent(mKeyboardLayoutIntent)
666                    .setSmallIcon(R.drawable.ic_settings_language)
667                    .setPriority(Notification.PRIORITY_LOW)
668                    .build();
669            mNotificationManager.notify(R.string.select_keyboard_layout_notification_title,
670                    notification);
671            mKeyboardLayoutNotificationShown = true;
672        }
673    }
674
675    // Must be called on handler.
676    private void hideMissingKeyboardLayoutNotification() {
677        if (mKeyboardLayoutNotificationShown) {
678            mKeyboardLayoutNotificationShown = false;
679            mNotificationManager.cancel(R.string.select_keyboard_layout_notification_title);
680        }
681    }
682
683    // Must be called on handler.
684    private void updateKeyboardLayouts() {
685        // Scan all input devices state for keyboard layouts that have been uninstalled.
686        final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
687        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
688            @Override
689            public void visitKeyboardLayout(Resources resources,
690                    String descriptor, String label, String collection, int keyboardLayoutResId) {
691                availableKeyboardLayouts.add(descriptor);
692            }
693        });
694        synchronized (mDataStore) {
695            try {
696                mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
697            } finally {
698                mDataStore.saveIfNeeded();
699            }
700        }
701
702        // Reload keyboard layouts.
703        reloadKeyboardLayouts();
704    }
705
706    private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices,
707            String descriptor) {
708        final int numDevices = inputDevices.length;
709        for (int i = 0; i < numDevices; i++) {
710            final InputDevice inputDevice = inputDevices[i];
711            if (inputDevice.getDescriptor().equals(descriptor)) {
712                return true;
713            }
714        }
715        return false;
716    }
717
718    @Override // Binder call
719    public KeyboardLayout[] getKeyboardLayouts() {
720        final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
721        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
722            @Override
723            public void visitKeyboardLayout(Resources resources,
724                    String descriptor, String label, String collection, int keyboardLayoutResId) {
725                list.add(new KeyboardLayout(descriptor, label, collection));
726            }
727        });
728        return list.toArray(new KeyboardLayout[list.size()]);
729    }
730
731    @Override // Binder call
732    public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
733        if (keyboardLayoutDescriptor == null) {
734            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
735        }
736
737        final KeyboardLayout[] result = new KeyboardLayout[1];
738        visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
739            @Override
740            public void visitKeyboardLayout(Resources resources,
741                    String descriptor, String label, String collection, int keyboardLayoutResId) {
742                result[0] = new KeyboardLayout(descriptor, label, collection);
743            }
744        });
745        if (result[0] == null) {
746            Log.w(TAG, "Could not get keyboard layout with descriptor '"
747                    + keyboardLayoutDescriptor + "'.");
748        }
749        return result[0];
750    }
751
752    private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
753        final PackageManager pm = mContext.getPackageManager();
754        Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
755        for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
756                PackageManager.GET_META_DATA)) {
757            visitKeyboardLayoutsInPackage(pm, resolveInfo.activityInfo, null, visitor);
758        }
759    }
760
761    private void visitKeyboardLayout(String keyboardLayoutDescriptor,
762            KeyboardLayoutVisitor visitor) {
763        KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
764        if (d != null) {
765            final PackageManager pm = mContext.getPackageManager();
766            try {
767                ActivityInfo receiver = pm.getReceiverInfo(
768                        new ComponentName(d.packageName, d.receiverName),
769                        PackageManager.GET_META_DATA);
770                visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, visitor);
771            } catch (NameNotFoundException ex) {
772            }
773        }
774    }
775
776    private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
777            String keyboardName, KeyboardLayoutVisitor visitor) {
778        Bundle metaData = receiver.metaData;
779        if (metaData == null) {
780            return;
781        }
782
783        int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
784        if (configResId == 0) {
785            Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
786                    + "' on receiver " + receiver.packageName + "/" + receiver.name);
787            return;
788        }
789
790        CharSequence receiverLabel = receiver.loadLabel(pm);
791        String collection = receiverLabel != null ? receiverLabel.toString() : "";
792
793        try {
794            Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
795            XmlResourceParser parser = resources.getXml(configResId);
796            try {
797                XmlUtils.beginDocument(parser, "keyboard-layouts");
798
799                for (;;) {
800                    XmlUtils.nextElement(parser);
801                    String element = parser.getName();
802                    if (element == null) {
803                        break;
804                    }
805                    if (element.equals("keyboard-layout")) {
806                        TypedArray a = resources.obtainAttributes(
807                                parser, com.android.internal.R.styleable.KeyboardLayout);
808                        try {
809                            String name = a.getString(
810                                    com.android.internal.R.styleable.KeyboardLayout_name);
811                            String label = a.getString(
812                                    com.android.internal.R.styleable.KeyboardLayout_label);
813                            int keyboardLayoutResId = a.getResourceId(
814                                    com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
815                                    0);
816                            if (name == null || label == null || keyboardLayoutResId == 0) {
817                                Log.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
818                                        + "attributes in keyboard layout "
819                                        + "resource from receiver "
820                                        + receiver.packageName + "/" + receiver.name);
821                            } else {
822                                String descriptor = KeyboardLayoutDescriptor.format(
823                                        receiver.packageName, receiver.name, name);
824                                if (keyboardName == null || name.equals(keyboardName)) {
825                                    visitor.visitKeyboardLayout(resources, descriptor,
826                                            label, collection, keyboardLayoutResId);
827                                }
828                            }
829                        } finally {
830                            a.recycle();
831                        }
832                    } else {
833                        Log.w(TAG, "Skipping unrecognized element '" + element
834                                + "' in keyboard layout resource from receiver "
835                                + receiver.packageName + "/" + receiver.name);
836                    }
837                }
838            } finally {
839                parser.close();
840            }
841        } catch (Exception ex) {
842            Log.w(TAG, "Could not parse keyboard layout resource from receiver "
843                    + receiver.packageName + "/" + receiver.name, ex);
844        }
845    }
846
847    @Override // Binder call
848    public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
849        if (inputDeviceDescriptor == null) {
850            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
851        }
852
853        synchronized (mDataStore) {
854            return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
855        }
856    }
857
858    @Override // Binder call
859    public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
860            String keyboardLayoutDescriptor) {
861        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
862                "setCurrentKeyboardLayoutForInputDevice()")) {
863            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
864        }
865        if (inputDeviceDescriptor == null) {
866            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
867        }
868        if (keyboardLayoutDescriptor == null) {
869            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
870        }
871
872        synchronized (mDataStore) {
873            try {
874                if (mDataStore.setCurrentKeyboardLayout(
875                        inputDeviceDescriptor, keyboardLayoutDescriptor)) {
876                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
877                }
878            } finally {
879                mDataStore.saveIfNeeded();
880            }
881        }
882    }
883
884    @Override // Binder call
885    public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
886        if (inputDeviceDescriptor == null) {
887            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
888        }
889
890        synchronized (mDataStore) {
891            return mDataStore.getKeyboardLayouts(inputDeviceDescriptor);
892        }
893    }
894
895    @Override // Binder call
896    public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
897            String keyboardLayoutDescriptor) {
898        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
899                "addKeyboardLayoutForInputDevice()")) {
900            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
901        }
902        if (inputDeviceDescriptor == null) {
903            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
904        }
905        if (keyboardLayoutDescriptor == null) {
906            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
907        }
908
909        synchronized (mDataStore) {
910            try {
911                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
912                if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor)
913                        && !Objects.equal(oldLayout,
914                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
915                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
916                }
917            } finally {
918                mDataStore.saveIfNeeded();
919            }
920        }
921    }
922
923    @Override // Binder call
924    public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
925            String keyboardLayoutDescriptor) {
926        if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
927                "removeKeyboardLayoutForInputDevice()")) {
928            throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
929        }
930        if (inputDeviceDescriptor == null) {
931            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
932        }
933        if (keyboardLayoutDescriptor == null) {
934            throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
935        }
936
937        synchronized (mDataStore) {
938            try {
939                String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
940                if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor,
941                        keyboardLayoutDescriptor)
942                        && !Objects.equal(oldLayout,
943                                mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
944                    mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
945                }
946            } finally {
947                mDataStore.saveIfNeeded();
948            }
949        }
950    }
951
952    public void switchKeyboardLayout(int deviceId, int direction) {
953        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
954    }
955
956    // Must be called on handler.
957    private void handleSwitchKeyboardLayout(int deviceId, int direction) {
958        final InputDevice device = getInputDevice(deviceId);
959        final String inputDeviceDescriptor = device.getDescriptor();
960        if (device != null) {
961            final boolean changed;
962            final String keyboardLayoutDescriptor;
963            synchronized (mDataStore) {
964                try {
965                    changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction);
966                    keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
967                            inputDeviceDescriptor);
968                } finally {
969                    mDataStore.saveIfNeeded();
970                }
971            }
972
973            if (changed) {
974                if (mSwitchedKeyboardLayoutToast != null) {
975                    mSwitchedKeyboardLayoutToast.cancel();
976                    mSwitchedKeyboardLayoutToast = null;
977                }
978                if (keyboardLayoutDescriptor != null) {
979                    KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
980                    if (keyboardLayout != null) {
981                        mSwitchedKeyboardLayoutToast = Toast.makeText(
982                                mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
983                        mSwitchedKeyboardLayoutToast.show();
984                    }
985                }
986
987                reloadKeyboardLayouts();
988            }
989        }
990    }
991
992    public void setInputWindows(InputWindowHandle[] windowHandles) {
993        nativeSetInputWindows(mPtr, windowHandles);
994    }
995
996    public void setFocusedApplication(InputApplicationHandle application) {
997        nativeSetFocusedApplication(mPtr, application);
998    }
999
1000    public void setInputDispatchMode(boolean enabled, boolean frozen) {
1001        nativeSetInputDispatchMode(mPtr, enabled, frozen);
1002    }
1003
1004    public void setSystemUiVisibility(int visibility) {
1005        nativeSetSystemUiVisibility(mPtr, visibility);
1006    }
1007
1008    /**
1009     * Atomically transfers touch focus from one window to another as identified by
1010     * their input channels.  It is possible for multiple windows to have
1011     * touch focus if they support split touch dispatch
1012     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
1013     * method only transfers touch focus of the specified window without affecting
1014     * other windows that may also have touch focus at the same time.
1015     * @param fromChannel The channel of a window that currently has touch focus.
1016     * @param toChannel The channel of the window that should receive touch focus in
1017     * place of the first.
1018     * @return True if the transfer was successful.  False if the window with the
1019     * specified channel did not actually have touch focus at the time of the request.
1020     */
1021    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
1022        if (fromChannel == null) {
1023            throw new IllegalArgumentException("fromChannel must not be null.");
1024        }
1025        if (toChannel == null) {
1026            throw new IllegalArgumentException("toChannel must not be null.");
1027        }
1028        return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
1029    }
1030
1031    @Override // Binder call
1032    public void tryPointerSpeed(int speed) {
1033        if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
1034                "tryPointerSpeed()")) {
1035            throw new SecurityException("Requires SET_POINTER_SPEED permission");
1036        }
1037
1038        if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
1039            throw new IllegalArgumentException("speed out of range");
1040        }
1041
1042        setPointerSpeedUnchecked(speed);
1043    }
1044
1045    public void updatePointerSpeedFromSettings() {
1046        int speed = getPointerSpeedSetting();
1047        setPointerSpeedUnchecked(speed);
1048    }
1049
1050    private void setPointerSpeedUnchecked(int speed) {
1051        speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
1052                InputManager.MAX_POINTER_SPEED);
1053        nativeSetPointerSpeed(mPtr, speed);
1054    }
1055
1056    private void registerPointerSpeedSettingObserver() {
1057        mContext.getContentResolver().registerContentObserver(
1058                Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
1059                new ContentObserver(mHandler) {
1060                    @Override
1061                    public void onChange(boolean selfChange) {
1062                        updatePointerSpeedFromSettings();
1063                    }
1064                });
1065    }
1066
1067    private int getPointerSpeedSetting() {
1068        int speed = InputManager.DEFAULT_POINTER_SPEED;
1069        try {
1070            speed = Settings.System.getInt(mContext.getContentResolver(),
1071                    Settings.System.POINTER_SPEED);
1072        } catch (SettingNotFoundException snfe) {
1073        }
1074        return speed;
1075    }
1076
1077    public void updateShowTouchesFromSettings() {
1078        int setting = getShowTouchesSetting(0);
1079        nativeSetShowTouches(mPtr, setting != 0);
1080    }
1081
1082    private void registerShowTouchesSettingObserver() {
1083        mContext.getContentResolver().registerContentObserver(
1084                Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
1085                new ContentObserver(mHandler) {
1086                    @Override
1087                    public void onChange(boolean selfChange) {
1088                        updateShowTouchesFromSettings();
1089                    }
1090                });
1091    }
1092
1093    private int getShowTouchesSetting(int defaultValue) {
1094        int result = defaultValue;
1095        try {
1096            result = Settings.System.getInt(mContext.getContentResolver(),
1097                    Settings.System.SHOW_TOUCHES);
1098        } catch (SettingNotFoundException snfe) {
1099        }
1100        return result;
1101    }
1102
1103    // Binder call
1104    @Override
1105    public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
1106        if (repeat >= pattern.length) {
1107            throw new ArrayIndexOutOfBoundsException();
1108        }
1109
1110        VibratorToken v;
1111        synchronized (mVibratorLock) {
1112            v = mVibratorTokens.get(token);
1113            if (v == null) {
1114                v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
1115                try {
1116                    token.linkToDeath(v, 0);
1117                } catch (RemoteException ex) {
1118                    // give up
1119                    throw new RuntimeException(ex);
1120                }
1121                mVibratorTokens.put(token, v);
1122            }
1123        }
1124
1125        synchronized (v) {
1126            v.mVibrating = true;
1127            nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
1128        }
1129    }
1130
1131    // Binder call
1132    @Override
1133    public void cancelVibrate(int deviceId, IBinder token) {
1134        VibratorToken v;
1135        synchronized (mVibratorLock) {
1136            v = mVibratorTokens.get(token);
1137            if (v == null || v.mDeviceId != deviceId) {
1138                return; // nothing to cancel
1139            }
1140        }
1141
1142        cancelVibrateIfNeeded(v);
1143    }
1144
1145    void onVibratorTokenDied(VibratorToken v) {
1146        synchronized (mVibratorLock) {
1147            mVibratorTokens.remove(v.mToken);
1148        }
1149
1150        cancelVibrateIfNeeded(v);
1151    }
1152
1153    private void cancelVibrateIfNeeded(VibratorToken v) {
1154        synchronized (v) {
1155            if (v.mVibrating) {
1156                nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
1157                v.mVibrating = false;
1158            }
1159        }
1160    }
1161
1162    @Override
1163    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1164        if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
1165                != PackageManager.PERMISSION_GRANTED) {
1166            pw.println("Permission Denial: can't dump InputManager from from pid="
1167                    + Binder.getCallingPid()
1168                    + ", uid=" + Binder.getCallingUid());
1169            return;
1170        }
1171
1172        pw.println("INPUT MANAGER (dumpsys input)\n");
1173        String dumpStr = nativeDump(mPtr);
1174        if (dumpStr != null) {
1175            pw.println(dumpStr);
1176        }
1177    }
1178
1179    private boolean checkCallingPermission(String permission, String func) {
1180        // Quick check: if the calling permission is me, it's all okay.
1181        if (Binder.getCallingPid() == Process.myPid()) {
1182            return true;
1183        }
1184
1185        if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
1186            return true;
1187        }
1188        String msg = "Permission Denial: " + func + " from pid="
1189                + Binder.getCallingPid()
1190                + ", uid=" + Binder.getCallingUid()
1191                + " requires " + permission;
1192        Slog.w(TAG, msg);
1193        return false;
1194    }
1195
1196    // Called by the heartbeat to ensure locks are not held indefinitely (for deadlock detection).
1197    public void monitor() {
1198        synchronized (mInputFilterLock) { }
1199        nativeMonitor(mPtr);
1200    }
1201
1202    // Native callback.
1203    private void notifyConfigurationChanged(long whenNanos) {
1204        mCallbacks.notifyConfigurationChanged();
1205    }
1206
1207    // Native callback.
1208    private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
1209        synchronized (mInputDevicesLock) {
1210            if (!mInputDevicesChangedPending) {
1211                mInputDevicesChangedPending = true;
1212                mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
1213                        mInputDevices).sendToTarget();
1214            }
1215
1216            mInputDevices = inputDevices;
1217        }
1218    }
1219
1220    // Native callback.
1221    private void notifySwitch(long whenNanos, int switchCode, int switchValue) {
1222        switch (switchCode) {
1223            case SW_LID:
1224                mCallbacks.notifyLidSwitchChanged(whenNanos, switchValue == 0);
1225                break;
1226        }
1227    }
1228
1229    // Native callback.
1230    private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
1231        mCallbacks.notifyInputChannelBroken(inputWindowHandle);
1232    }
1233
1234    // Native callback.
1235    private long notifyANR(InputApplicationHandle inputApplicationHandle,
1236            InputWindowHandle inputWindowHandle) {
1237        return mCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
1238    }
1239
1240    // Native callback.
1241    final boolean filterInputEvent(InputEvent event, int policyFlags) {
1242        synchronized (mInputFilterLock) {
1243            if (mInputFilter != null) {
1244                try {
1245                    mInputFilter.filterInputEvent(event, policyFlags);
1246                } catch (RemoteException e) {
1247                    /* ignore */
1248                }
1249                return false;
1250            }
1251        }
1252        event.recycle();
1253        return true;
1254    }
1255
1256    // Native callback.
1257    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
1258        return mCallbacks.interceptKeyBeforeQueueing(
1259                event, policyFlags, isScreenOn);
1260    }
1261
1262    // Native callback.
1263    private int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
1264        return mCallbacks.interceptMotionBeforeQueueingWhenScreenOff(policyFlags);
1265    }
1266
1267    // Native callback.
1268    private long interceptKeyBeforeDispatching(InputWindowHandle focus,
1269            KeyEvent event, int policyFlags) {
1270        return mCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
1271    }
1272
1273    // Native callback.
1274    private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
1275            KeyEvent event, int policyFlags) {
1276        return mCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
1277    }
1278
1279    // Native callback.
1280    private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
1281        return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
1282                injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
1283    }
1284
1285    // Native callback.
1286    private int getVirtualKeyQuietTimeMillis() {
1287        return mContext.getResources().getInteger(
1288                com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
1289    }
1290
1291    // Native callback.
1292    private String[] getExcludedDeviceNames() {
1293        ArrayList<String> names = new ArrayList<String>();
1294
1295        // Read partner-provided list of excluded input devices
1296        XmlPullParser parser = null;
1297        // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
1298        File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
1299        FileReader confreader = null;
1300        try {
1301            confreader = new FileReader(confFile);
1302            parser = Xml.newPullParser();
1303            parser.setInput(confreader);
1304            XmlUtils.beginDocument(parser, "devices");
1305
1306            while (true) {
1307                XmlUtils.nextElement(parser);
1308                if (!"device".equals(parser.getName())) {
1309                    break;
1310                }
1311                String name = parser.getAttributeValue(null, "name");
1312                if (name != null) {
1313                    names.add(name);
1314                }
1315            }
1316        } catch (FileNotFoundException e) {
1317            // It's ok if the file does not exist.
1318        } catch (Exception e) {
1319            Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
1320        } finally {
1321            try { if (confreader != null) confreader.close(); } catch (IOException e) { }
1322        }
1323
1324        return names.toArray(new String[names.size()]);
1325    }
1326
1327    // Native callback.
1328    private int getKeyRepeatTimeout() {
1329        return ViewConfiguration.getKeyRepeatTimeout();
1330    }
1331
1332    // Native callback.
1333    private int getKeyRepeatDelay() {
1334        return ViewConfiguration.getKeyRepeatDelay();
1335    }
1336
1337    // Native callback.
1338    private int getHoverTapTimeout() {
1339        return ViewConfiguration.getHoverTapTimeout();
1340    }
1341
1342    // Native callback.
1343    private int getHoverTapSlop() {
1344        return ViewConfiguration.getHoverTapSlop();
1345    }
1346
1347    // Native callback.
1348    private int getDoubleTapTimeout() {
1349        return ViewConfiguration.getDoubleTapTimeout();
1350    }
1351
1352    // Native callback.
1353    private int getLongPressTimeout() {
1354        return ViewConfiguration.getLongPressTimeout();
1355    }
1356
1357    // Native callback.
1358    private int getPointerLayer() {
1359        return mCallbacks.getPointerLayer();
1360    }
1361
1362    // Native callback.
1363    private PointerIcon getPointerIcon() {
1364        return PointerIcon.getDefaultIcon(mContext);
1365    }
1366
1367    // Native callback.
1368    private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
1369        if (!mSystemReady) {
1370            return null;
1371        }
1372
1373        String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
1374                inputDeviceDescriptor);
1375        if (keyboardLayoutDescriptor == null) {
1376            return null;
1377        }
1378
1379        final String[] result = new String[2];
1380        visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
1381            @Override
1382            public void visitKeyboardLayout(Resources resources,
1383                    String descriptor, String label, String collection, int keyboardLayoutResId) {
1384                try {
1385                    result[0] = descriptor;
1386                    result[1] = Streams.readFully(new InputStreamReader(
1387                            resources.openRawResource(keyboardLayoutResId)));
1388                } catch (IOException ex) {
1389                } catch (NotFoundException ex) {
1390                }
1391            }
1392        });
1393        if (result[0] == null) {
1394            Log.w(TAG, "Could not get keyboard layout with descriptor '"
1395                    + keyboardLayoutDescriptor + "'.");
1396            return null;
1397        }
1398        return result;
1399    }
1400
1401    // Native callback.
1402    private String getDeviceAlias(String uniqueId) {
1403        if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
1404            // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId)
1405            return null;
1406        }
1407        return null;
1408    }
1409
1410
1411    /**
1412     * Callback interface implemented by the Window Manager.
1413     */
1414    public interface Callbacks {
1415        public void notifyConfigurationChanged();
1416
1417        public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
1418
1419        public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
1420
1421        public long notifyANR(InputApplicationHandle inputApplicationHandle,
1422                InputWindowHandle inputWindowHandle);
1423
1424        public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn);
1425
1426        public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags);
1427
1428        public long interceptKeyBeforeDispatching(InputWindowHandle focus,
1429                KeyEvent event, int policyFlags);
1430
1431        public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
1432                KeyEvent event, int policyFlags);
1433
1434        public int getPointerLayer();
1435    }
1436
1437    /**
1438     * Private handler for the input manager.
1439     */
1440    private final class InputManagerHandler extends Handler {
1441        @Override
1442        public void handleMessage(Message msg) {
1443            switch (msg.what) {
1444                case MSG_DELIVER_INPUT_DEVICES_CHANGED:
1445                    deliverInputDevicesChanged((InputDevice[])msg.obj);
1446                    break;
1447                case MSG_SWITCH_KEYBOARD_LAYOUT:
1448                    handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
1449                    break;
1450                case MSG_RELOAD_KEYBOARD_LAYOUTS:
1451                    reloadKeyboardLayouts();
1452                    break;
1453                case MSG_UPDATE_KEYBOARD_LAYOUTS:
1454                    updateKeyboardLayouts();
1455                    break;
1456                case MSG_RELOAD_DEVICE_ALIASES:
1457                    reloadDeviceAliases();
1458                    break;
1459            }
1460        }
1461    }
1462
1463    /**
1464     * Hosting interface for input filters to call back into the input manager.
1465     */
1466    private final class InputFilterHost extends IInputFilterHost.Stub {
1467        private boolean mDisconnected;
1468
1469        public void disconnectLocked() {
1470            mDisconnected = true;
1471        }
1472
1473        public void sendInputEvent(InputEvent event, int policyFlags) {
1474            if (event == null) {
1475                throw new IllegalArgumentException("event must not be null");
1476            }
1477
1478            synchronized (mInputFilterLock) {
1479                if (!mDisconnected) {
1480                    nativeInjectInputEvent(mPtr, event, 0, 0,
1481                            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
1482                            policyFlags | WindowManagerPolicy.FLAG_FILTERED);
1483                }
1484            }
1485        }
1486    }
1487
1488    private static final class KeyboardLayoutDescriptor {
1489        public String packageName;
1490        public String receiverName;
1491        public String keyboardLayoutName;
1492
1493        public static String format(String packageName,
1494                String receiverName, String keyboardName) {
1495            return packageName + "/" + receiverName + "/" + keyboardName;
1496        }
1497
1498        public static KeyboardLayoutDescriptor parse(String descriptor) {
1499            int pos = descriptor.indexOf('/');
1500            if (pos < 0 || pos + 1 == descriptor.length()) {
1501                return null;
1502            }
1503            int pos2 = descriptor.indexOf('/', pos + 1);
1504            if (pos2 < pos + 2 || pos2 + 1 == descriptor.length()) {
1505                return null;
1506            }
1507
1508            KeyboardLayoutDescriptor result = new KeyboardLayoutDescriptor();
1509            result.packageName = descriptor.substring(0, pos);
1510            result.receiverName = descriptor.substring(pos + 1, pos2);
1511            result.keyboardLayoutName = descriptor.substring(pos2 + 1);
1512            return result;
1513        }
1514    }
1515
1516    private interface KeyboardLayoutVisitor {
1517        void visitKeyboardLayout(Resources resources,
1518                String descriptor, String label, String collection, int keyboardLayoutResId);
1519    }
1520
1521    private final class InputDevicesChangedListenerRecord implements DeathRecipient {
1522        private final int mPid;
1523        private final IInputDevicesChangedListener mListener;
1524
1525        public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) {
1526            mPid = pid;
1527            mListener = listener;
1528        }
1529
1530        @Override
1531        public void binderDied() {
1532            if (DEBUG) {
1533                Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died.");
1534            }
1535            onInputDevicesChangedListenerDied(mPid);
1536        }
1537
1538        public void notifyInputDevicesChanged(int[] info) {
1539            try {
1540                mListener.onInputDevicesChanged(info);
1541            } catch (RemoteException ex) {
1542                Slog.w(TAG, "Failed to notify process "
1543                        + mPid + " that input devices changed, assuming it died.", ex);
1544                binderDied();
1545            }
1546        }
1547    }
1548
1549    private final class VibratorToken implements DeathRecipient {
1550        public final int mDeviceId;
1551        public final IBinder mToken;
1552        public final int mTokenValue;
1553
1554        public boolean mVibrating;
1555
1556        public VibratorToken(int deviceId, IBinder token, int tokenValue) {
1557            mDeviceId = deviceId;
1558            mToken = token;
1559            mTokenValue = tokenValue;
1560        }
1561
1562        @Override
1563        public void binderDied() {
1564            if (DEBUG) {
1565                Slog.d(TAG, "Vibrator token died.");
1566            }
1567            onVibratorTokenDied(this);
1568        }
1569    }
1570}
1571