Notify applications when input devices change.

This change allows the InputManager to keep track of what input
devices are registered with the system and when they change.
It needs to do this so that it can properly clear its cache of
input device properties (especially the key map!) when changes
occur.

Added new API so that applications can register listeners for
input device changes.

Fixed a minor bug in EventHub where it didn't handle EPOLLHUP
properly so it would spam the log about unsupposed epoll events
until inotify noticed that the device was gone and removed it.

Change-Id: I937d8c601f7185d4299038bce6a2934fe4fdd2b3
This commit is contained in:
Jeff Brown
2012-04-12 17:32:48 -07:00
parent cc11698319
commit af9e8d3818
17 changed files with 739 additions and 217 deletions

View File

@@ -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 \

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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.
@@ -168,6 +184,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>
@@ -326,50 +439,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
@@ -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;
}
}
}
}

View File

@@ -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();
@@ -363,6 +366,19 @@ public final class InputDevice implements Parcelable {
return mId;
}
/**
* 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>
@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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");

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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));
ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"),
0, NULL)); // no classes so device will be ignored
InputDeviceInfo info;
status_t result = mReader->getInputDeviceInfo(1, &info);
Vector<InputDeviceInfo> inputDevices;
mReader->getInputDevices(inputDevices);
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());
}
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, 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]);
// 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

View File

@@ -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
@@ -740,6 +837,18 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
mCallbacks.notifyConfigurationChanged();
}
// 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);
@@ -905,6 +1014,20 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
public int getPointerLayer();
}
/**
* 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.
*/
@@ -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();
}
}
}
}

View File

@@ -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;
@@ -80,6 +81,10 @@ static struct {
jmethodID getPointerIcon;
} gServiceClassInfo;
static struct {
jclass clazz;
} gInputDeviceClassInfo;
static struct {
jclass clazz;
} gKeyEventClassInfo;
@@ -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");