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