summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt10
-rw-r--r--core/java/android/hardware/input/IInputDevicesChangedListener.aidl29
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl4
-rwxr-xr-xcore/java/android/hardware/input/InputManager.java306
-rwxr-xr-xcore/java/android/view/InputDevice.java20
-rw-r--r--core/java/android/view/KeyCharacterMap.java2
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java68
-rw-r--r--core/jni/android_view_InputDevice.cpp7
-rw-r--r--include/androidfw/InputDevice.h5
-rw-r--r--libs/androidfw/InputDevice.cpp9
-rw-r--r--services/input/EventHub.cpp5
-rw-r--r--services/input/InputReader.cpp106
-rw-r--r--services/input/InputReader.h32
-rw-r--r--services/input/tests/InputReader_test.cpp97
-rw-r--r--services/java/com/android/server/input/InputManagerService.java183
-rw-r--r--services/jni/com_android_server_input_InputManagerService.cpp78
17 files changed, 742 insertions, 220 deletions
diff --git a/Android.mk b/Android.mk
index cacdee9..4e3929c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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");