Merge "Notify applications when input devices change."

This commit is contained in:
Jeff Brown
2012-04-12 18:55:57 -07:00
committed by Android (Google) Code Review
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

@@ -9822,10 +9822,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");