diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | api/current.txt | 10 | ||||
-rw-r--r-- | core/java/android/hardware/input/IInputDevicesChangedListener.aidl | 29 | ||||
-rw-r--r-- | core/java/android/hardware/input/IInputManager.aidl | 4 | ||||
-rwxr-xr-x | core/java/android/hardware/input/InputManager.java | 306 | ||||
-rwxr-xr-x | core/java/android/view/InputDevice.java | 20 | ||||
-rw-r--r-- | core/java/android/view/KeyCharacterMap.java | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/PointerLocationView.java | 68 | ||||
-rw-r--r-- | core/jni/android_view_InputDevice.cpp | 7 | ||||
-rw-r--r-- | include/androidfw/InputDevice.h | 5 | ||||
-rw-r--r-- | libs/androidfw/InputDevice.cpp | 9 | ||||
-rw-r--r-- | services/input/EventHub.cpp | 5 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 106 | ||||
-rw-r--r-- | services/input/InputReader.h | 32 | ||||
-rw-r--r-- | services/input/tests/InputReader_test.cpp | 97 | ||||
-rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 183 | ||||
-rw-r--r-- | services/jni/com_android_server_input_InputManagerService.cpp | 78 |
17 files changed, 742 insertions, 220 deletions
@@ -111,6 +111,7 @@ LOCAL_SRC_FILES += \ core/java/android/database/IContentObserver.aidl \ core/java/android/hardware/ISerialManager.aidl \ core/java/android/hardware/input/IInputManager.aidl \ + core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ core/java/android/hardware/usb/IUsbManager.aidl \ core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index ee964f7..3f79136 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9820,10 +9820,20 @@ package android.hardware { package android.hardware.input { public final class InputManager { + method public android.view.InputDevice getInputDevice(int); + method public int[] getInputDeviceIds(); + method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler); + method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener); field public static final java.lang.String ACTION_QUERY_KEYBOARD_LAYOUTS = "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; field public static final java.lang.String META_DATA_KEYBOARD_LAYOUTS = "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; } + public static abstract interface InputManager.InputDeviceListener { + method public abstract void onInputDeviceAdded(int); + method public abstract void onInputDeviceChanged(int); + method public abstract void onInputDeviceRemoved(int); + } + } package android.hardware.usb { diff --git a/core/java/android/hardware/input/IInputDevicesChangedListener.aidl b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl new file mode 100644 index 0000000..5d8ada1 --- /dev/null +++ b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +/** @hide */ +interface IInputDevicesChangedListener { + /* Called when input devices changed, such as a device being added, + * removed or changing configuration. + * + * The parameter is an array of pairs (deviceId, generation) indicating the current + * device id and generation of all input devices. The client can determine what + * has happened by comparing the result to its prior observations. + */ + oneway void onInputDevicesChanged(in int[] deviceIdAndGeneration); +} diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 47e0d1e..ca8321f 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -17,6 +17,7 @@ package android.hardware.input; import android.hardware.input.KeyboardLayout; +import android.hardware.input.IInputDevicesChangedListener; import android.view.InputDevice; import android.view.InputEvent; @@ -42,4 +43,7 @@ interface IInputManager { String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor); void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor); + + // Registers an input devices changed listener. + void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 3b3c237..35c49a1 100755 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -19,7 +19,10 @@ package android.hardware.input; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; @@ -29,6 +32,8 @@ import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; +import java.util.ArrayList; + /** * Provides information about input devices and available key layouts. * <p> @@ -40,11 +45,22 @@ import android.view.InputEvent; */ public final class InputManager { private static final String TAG = "InputManager"; + private static final boolean DEBUG = false; + + private static final int MSG_DEVICE_ADDED = 1; + private static final int MSG_DEVICE_REMOVED = 2; + private static final int MSG_DEVICE_CHANGED = 3; private static InputManager sInstance; private final IInputManager mIm; - private final SparseArray<InputDevice> mInputDevices = new SparseArray<InputDevice>(); + + // Guarded by mInputDevicesLock + private final Object mInputDevicesLock = new Object(); + private SparseArray<InputDevice> mInputDevices; + private InputDevicesChangedListener mInputDevicesChangedListener; + private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = + new ArrayList<InputDeviceListenerDelegate>(); /** * Broadcast Action: Query available keyboard layouts. @@ -169,6 +185,103 @@ public final class InputManager { } /** + * Gets information about the input device with the specified id. + * @param id The device id. + * @return The input device or null if not found. + */ + public InputDevice getInputDevice(int id) { + synchronized (mInputDevicesLock) { + populateInputDevicesLocked(); + + int index = mInputDevices.indexOfKey(id); + if (index < 0) { + return null; + } + + InputDevice inputDevice = mInputDevices.valueAt(index); + if (inputDevice == null) { + try { + inputDevice = mIm.getInputDevice(id); + } catch (RemoteException ex) { + throw new RuntimeException("Could not get input device information.", ex); + } + } + mInputDevices.setValueAt(index, inputDevice); + return inputDevice; + } + } + + /** + * Gets the ids of all input devices in the system. + * @return The input device ids. + */ + public int[] getInputDeviceIds() { + synchronized (mInputDevicesLock) { + populateInputDevicesLocked(); + + final int count = mInputDevices.size(); + final int[] ids = new int[count]; + for (int i = 0; i < count; i++) { + ids[i] = mInputDevices.keyAt(i); + } + return ids; + } + } + + /** + * Registers an input device listener to receive notifications about when + * input devices are added, removed or changed. + * + * @param listener The listener to register. + * @param handler The handler on which the listener should be invoked, or null + * if the listener should be invoked on the calling thread's looper. + * + * @see #unregisterInputDeviceListener + */ + public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mInputDevicesLock) { + int index = findInputDeviceListenerLocked(listener); + if (index < 0) { + mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); + } + } + } + + /** + * Unregisters an input device listener. + * + * @param listener The listener to unregister. + * + * @see #registerInputDeviceListener + */ + public void unregisterInputDeviceListener(InputDeviceListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mInputDevicesLock) { + int index = findInputDeviceListenerLocked(listener); + if (index >= 0) { + mInputDeviceListeners.remove(index); + } + } + } + + private int findInputDeviceListenerLocked(InputDeviceListener listener) { + final int numListeners = mInputDeviceListeners.size(); + for (int i = 0; i < numListeners; i++) { + if (mInputDeviceListeners.get(i).mListener == listener) { + return i; + } + } + return -1; + } + + /** * Gets information about all supported keyboard layouts. * <p> * The input manager consults the built-in keyboard layouts as well @@ -327,50 +440,6 @@ public final class InputManager { } /** - * Gets information about the input device with the specified id. - * @param id The device id. - * @return The input device or null if not found. - * - * @hide - */ - public InputDevice getInputDevice(int id) { - synchronized (mInputDevices) { - InputDevice inputDevice = mInputDevices.get(id); - if (inputDevice != null) { - return inputDevice; - } - } - final InputDevice newInputDevice; - try { - newInputDevice = mIm.getInputDevice(id); - } catch (RemoteException ex) { - throw new RuntimeException("Could not get input device information.", ex); - } - synchronized (mInputDevices) { - InputDevice inputDevice = mInputDevices.get(id); - if (inputDevice != null) { - return inputDevice; - } - mInputDevices.put(id, newInputDevice); - return newInputDevice; - } - } - - /** - * Gets the ids of all input devices in the system. - * @return The input device ids. - * - * @hide - */ - public int[] getInputDeviceIds() { - try { - return mIm.getInputDeviceIds(); - } catch (RemoteException ex) { - throw new RuntimeException("Could not get input device ids.", ex); - } - } - - /** * Queries the framework about whether any physical keys exist on the * any keyboard attached to the device that are capable of producing the given * array of key codes. @@ -429,4 +498,151 @@ public final class InputManager { return false; } } + + private void populateInputDevicesLocked() { + if (mInputDevicesChangedListener == null) { + final InputDevicesChangedListener listener = new InputDevicesChangedListener(); + try { + mIm.registerInputDevicesChangedListener(listener); + } catch (RemoteException ex) { + throw new RuntimeException( + "Could not get register input device changed listener", ex); + } + mInputDevicesChangedListener = listener; + } + + if (mInputDevices == null) { + final int[] ids; + try { + ids = mIm.getInputDeviceIds(); + } catch (RemoteException ex) { + throw new RuntimeException("Could not get input device ids.", ex); + } + + mInputDevices = new SparseArray<InputDevice>(); + for (int i = 0; i < ids.length; i++) { + mInputDevices.put(ids[i], null); + } + } + } + + private void onInputDevicesChanged(int[] deviceIdAndGeneration) { + if (DEBUG) { + Log.d(TAG, "Received input devices changed."); + } + + synchronized (mInputDevicesLock) { + for (int i = mInputDevices.size(); --i > 0; ) { + final int deviceId = mInputDevices.keyAt(i); + if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { + if (DEBUG) { + Log.d(TAG, "Device removed: " + deviceId); + } + mInputDevices.removeAt(i); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); + } + } + + for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { + final int deviceId = deviceIdAndGeneration[i]; + int index = mInputDevices.indexOfKey(deviceId); + if (index >= 0) { + final InputDevice device = mInputDevices.valueAt(index); + if (device != null) { + final int generation = deviceIdAndGeneration[i + 1]; + if (device.getGeneration() != generation) { + if (DEBUG) { + Log.d(TAG, "Device changed: " + deviceId); + } + mInputDevices.setValueAt(index, null); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); + } + } + } else { + if (DEBUG) { + Log.d(TAG, "Device added: " + deviceId); + } + mInputDevices.put(deviceId, null); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); + } + } + } + } + + private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { + final int numListeners = mInputDeviceListeners.size(); + for (int i = 0; i < numListeners; i++) { + InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); + listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); + } + } + + private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { + for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { + if (deviceIdAndGeneration[i] == deviceId) { + return true; + } + } + return false; + } + + /** + * Listens for changes in input devices. + */ + public interface InputDeviceListener { + /** + * Called whenever an input device has been added to the system. + * Use {@link InputManager#getInputDevice} to get more information about the device. + * + * @param deviceId The id of the input device that was added. + */ + void onInputDeviceAdded(int deviceId); + + /** + * Called whenever an input device has been removed from the system. + * + * @param deviceId The id of the input device that was removed. + */ + void onInputDeviceRemoved(int deviceId); + + /** + * Called whenever the properties of an input device have changed since they + * were last queried. Use {@link InputManager#getInputDevice} to get + * a fresh {@link InputDevice} object with the new properties. + * + * @param deviceId The id of the input device that changed. + */ + void onInputDeviceChanged(int deviceId); + } + + private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { + @Override + public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { + InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); + } + } + + private static final class InputDeviceListenerDelegate extends Handler { + public final InputDeviceListener mListener; + + public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { + super(handler != null ? handler.getLooper() : Looper.myLooper()); + mListener = listener; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DEVICE_ADDED: + mListener.onInputDeviceAdded(msg.arg1); + break; + case MSG_DEVICE_REMOVED: + mListener.onInputDeviceRemoved(msg.arg1); + break; + case MSG_DEVICE_CHANGED: + mListener.onInputDeviceChanged(msg.arg1); + break; + } + } + } } diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 75b2c746..4ebb679 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -40,6 +40,7 @@ import java.util.List; */ public final class InputDevice implements Parcelable { private final int mId; + private final int mGeneration; private final String mName; private final String mDescriptor; private final int mSources; @@ -302,9 +303,10 @@ public final class InputDevice implements Parcelable { }; // Called by native code. - private InputDevice(int id, String name, String descriptor, int sources, + private InputDevice(int id, int generation, String name, String descriptor, int sources, int keyboardType, KeyCharacterMap keyCharacterMap) { mId = id; + mGeneration = generation; mName = name; mDescriptor = descriptor; mSources = sources; @@ -314,6 +316,7 @@ public final class InputDevice implements Parcelable { private InputDevice(Parcel in) { mId = in.readInt(); + mGeneration = in.readInt(); mName = in.readString(); mDescriptor = in.readString(); mSources = in.readInt(); @@ -364,6 +367,19 @@ public final class InputDevice implements Parcelable { } /** + * Gets a generation number for this input device. + * The generation number is incremented whenever the device is reconfigured and its + * properties may have changed. + * + * @return The generation number. + * + * @hide + */ + public int getGeneration() { + return mGeneration; + } + + /** * Gets the input device descriptor, which is a stable identifier for an input device. * <p> * An input device descriptor uniquely identifies an input device. Its value @@ -595,6 +611,7 @@ public final class InputDevice implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); + out.writeInt(mGeneration); out.writeString(mName); out.writeString(mDescriptor); out.writeInt(mSources); @@ -624,6 +641,7 @@ public final class InputDevice implements Parcelable { StringBuilder description = new StringBuilder(); description.append("Input Device ").append(mId).append(": ").append(mName).append("\n"); description.append(" Descriptor: ").append(mDescriptor).append("\n"); + description.append(" Generation: ").append(mGeneration).append("\n"); description.append(" Keyboard Type: "); switch (mKeyboardType) { diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 3d165ea..12d7b12 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -22,8 +22,6 @@ import android.text.method.MetaKeyKeyListener; import android.util.AndroidRuntimeException; import android.util.SparseIntArray; import android.hardware.input.InputManager; -import android.util.SparseArray; -import android.view.InputDevice.MotionRange; import java.lang.Character; diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 0524c25..1d6af90 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -21,6 +21,8 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Paint.FontMetricsInt; +import android.hardware.input.InputManager; +import android.hardware.input.InputManager.InputDeviceListener; import android.util.Log; import android.view.InputDevice; import android.view.KeyEvent; @@ -32,7 +34,7 @@ import android.view.MotionEvent.PointerCoords; import java.util.ArrayList; -public class PointerLocationView extends View { +public class PointerLocationView extends View implements InputDeviceListener { private static final String TAG = "Pointer"; public static class PointerState { @@ -82,6 +84,8 @@ public class PointerLocationView extends View { private final int ESTIMATE_FUTURE_POINTS = 2; private final float ESTIMATE_INTERVAL = 0.02f; + private final InputManager mIm; + private final ViewConfiguration mVC; private final Paint mTextPaint; private final Paint mTextBackgroundPaint; @@ -108,6 +112,8 @@ public class PointerLocationView extends View { super(c); setFocusableInTouchMode(true); + mIm = (InputManager)c.getSystemService(Context.INPUT_SERVICE); + mVC = ViewConfiguration.get(c); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); @@ -139,18 +145,6 @@ public class PointerLocationView extends View { mActivePointerId = 0; mVelocity = VelocityTracker.obtain(); - - logInputDeviceCapabilities(); - } - - private void logInputDeviceCapabilities() { - int[] deviceIds = InputDevice.getDeviceIds(); - for (int i = 0; i < deviceIds.length; i++) { - InputDevice device = InputDevice.getDevice(deviceIds[i]); - if (device != null) { - Log.i(TAG, device.toString()); - } - } } public void setPrintCoords(boolean state) { @@ -631,7 +625,53 @@ public class PointerLocationView extends View { logMotionEvent("Trackball", event); return true; } - + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mIm.registerInputDeviceListener(this, getHandler()); + logInputDevices(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + mIm.unregisterInputDeviceListener(this); + } + + @Override + public void onInputDeviceAdded(int deviceId) { + logInputDeviceState(deviceId, "Device Added"); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + logInputDeviceState(deviceId, "Device Changed"); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + logInputDeviceState(deviceId, "Device Removed"); + } + + private void logInputDevices() { + int[] deviceIds = InputDevice.getDeviceIds(); + for (int i = 0; i < deviceIds.length; i++) { + logInputDeviceState(deviceIds[i], "Device Enumerated"); + } + } + + private void logInputDeviceState(int deviceId, String state) { + InputDevice device = mIm.getInputDevice(deviceId); + if (device != null) { + Log.i(TAG, state + ": " + device); + } else { + Log.i(TAG, state + ": " + deviceId); + } + } + // HACK // A quick and dirty string builder implementation optimized for GC. // Using String.format causes the application grind to a halt when diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index d7d476a..e8a3a3b 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -54,8 +54,9 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi } ScopedLocalRef<jobject> inputDeviceObj(env, env->NewObject(gInputDeviceClassInfo.clazz, - gInputDeviceClassInfo.ctor, deviceInfo.getId(), nameObj.get(), - descriptorObj.get(), deviceInfo.getSources(), deviceInfo.getKeyboardType(), + gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(), + nameObj.get(), descriptorObj.get(), + deviceInfo.getSources(), deviceInfo.getKeyboardType(), kcmObj.get())); const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); @@ -86,7 +87,7 @@ int register_android_view_InputDevice(JNIEnv* env) gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz)); GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, - "<init>", "(ILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;)V"); + "<init>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;)V"); GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFF)V"); diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h index c9554dc..2eac544 100644 --- a/include/androidfw/InputDevice.h +++ b/include/androidfw/InputDevice.h @@ -66,9 +66,11 @@ public: float fuzz; }; - void initialize(int32_t id, const String8& name, const String8& descriptor); + void initialize(int32_t id, int32_t generation, + const String8& name, const String8& descriptor); inline int32_t getId() const { return mId; } + inline int32_t getGeneration() const { return mGeneration; } inline const String8 getName() const { return mName; } inline const String8 getDescriptor() const { return mDescriptor; } inline uint32_t getSources() const { return mSources; } @@ -97,6 +99,7 @@ public: private: int32_t mId; + int32_t mGeneration; String8 mName; String8 mDescriptor; uint32_t mSources; diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp index 698feb6..6bb06a9 100644 --- a/libs/androidfw/InputDevice.cpp +++ b/libs/androidfw/InputDevice.cpp @@ -127,11 +127,12 @@ String8 getInputDeviceConfigurationFilePathByName( // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { - initialize(-1, String8("uninitialized device info"), String8("unknown")); + initialize(-1, -1, String8("uninitialized device info"), String8("unknown")); } InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : - mId(other.mId), mName(other.mName), mDescriptor(other.mDescriptor), + mId(other.mId), mGeneration(other.mGeneration), + mName(other.mName), mDescriptor(other.mDescriptor), mSources(other.mSources), mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap), @@ -141,8 +142,10 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : InputDeviceInfo::~InputDeviceInfo() { } -void InputDeviceInfo::initialize(int32_t id, const String8& name, const String8& descriptor) { +void InputDeviceInfo::initialize(int32_t id, int32_t generation, + const String8& name, const String8& descriptor) { mId = id; + mGeneration = generation; mName = name; mDescriptor = descriptor; mSources = 0; diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 2ba821e..fbffc94 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -720,6 +720,11 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz break; } } + } else if (eventItem.events & EPOLLHUP) { + ALOGI("Removing device %s due to epoll hang-up event.", + device->identifier.name.string()); + deviceChanged = true; + closeDeviceLocked(device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index ea614ad..71eba52 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -237,7 +237,8 @@ InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), - mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), + mGlobalMetaState(0), mGeneration(1), + mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); @@ -257,18 +258,24 @@ InputReader::~InputReader() { } void InputReader::loopOnce() { + int32_t oldGeneration; int32_t timeoutMillis; + bool inputDevicesChanged = false; + Vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex _l(mLock); + oldGeneration = mGeneration; + timeoutMillis = -1; + uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; + timeoutMillis = 0; refreshConfigurationLocked(changes); } - timeoutMillis = -1; - if (mNextTimeout != LLONG_MAX) { + if (timeoutMillis < 0 && mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } @@ -283,7 +290,8 @@ void InputReader::loopOnce() { if (count) { processEventsLocked(mEventBuffer, count); } - if (!count || timeoutMillis == 0) { + + if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { #if DEBUG_RAW_EVENTS @@ -293,8 +301,18 @@ void InputReader::loopOnce() { timeoutExpiredLocked(now); } } + + if (oldGeneration != mGeneration) { + inputDevicesChanged = true; + getInputDevicesLocked(inputDevices); + } } // release lock + // Send out a message that the describes the changed input devices. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -344,6 +362,12 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + return; + } + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); uint32_t classes = mEventHub->getDeviceClasses(deviceId); @@ -359,27 +383,22 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { identifier.name.string(), device->getSources()); } - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - mDevices.add(deviceId, device); - } else { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); - delete device; - return; - } + mDevices.add(deviceId, device); + bumpGenerationLocked(); } void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { InputDevice* device = NULL; ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); - } else { + if (deviceIndex < 0) { ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + bumpGenerationLocked(); + if (device->isIgnored()) { ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), device->getName().string()); @@ -394,7 +413,8 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { InputDevice* InputReader::createDeviceLocked(int32_t deviceId, const InputDeviceIdentifier& identifier, uint32_t classes) { - InputDevice* device = new InputDevice(&mContext, deviceId, identifier, classes); + InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), + identifier, classes); // External devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { @@ -577,39 +597,30 @@ void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { } } +int32_t InputReader::bumpGenerationLocked() { + return ++mGeneration; +} + void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { AutoMutex _l(mLock); *outConfiguration = mInputConfiguration; } -status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { +void InputReader::getInputDevices(Vector<InputDeviceInfo>& outInputDevices) { AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - return NAME_NOT_FOUND; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - return NAME_NOT_FOUND; - } - - device->getDeviceInfo(outDeviceInfo); - return OK; + getInputDevicesLocked(outInputDevices); } -void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { - AutoMutex _l(mLock); - - outDeviceIds.clear(); +void InputReader::getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices) { + outInputDevices.clear(); size_t numDevices = mDevices.size(); for (size_t i = 0; i < numDevices; i++) { InputDevice* device = mDevices.valueAt(i); if (!device->isIgnored()) { - outDeviceIds.add(device->getId()); + outInputDevices.push(); + device->getDeviceInfo(&outInputDevices.editTop()); } } } @@ -824,6 +835,11 @@ void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { mReader->requestTimeoutAtTimeLocked(when); } +int32_t InputReader::ContextImpl::bumpGeneration() { + // lock is already held by the input loop + return mReader->bumpGenerationLocked(); +} + InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } @@ -854,9 +870,10 @@ bool InputReaderThread::threadLoop() { // --- InputDevice --- -InputDevice::InputDevice(InputReaderContext* context, int32_t id, +InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, const InputDeviceIdentifier& identifier, uint32_t classes) : - mContext(context), mId(id), mIdentifier(identifier), mClasses(classes), + mContext(context), mId(id), mGeneration(generation), + mIdentifier(identifier), mClasses(classes), mSources(0), mIsExternal(false), mDropUntilNextSync(false) { } @@ -874,6 +891,7 @@ void InputDevice::dump(String8& dump) { dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(), deviceInfo.getName().string()); + dump.appendFormat(INDENT2 "Generation: %d\n", mGeneration); dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); @@ -983,7 +1001,7 @@ void InputDevice::timeoutExpired(nsecs_t when) { } void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mIdentifier.name, mIdentifier.descriptor); + outDeviceInfo->initialize(mId, mGeneration, mIdentifier.name, mIdentifier.descriptor); size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { @@ -1054,6 +1072,10 @@ void InputDevice::fadePointer() { } } +void InputDevice::bumpGeneration() { + mGeneration = mContext->bumpGeneration(); +} + void InputDevice::notifyReset(nsecs_t when) { NotifyDeviceResetArgs args(when, mId); mContext->getListener()->notifyDeviceReset(&args); @@ -1728,6 +1750,10 @@ status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axi return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); } +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name) { if (axis.valid) { @@ -2137,6 +2163,7 @@ void CursorInputMapper::configure(nsecs_t when, } else { mOrientation = DISPLAY_ORIENTATION_0; } + bumpGeneration(); } } @@ -2998,6 +3025,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Inform the dispatcher about the changes. *outResetNeeded = true; + bumpGeneration(); } } diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 8ab5905..d29776d 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -209,6 +209,11 @@ public: /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0; + + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + */ + virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0; }; @@ -240,16 +245,11 @@ public: */ virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; - /* Gets information about the specified input device. - * Returns OK if the device information was obtained or NAME_NOT_FOUND if there - * was no such device. + /* Gets information about all input devices. * * This method may be called on any thread (usually by the input manager). */ - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; - - /* Gets the list of all registered device ids. */ - virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0; + virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices) = 0; /* Query current input state. */ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, @@ -288,6 +288,7 @@ public: virtual void fadePointer() = 0; virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual int32_t bumpGeneration() = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; virtual InputListenerInterface* getListener() = 0; @@ -319,9 +320,7 @@ public: virtual void loopOnce(); virtual void getInputConfiguration(InputConfiguration* outConfiguration); - - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); - virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds); + virtual void getInputDevices(Vector<InputDeviceInfo>& outInputDevices); virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); @@ -353,6 +352,7 @@ protected: InputDevice* device, int32_t keyCode, int32_t scanCode); virtual void fadePointer(); virtual void requestTimeoutAtTime(nsecs_t when); + virtual int32_t bumpGeneration(); virtual InputReaderPolicyInterface* getPolicy(); virtual InputListenerInterface* getListener(); virtual EventHubInterface* getEventHub(); @@ -393,9 +393,14 @@ private: void fadePointerLocked(); + int32_t mGeneration; + int32_t bumpGenerationLocked(); + InputConfiguration mInputConfiguration; void updateInputConfigurationLocked(); + void getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices); + nsecs_t mDisableVirtualKeysTimeout; void disableVirtualKeysUntilLocked(nsecs_t time); bool shouldDropVirtualKeyLocked(nsecs_t now, @@ -432,12 +437,13 @@ private: /* Represents the state of a single input device. */ class InputDevice { public: - InputDevice(InputReaderContext* context, int32_t id, + InputDevice(InputReaderContext* context, int32_t id, int32_t generation, const InputDeviceIdentifier& identifier, uint32_t classes); ~InputDevice(); inline InputReaderContext* getContext() { return mContext; } inline int32_t getId() { return mId; } + inline int32_t getGeneration() { return mGeneration; } inline const String8& getName() { return mIdentifier.name; } inline uint32_t getClasses() { return mClasses; } inline uint32_t getSources() { return mSources; } @@ -465,6 +471,8 @@ public: void fadePointer(); + void bumpGeneration(); + void notifyReset(nsecs_t when); inline const PropertyMap& getConfiguration() { return mConfiguration; } @@ -487,6 +495,7 @@ public: private: InputReaderContext* mContext; int32_t mId; + int32_t mGeneration; InputDeviceIdentifier mIdentifier; uint32_t mClasses; @@ -849,6 +858,7 @@ protected: InputReaderContext* mContext; status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); static void dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name); diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index eac9a1c..e59af4e 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -127,6 +127,7 @@ private: class FakeInputReaderPolicy : public InputReaderPolicyInterface { InputReaderConfiguration mConfig; KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers; + Vector<InputDeviceInfo> mInputDevices; protected: virtual ~FakeInputReaderPolicy() { } @@ -141,10 +142,6 @@ public: mConfig.setDisplayInfo(displayId, true /*external*/, width, height, orientation); } - virtual nsecs_t getVirtualKeyQuietTime() { - return 0; - } - void addExcludedDeviceName(const String8& deviceName) { mConfig.excludedDeviceNames.push(deviceName); } @@ -157,6 +154,10 @@ public: return &mConfig; } + const Vector<InputDeviceInfo>& getInputDevices() const { + return mInputDevices; + } + private: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) { *outConfig = mConfig; @@ -165,6 +166,10 @@ private: virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) { return mPointerControllers.valueFor(deviceId); } + + virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) { + mInputDevices = inputDevices; + } }; @@ -667,6 +672,7 @@ class FakeInputReaderContext : public InputReaderContext { sp<InputListenerInterface> mListener; int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; + int32_t mGeneration; public: FakeInputReaderContext(const sp<EventHubInterface>& eventHub, @@ -722,6 +728,10 @@ private: virtual void requestTimeoutAtTime(nsecs_t when) { } + + virtual int32_t bumpGeneration() { + return ++mGeneration; + } }; @@ -887,7 +897,8 @@ public: InputDevice* newDevice(int32_t deviceId, const String8& name, uint32_t classes) { InputDeviceIdentifier identifier; identifier.name = name; - return new InputDevice(&mContext, deviceId, identifier, classes); + int32_t generation = deviceId + 1; + return new InputDevice(&mContext, deviceId, generation, identifier, classes); } protected: @@ -1045,52 +1056,30 @@ TEST_F(InputReaderTest, GetInputConfiguration_WhenDPadPresent_ReturnsDPadNavigat ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); } -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) { +TEST_F(InputReaderTest, GetInputDevices) { ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), INPUT_DEVICE_CLASS_KEYBOARD, NULL)); - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(OK, result); - ASSERT_EQ(1, info.getId()); - ASSERT_STREQ("keyboard", info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, info.getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, info.getSources()); - ASSERT_EQ(size_t(0), info.getMotionRanges().size()); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsInvalid) { - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(-1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) { - addDevice(1, String8("ignored"), 0, NULL); // no classes so device will be ignored - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceIds) { - sp<FakePointerController> controller = new FakePointerController(); - mFakePolicy->setPointerController(2, controller); - - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY, NULL)); - ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("mouse"), - INPUT_DEVICE_CLASS_CURSOR, NULL)); - - Vector<int32_t> ids; - mReader->getInputDeviceIds(ids); - - ASSERT_EQ(size_t(2), ids.size()); - ASSERT_EQ(1, ids[0]); - ASSERT_EQ(2, ids[1]); + ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"), + 0, NULL)); // no classes so device will be ignored + + Vector<InputDeviceInfo> inputDevices; + mReader->getInputDevices(inputDevices); + + ASSERT_EQ(1U, inputDevices.size()); + ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_STREQ("keyboard", inputDevices[0].getName().string()); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); + ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); + + // Should also have received a notification describing the new input devices. + inputDevices = mFakePolicy->getInputDevices(); + ASSERT_EQ(1U, inputDevices.size()); + ASSERT_EQ(1, inputDevices[0].getId()); + ASSERT_STREQ("keyboard", inputDevices[0].getName().string()); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); + ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); } TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { @@ -1243,6 +1232,7 @@ class InputDeviceTest : public testing::Test { protected: static const char* DEVICE_NAME; static const int32_t DEVICE_ID; + static const int32_t DEVICE_GENERATION; static const uint32_t DEVICE_CLASSES; sp<FakeEventHub> mFakeEventHub; @@ -1261,7 +1251,8 @@ protected: mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, identifier, DEVICE_CLASSES); + mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + identifier, DEVICE_CLASSES); } virtual void TearDown() { @@ -1276,6 +1267,7 @@ protected: const char* InputDeviceTest::DEVICE_NAME = "device"; const int32_t InputDeviceTest::DEVICE_ID = 1; +const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK; @@ -1428,6 +1420,7 @@ class InputMapperTest : public testing::Test { protected: static const char* DEVICE_NAME; static const int32_t DEVICE_ID; + static const int32_t DEVICE_GENERATION; static const uint32_t DEVICE_CLASSES; sp<FakeEventHub> mFakeEventHub; @@ -1443,7 +1436,8 @@ protected: mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, identifier, DEVICE_CLASSES); + mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + identifier, DEVICE_CLASSES); mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); } @@ -1522,6 +1516,7 @@ protected: const char* InputMapperTest::DEVICE_NAME = "device"; const int32_t InputMapperTest::DEVICE_ID = 1; +const int32_t InputMapperTest::DEVICE_GENERATION = 2; const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index 2f25df1..ce7671f 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -34,18 +34,23 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.database.ContentObserver; import android.hardware.input.IInputManager; +import android.hardware.input.IInputDevicesChangedListener; import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; +import android.os.IBinder; +import android.os.Message; import android.os.MessageQueue; import android.os.Process; +import android.os.RemoteException; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.util.Xml; import android.view.InputChannel; import android.view.InputDevice; @@ -77,12 +82,33 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; + private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1; + // Pointer to native input manager service object. private final int mPtr; private final Context mContext; private final Callbacks mCallbacks; - private final Handler mHandler; + private final InputManagerHandler mHandler; + + // Used to simulate a persistent data store for keyboard layouts. + // TODO: Replace with the real thing. + private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>(); + + // List of currently registered input devices changed listeners by process id. + private Object mInputDevicesLock = new Object(); + private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock + private InputDevice[] mInputDevices = new InputDevice[0]; + private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners = + new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock + private final ArrayList<InputDevicesChangedListenerRecord> + mTempInputDevicesChangedListenersToNotify = + new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only + + // State for the currently installed input filter. + final Object mInputFilterLock = new Object(); + InputFilter mInputFilter; // guarded by mInputFilterLock + InputFilterHost mInputFilterHost; // guarded by mInputFilterLock private static native int nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -111,9 +137,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private static native void nativeSetSystemUiVisibility(int ptr, int visibility); private static native void nativeSetFocusedApplication(int ptr, InputApplicationHandle application); - private static native InputDevice nativeGetInputDevice(int ptr, int deviceId); private static native void nativeGetInputConfiguration(int ptr, Configuration configuration); - private static native int[] nativeGetInputDeviceIds(int ptr); private static native boolean nativeTransferTouchFocus(int ptr, InputChannel fromChannel, InputChannel toChannel); private static native void nativeSetPointerSpeed(int ptr, int speed); @@ -145,19 +169,10 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. /** The key is down but is a virtual key press that is being emulated by the system. */ public static final int KEY_STATE_VIRTUAL = 2; - // Used to simulate a persistent data store for keyboard layouts. - // TODO: Replace with the real thing. - private final HashMap<String, String> mFakeRegistry = new HashMap<String, String>(); - - // State for the currently installed input filter. - final Object mInputFilterLock = new Object(); - InputFilter mInputFilter; - InputFilterHost mInputFilterHost; - public InputManagerService(Context context, Callbacks callbacks) { this.mContext = context; this.mCallbacks = callbacks; - this.mHandler = new Handler(); + this.mHandler = new InputManagerHandler(); Slog.i(TAG, "Initializing input manager"); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); @@ -396,16 +411,98 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. */ @Override // Binder call public InputDevice getInputDevice(int deviceId) { - return nativeGetInputDevice(mPtr, deviceId); + synchronized (mInputDevicesLock) { + final int count = mInputDevices.length; + for (int i = 0; i < count; i++) { + final InputDevice inputDevice = mInputDevices[i]; + if (inputDevice.getId() == deviceId) { + return inputDevice; + } + } + } + return null; } - + /** * Gets the ids of all input devices in the system. * @return The input device ids. */ @Override // Binder call public int[] getInputDeviceIds() { - return nativeGetInputDeviceIds(mPtr); + synchronized (mInputDevicesLock) { + final int count = mInputDevices.length; + int[] ids = new int[count]; + for (int i = 0; i < count; i++) { + ids[i] = mInputDevices[i].getId(); + } + return ids; + } + } + + @Override // Binder call + public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mInputDevicesLock) { + int callingPid = Binder.getCallingPid(); + if (mInputDevicesChangedListeners.get(callingPid) != null) { + throw new SecurityException("The calling process has already " + + "registered an InputDevicesChangedListener."); + } + + InputDevicesChangedListenerRecord record = + new InputDevicesChangedListenerRecord(callingPid, listener); + try { + IBinder binder = listener.asBinder(); + binder.linkToDeath(record, 0); + } catch (RemoteException ex) { + // give up + throw new RuntimeException(ex); + } + + mInputDevicesChangedListeners.put(callingPid, record); + } + } + + private void onInputDevicesChangedListenerDied(int pid) { + synchronized (mInputDevicesLock) { + mInputDevicesChangedListeners.remove(pid); + } + } + + // Must be called on handler. + private void deliverInputDevicesChanged() { + mTempInputDevicesChangedListenersToNotify.clear(); + + final int numListeners; + final int[] deviceIdAndGeneration; + synchronized (mInputDevicesLock) { + if (!mInputDevicesChangedPending) { + return; + } + mInputDevicesChangedPending = false; + + numListeners = mInputDevicesChangedListeners.size(); + for (int i = 0; i < numListeners; i++) { + mTempInputDevicesChangedListenersToNotify.add( + mInputDevicesChangedListeners.valueAt(i)); + } + + final int numDevices = mInputDevices.length; + deviceIdAndGeneration = new int[numDevices * 2]; + for (int i = 0; i < numDevices; i++) { + final InputDevice inputDevice = mInputDevices[i]; + deviceIdAndGeneration[i * 2] = inputDevice.getId(); + deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration(); + } + } + + for (int i = 0; i < numListeners; i++) { + mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged( + deviceIdAndGeneration); + } } @Override // Binder call @@ -741,6 +838,18 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. } // Native callback. + private void notifyInputDevicesChanged(InputDevice[] inputDevices) { + synchronized (mInputDevicesLock) { + mInputDevices = inputDevices; + + if (!mInputDevicesChangedPending) { + mInputDevicesChangedPending = true; + mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED); + } + } + } + + // Native callback. private void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { mCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen); } @@ -906,6 +1015,20 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. } /** + * Private handler for the input manager. + */ + private final class InputManagerHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DELIVER_INPUT_DEVICES_CHANGED: + deliverInputDevicesChanged(); + break; + } + } + } + + /** * Hosting interface for input filters to call back into the input manager. */ private final class InputFilterHost implements InputFilter.Host { @@ -957,4 +1080,32 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. return result; } } + + private final class InputDevicesChangedListenerRecord implements DeathRecipient { + private final int mPid; + private final IInputDevicesChangedListener mListener; + + public InputDevicesChangedListenerRecord(int pid, IInputDevicesChangedListener listener) { + mPid = pid; + mListener = listener; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Input devices changed listener for pid " + mPid + " died."); + } + onInputDevicesChangedListenerDied(mPid); + } + + public void notifyInputDevicesChanged(int[] info) { + try { + mListener.onInputDevicesChanged(info); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + + mPid + " that input devices changed, assuming it died.", ex); + binderDied(); + } + } + } } diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp index 85d6e11..f1536fd 100644 --- a/services/jni/com_android_server_input_InputManagerService.cpp +++ b/services/jni/com_android_server_input_InputManagerService.cpp @@ -59,6 +59,7 @@ static const float POINTER_SPEED_EXPONENT = 1.0f / 4; static struct { jmethodID notifyConfigurationChanged; + jmethodID notifyInputDevicesChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; jmethodID notifyANR; @@ -82,6 +83,10 @@ static struct { static struct { jclass clazz; +} gInputDeviceClassInfo; + +static struct { + jclass clazz; } gKeyEventClassInfo; static struct { @@ -179,6 +184,7 @@ public: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); + virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -486,6 +492,36 @@ void NativeInputManager::ensureSpriteControllerLocked() { } } +void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) { + JNIEnv* env = jniEnv(); + + size_t count = inputDevices.size(); + jobjectArray inputDevicesObjArray = env->NewObjectArray( + count, gInputDeviceClassInfo.clazz, NULL); + if (inputDevicesObjArray) { + bool error = false; + for (size_t i = 0; i < count; i++) { + jobject inputDeviceObj = android_view_InputDevice_create(env, inputDevices.itemAt(i)); + if (!inputDeviceObj) { + error = true; + break; + } + + env->SetObjectArrayElement(inputDevicesObjArray, i, inputDeviceObj); + env->DeleteLocalRef(inputDeviceObj); + } + + if (!error) { + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputDevicesChanged, + inputDevicesObjArray); + } + + env->DeleteLocalRef(inputDevicesObjArray); + } + + checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged"); +} + void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { #if DEBUG_INPUT_DISPATCHER_POLICY @@ -1147,36 +1183,6 @@ static void nativeSetSystemUiVisibility(JNIEnv* env, im->setSystemUiVisibility(visibility); } -static jobject nativeGetInputDevice(JNIEnv* env, - jclass clazz, jint ptr, jint deviceId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - - InputDeviceInfo deviceInfo; - status_t status = im->getInputManager()->getReader()->getInputDeviceInfo( - deviceId, & deviceInfo); - if (status) { - return NULL; - } - - return android_view_InputDevice_create(env, deviceInfo); -} - -static jintArray nativeGetInputDeviceIds(JNIEnv* env, - jclass clazz, jint ptr) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - - Vector<int> deviceIds; - im->getInputManager()->getReader()->getInputDeviceIds(deviceIds); - - jintArray deviceIdsObj = env->NewIntArray(deviceIds.size()); - if (! deviceIdsObj) { - return NULL; - } - - env->SetIntArrayRegion(deviceIdsObj, 0, deviceIds.size(), deviceIds.array()); - return deviceIdsObj; -} - static void nativeGetInputConfiguration(JNIEnv* env, jclass clazz, jint ptr, jobject configObj) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -1273,10 +1279,6 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetInputDispatchMode }, { "nativeSetSystemUiVisibility", "(II)V", (void*) nativeSetSystemUiVisibility }, - { "nativeGetInputDevice", "(II)Landroid/view/InputDevice;", - (void*) nativeGetInputDevice }, - { "nativeGetInputDeviceIds", "(I)[I", - (void*) nativeGetInputDeviceIds }, { "nativeGetInputConfiguration", "(ILandroid/content/res/Configuration;)V", (void*) nativeGetInputConfiguration }, { "nativeTransferTouchFocus", "(ILandroid/view/InputChannel;Landroid/view/InputChannel;)Z", @@ -1316,6 +1318,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz, "notifyConfigurationChanged", "(J)V"); + GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz, + "notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V"); + GET_METHOD_ID(gServiceClassInfo.notifyLidSwitchChanged, clazz, "notifyLidSwitchChanged", "(JZ)V"); @@ -1377,6 +1382,11 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz, "getPointerIcon", "()Landroid/view/PointerIcon;"); + // InputDevice + + FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz)); + // KeyEvent FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); |