Merge "Add support for input devices that have vibrators."
This commit is contained in:
@@ -22362,6 +22362,7 @@ package android.view {
|
||||
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
|
||||
method public java.lang.String getName();
|
||||
method public int getSources();
|
||||
method public android.os.Vibrator getVibrator();
|
||||
method public boolean isVirtual();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.hardware.input;
|
||||
|
||||
import android.hardware.input.KeyboardLayout;
|
||||
import android.hardware.input.IInputDevicesChangedListener;
|
||||
import android.os.IBinder;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputEvent;
|
||||
|
||||
@@ -46,4 +47,8 @@ interface IInputManager {
|
||||
|
||||
// Registers an input devices changed listener.
|
||||
void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
|
||||
|
||||
// Input device vibrator control.
|
||||
void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
|
||||
void cancelVibrate(int deviceId, IBinder token);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ package android.hardware.input;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
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.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.SettingNotFoundException;
|
||||
import android.util.Log;
|
||||
@@ -586,6 +588,15 @@ public final class InputManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a vibrator service associated with an input device, assuming it has one.
|
||||
* @return The vibrator, never null.
|
||||
* @hide
|
||||
*/
|
||||
public Vibrator getInputDeviceVibrator(int deviceId) {
|
||||
return new InputDeviceVibrator(deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for changes in input devices.
|
||||
*/
|
||||
@@ -645,4 +656,45 @@ public final class InputManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class InputDeviceVibrator extends Vibrator {
|
||||
private final int mDeviceId;
|
||||
private final Binder mToken;
|
||||
|
||||
public InputDeviceVibrator(int deviceId) {
|
||||
mDeviceId = deviceId;
|
||||
mToken = new Binder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVibrator() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void vibrate(long milliseconds) {
|
||||
vibrate(new long[] { 0, milliseconds}, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void vibrate(long[] pattern, int repeat) {
|
||||
if (repeat >= pattern.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
try {
|
||||
mIm.vibrate(mDeviceId, pattern, repeat, mToken);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to vibrate.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
try {
|
||||
mIm.cancelVibrate(mDeviceId, mToken);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to cancel vibration.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
core/java/android/os/NullVibrator.java
Normal file
55
core/java/android/os/NullVibrator.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.os;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Vibrator implementation that does nothing.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NullVibrator extends Vibrator {
|
||||
private static final NullVibrator sInstance = new NullVibrator();
|
||||
|
||||
private NullVibrator() {
|
||||
}
|
||||
|
||||
public static NullVibrator getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVibrator() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void vibrate(long milliseconds) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void vibrate(long[] pattern, int repeat) {
|
||||
if (repeat >= pattern.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Vibrator;
|
||||
import android.os.NullVibrator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -46,8 +49,11 @@ public final class InputDevice implements Parcelable {
|
||||
private final int mSources;
|
||||
private final int mKeyboardType;
|
||||
private final KeyCharacterMap mKeyCharacterMap;
|
||||
private final boolean mHasVibrator;
|
||||
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
|
||||
|
||||
private Vibrator mVibrator; // guarded by mMotionRanges during initialization
|
||||
|
||||
/**
|
||||
* A mask for input source classes.
|
||||
*
|
||||
@@ -304,7 +310,7 @@ public final class InputDevice implements Parcelable {
|
||||
|
||||
// Called by native code.
|
||||
private InputDevice(int id, int generation, String name, String descriptor, int sources,
|
||||
int keyboardType, KeyCharacterMap keyCharacterMap) {
|
||||
int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator) {
|
||||
mId = id;
|
||||
mGeneration = generation;
|
||||
mName = name;
|
||||
@@ -312,6 +318,7 @@ public final class InputDevice implements Parcelable {
|
||||
mSources = sources;
|
||||
mKeyboardType = keyboardType;
|
||||
mKeyCharacterMap = keyCharacterMap;
|
||||
mHasVibrator = hasVibrator;
|
||||
}
|
||||
|
||||
private InputDevice(Parcel in) {
|
||||
@@ -322,6 +329,7 @@ public final class InputDevice implements Parcelable {
|
||||
mSources = in.readInt();
|
||||
mKeyboardType = in.readInt();
|
||||
mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
|
||||
mHasVibrator = in.readInt() != 0;
|
||||
|
||||
for (;;) {
|
||||
int axis = in.readInt();
|
||||
@@ -521,6 +529,31 @@ public final class InputDevice implements Parcelable {
|
||||
mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the vibrator service associated with the device, if there is one.
|
||||
* Even if the device does not have a vibrator, the result is never null.
|
||||
* Use {@link Vibrator#hasVibrator} to determine whether a vibrator is
|
||||
* present.
|
||||
*
|
||||
* Note that the vibrator associated with the device may be different from
|
||||
* the system vibrator. To obtain an instance of the system vibrator instead, call
|
||||
* {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
|
||||
*
|
||||
* @return The vibrator service associated with the device, never null.
|
||||
*/
|
||||
public Vibrator getVibrator() {
|
||||
synchronized (mMotionRanges) {
|
||||
if (mVibrator == null) {
|
||||
if (mHasVibrator) {
|
||||
mVibrator = InputManager.getInstance().getInputDeviceVibrator(mId);
|
||||
} else {
|
||||
mVibrator = NullVibrator.getInstance();
|
||||
}
|
||||
}
|
||||
return mVibrator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information about the range of values for a particular {@link MotionEvent} axis.
|
||||
*
|
||||
@@ -617,6 +650,7 @@ public final class InputDevice implements Parcelable {
|
||||
out.writeInt(mSources);
|
||||
out.writeInt(mKeyboardType);
|
||||
mKeyCharacterMap.writeToParcel(out, flags);
|
||||
out.writeInt(mHasVibrator ? 1 : 0);
|
||||
|
||||
final int numRanges = mMotionRanges.size();
|
||||
for (int i = 0; i < numRanges; i++) {
|
||||
@@ -657,6 +691,8 @@ public final class InputDevice implements Parcelable {
|
||||
}
|
||||
description.append("\n");
|
||||
|
||||
description.append(" Has Vibrator: ").append(mHasVibrator).append("\n");
|
||||
|
||||
description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" (");
|
||||
appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard");
|
||||
appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad");
|
||||
|
||||
@@ -57,7 +57,7 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
|
||||
gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
|
||||
nameObj.get(), descriptorObj.get(),
|
||||
deviceInfo.getSources(), deviceInfo.getKeyboardType(),
|
||||
kcmObj.get()));
|
||||
kcmObj.get(), deviceInfo.hasVibrator()));
|
||||
|
||||
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
|
||||
for (size_t i = 0; i < ranges.size(); i++) {
|
||||
@@ -87,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>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;)V");
|
||||
"<init>", "(IILjava/lang/String;Ljava/lang/String;IILandroid/view/KeyCharacterMap;Z)V");
|
||||
|
||||
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
|
||||
"addMotionRange", "(IIFFFF)V");
|
||||
|
||||
@@ -93,6 +93,9 @@ public:
|
||||
return mKeyCharacterMap;
|
||||
}
|
||||
|
||||
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
|
||||
inline bool hasVibrator() const { return mHasVibrator; }
|
||||
|
||||
inline const Vector<MotionRange>& getMotionRanges() const {
|
||||
return mMotionRanges;
|
||||
}
|
||||
@@ -105,6 +108,7 @@ private:
|
||||
uint32_t mSources;
|
||||
int32_t mKeyboardType;
|
||||
sp<KeyCharacterMap> mKeyCharacterMap;
|
||||
bool mHasVibrator;
|
||||
|
||||
Vector<MotionRange> mMotionRanges;
|
||||
};
|
||||
|
||||
@@ -136,6 +136,7 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
|
||||
mSources(other.mSources),
|
||||
mKeyboardType(other.mKeyboardType),
|
||||
mKeyCharacterMap(other.mKeyCharacterMap),
|
||||
mHasVibrator(other.mHasVibrator),
|
||||
mMotionRanges(other.mMotionRanges) {
|
||||
}
|
||||
|
||||
@@ -150,6 +151,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation,
|
||||
mDescriptor = descriptor;
|
||||
mSources = 0;
|
||||
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
|
||||
mHasVibrator = false;
|
||||
mMotionRanges.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -161,12 +161,14 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path,
|
||||
const InputDeviceIdentifier& identifier) :
|
||||
next(NULL),
|
||||
fd(fd), id(id), path(path), identifier(identifier),
|
||||
classes(0), configuration(NULL), virtualKeyMap(NULL) {
|
||||
classes(0), configuration(NULL), virtualKeyMap(NULL),
|
||||
ffEffectPlaying(false), ffEffectId(-1) {
|
||||
memset(keyBitmask, 0, sizeof(keyBitmask));
|
||||
memset(absBitmask, 0, sizeof(absBitmask));
|
||||
memset(relBitmask, 0, sizeof(relBitmask));
|
||||
memset(swBitmask, 0, sizeof(swBitmask));
|
||||
memset(ledBitmask, 0, sizeof(ledBitmask));
|
||||
memset(ffBitmask, 0, sizeof(ffBitmask));
|
||||
memset(propBitmask, 0, sizeof(propBitmask));
|
||||
}
|
||||
|
||||
@@ -534,6 +536,62 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && !device->isVirtual()) {
|
||||
ff_effect effect;
|
||||
memset(&effect, 0, sizeof(effect));
|
||||
effect.type = FF_RUMBLE;
|
||||
effect.id = device->ffEffectId;
|
||||
effect.u.rumble.strong_magnitude = 0xc000;
|
||||
effect.u.rumble.weak_magnitude = 0xc000;
|
||||
effect.replay.length = (duration + 999999LL) / 1000000LL;
|
||||
effect.replay.delay = 0;
|
||||
if (ioctl(device->fd, EVIOCSFF, &effect)) {
|
||||
ALOGW("Could not upload force feedback effect to device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
device->ffEffectId = effect.id;
|
||||
|
||||
struct input_event ev;
|
||||
ev.time.tv_sec = 0;
|
||||
ev.time.tv_usec = 0;
|
||||
ev.type = EV_FF;
|
||||
ev.code = device->ffEffectId;
|
||||
ev.value = 1;
|
||||
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
||||
ALOGW("Could not start force feedback effect on device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
device->ffEffectPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EventHub::cancelVibrate(int32_t deviceId) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device && !device->isVirtual()) {
|
||||
if (device->ffEffectPlaying) {
|
||||
device->ffEffectPlaying = false;
|
||||
|
||||
struct input_event ev;
|
||||
ev.time.tv_sec = 0;
|
||||
ev.time.tv_usec = 0;
|
||||
ev.type = EV_FF;
|
||||
ev.code = device->ffEffectId;
|
||||
ev.value = 0;
|
||||
if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) {
|
||||
ALOGW("Could not stop force feedback effect on device %s due to error %d.",
|
||||
device->identifier.name.string(), errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
|
||||
if (deviceId == BUILT_IN_KEYBOARD_ID) {
|
||||
deviceId = mBuiltInKeyboardId;
|
||||
@@ -949,6 +1007,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
|
||||
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
|
||||
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
|
||||
|
||||
// See if this is a keyboard. Ignore everything in the button range except for
|
||||
@@ -1010,6 +1069,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether this device supports the vibrator.
|
||||
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
|
||||
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
|
||||
}
|
||||
|
||||
// Configure virtual keys.
|
||||
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
|
||||
// Load the virtual keys for the touch screen, if any.
|
||||
|
||||
@@ -113,6 +113,9 @@ enum {
|
||||
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
|
||||
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
|
||||
|
||||
/* The input device has a vibrator (supports FF_RUMBLE). */
|
||||
INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200,
|
||||
|
||||
/* The input device is virtual (not a real device, not part of UI configuration). */
|
||||
INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000,
|
||||
|
||||
@@ -219,6 +222,10 @@ public:
|
||||
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
|
||||
|
||||
/* Control the vibrator. */
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
|
||||
virtual void cancelVibrate(int32_t deviceId) = 0;
|
||||
|
||||
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
|
||||
virtual void requestReopenDevices() = 0;
|
||||
|
||||
@@ -277,6 +284,9 @@ public:
|
||||
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
|
||||
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration);
|
||||
virtual void cancelVibrate(int32_t deviceId);
|
||||
|
||||
virtual void requestReopenDevices();
|
||||
|
||||
virtual void wake();
|
||||
@@ -303,6 +313,7 @@ private:
|
||||
uint8_t relBitmask[(REL_MAX + 1) / 8];
|
||||
uint8_t swBitmask[(SW_MAX + 1) / 8];
|
||||
uint8_t ledBitmask[(LED_MAX + 1) / 8];
|
||||
uint8_t ffBitmask[(FF_MAX + 1) / 8];
|
||||
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];
|
||||
|
||||
String8 configurationFile;
|
||||
@@ -310,6 +321,9 @@ private:
|
||||
VirtualKeyMap* virtualKeyMap;
|
||||
KeyMap keyMap;
|
||||
|
||||
bool ffEffectPlaying;
|
||||
int16_t ffEffectId; // initially -1
|
||||
|
||||
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
|
||||
~Device();
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
// Log debug messages about gesture detection.
|
||||
#define DEBUG_GESTURES 0
|
||||
|
||||
// Log debug messages about the vibrator.
|
||||
#define DEBUG_VIBRATOR 0
|
||||
|
||||
#include "InputReader.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
@@ -273,9 +276,7 @@ void InputReader::loopOnce() {
|
||||
mConfigurationChangesToRefresh = 0;
|
||||
timeoutMillis = 0;
|
||||
refreshConfigurationLocked(changes);
|
||||
}
|
||||
|
||||
if (timeoutMillis < 0 && mNextTimeout != LLONG_MAX) {
|
||||
} else if (mNextTimeout != LLONG_MAX) {
|
||||
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
|
||||
}
|
||||
@@ -426,6 +427,11 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
|
||||
device->addMapper(new SwitchInputMapper(device));
|
||||
}
|
||||
|
||||
// Vibrator-like devices.
|
||||
if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
|
||||
device->addMapper(new VibratorInputMapper(device));
|
||||
}
|
||||
|
||||
// Keyboard-like devices.
|
||||
uint32_t keyboardSource = 0;
|
||||
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
|
||||
@@ -594,6 +600,7 @@ void InputReader::fadePointerLocked() {
|
||||
void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
|
||||
if (when < mNextTimeout) {
|
||||
mNextTimeout = when;
|
||||
mEventHub->wake();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,6 +728,27 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) {
|
||||
}
|
||||
}
|
||||
|
||||
void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
|
||||
ssize_t repeat, int32_t token) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
|
||||
if (deviceIndex >= 0) {
|
||||
InputDevice* device = mDevices.valueAt(deviceIndex);
|
||||
device->vibrate(pattern, patternSize, repeat, token);
|
||||
}
|
||||
}
|
||||
|
||||
void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
|
||||
if (deviceIndex >= 0) {
|
||||
InputDevice* device = mDevices.valueAt(deviceIndex);
|
||||
device->cancelVibrate(token);
|
||||
}
|
||||
}
|
||||
|
||||
void InputReader::dump(String8& dump) {
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
@@ -1054,6 +1082,23 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
return result;
|
||||
}
|
||||
|
||||
void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token) {
|
||||
size_t numMappers = mMappers.size();
|
||||
for (size_t i = 0; i < numMappers; i++) {
|
||||
InputMapper* mapper = mMappers[i];
|
||||
mapper->vibrate(pattern, patternSize, repeat, token);
|
||||
}
|
||||
}
|
||||
|
||||
void InputDevice::cancelVibrate(int32_t token) {
|
||||
size_t numMappers = mMappers.size();
|
||||
for (size_t i = 0; i < numMappers; i++) {
|
||||
InputMapper* mapper = mMappers[i];
|
||||
mapper->cancelVibrate(token);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t InputDevice::getMetaState() {
|
||||
int32_t result = 0;
|
||||
size_t numMappers = mMappers.size();
|
||||
@@ -1739,6 +1784,13 @@ bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token) {
|
||||
}
|
||||
|
||||
void InputMapper::cancelVibrate(int32_t token) {
|
||||
}
|
||||
|
||||
int32_t InputMapper::getMetaState() {
|
||||
return 0;
|
||||
}
|
||||
@@ -1796,6 +1848,120 @@ int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCod
|
||||
}
|
||||
|
||||
|
||||
// --- VibratorInputMapper ---
|
||||
|
||||
VibratorInputMapper::VibratorInputMapper(InputDevice* device) :
|
||||
InputMapper(device), mVibrating(false) {
|
||||
}
|
||||
|
||||
VibratorInputMapper::~VibratorInputMapper() {
|
||||
}
|
||||
|
||||
uint32_t VibratorInputMapper::getSources() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
|
||||
InputMapper::populateDeviceInfo(info);
|
||||
|
||||
info->setVibrator(true);
|
||||
}
|
||||
|
||||
void VibratorInputMapper::process(const RawEvent* rawEvent) {
|
||||
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
|
||||
}
|
||||
|
||||
void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token) {
|
||||
#if DEBUG_VIBRATOR
|
||||
String8 patternStr;
|
||||
for (size_t i = 0; i < patternSize; i++) {
|
||||
if (i != 0) {
|
||||
patternStr.append(", ");
|
||||
}
|
||||
patternStr.appendFormat("%lld", pattern[i]);
|
||||
}
|
||||
ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d",
|
||||
getDeviceId(), patternStr.string(), repeat, token);
|
||||
#endif
|
||||
|
||||
mVibrating = true;
|
||||
memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t));
|
||||
mPatternSize = patternSize;
|
||||
mRepeat = repeat;
|
||||
mToken = token;
|
||||
mIndex = -1;
|
||||
|
||||
nextStep();
|
||||
}
|
||||
|
||||
void VibratorInputMapper::cancelVibrate(int32_t token) {
|
||||
#if DEBUG_VIBRATOR
|
||||
ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
|
||||
#endif
|
||||
|
||||
if (mVibrating && mToken == token) {
|
||||
stopVibrating();
|
||||
}
|
||||
}
|
||||
|
||||
void VibratorInputMapper::timeoutExpired(nsecs_t when) {
|
||||
if (mVibrating) {
|
||||
if (when >= mNextStepTime) {
|
||||
nextStep();
|
||||
} else {
|
||||
getContext()->requestTimeoutAtTime(mNextStepTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VibratorInputMapper::nextStep() {
|
||||
mIndex += 1;
|
||||
if (size_t(mIndex) >= mPatternSize) {
|
||||
if (mRepeat < 0) {
|
||||
// We are done.
|
||||
stopVibrating();
|
||||
return;
|
||||
}
|
||||
mIndex = mRepeat;
|
||||
}
|
||||
|
||||
bool vibratorOn = mIndex & 1;
|
||||
nsecs_t duration = mPattern[mIndex];
|
||||
if (vibratorOn) {
|
||||
#if DEBUG_VIBRATOR
|
||||
ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld",
|
||||
getDeviceId(), duration);
|
||||
#endif
|
||||
getEventHub()->vibrate(getDeviceId(), duration);
|
||||
} else {
|
||||
#if DEBUG_VIBRATOR
|
||||
ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
|
||||
#endif
|
||||
getEventHub()->cancelVibrate(getDeviceId());
|
||||
}
|
||||
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
mNextStepTime = now + duration;
|
||||
getContext()->requestTimeoutAtTime(mNextStepTime);
|
||||
#if DEBUG_VIBRATOR
|
||||
ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VibratorInputMapper::stopVibrating() {
|
||||
mVibrating = false;
|
||||
#if DEBUG_VIBRATOR
|
||||
ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
|
||||
#endif
|
||||
getEventHub()->cancelVibrate(getDeviceId());
|
||||
}
|
||||
|
||||
void VibratorInputMapper::dump(String8& dump) {
|
||||
dump.append(INDENT2 "Vibrator Input Mapper:\n");
|
||||
dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating));
|
||||
}
|
||||
|
||||
|
||||
// --- KeyboardInputMapper ---
|
||||
|
||||
KeyboardInputMapper::KeyboardInputMapper(InputDevice* device,
|
||||
|
||||
@@ -33,6 +33,14 @@
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Maximum supported size of a vibration pattern.
|
||||
// Must be at least 2.
|
||||
#define MAX_VIBRATE_PATTERN_SIZE 100
|
||||
|
||||
// Maximum allowable delay value in a vibration pattern before
|
||||
// which the delay will be truncated.
|
||||
#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL)
|
||||
|
||||
namespace android {
|
||||
|
||||
class InputDevice;
|
||||
@@ -267,6 +275,11 @@ public:
|
||||
* The changes flag is a bitfield that indicates what has changed and whether
|
||||
* the input devices must all be reopened. */
|
||||
virtual void requestRefreshConfiguration(uint32_t changes) = 0;
|
||||
|
||||
/* Controls the vibrator of a particular input device. */
|
||||
virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
|
||||
ssize_t repeat, int32_t token) = 0;
|
||||
virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -334,6 +347,10 @@ public:
|
||||
|
||||
virtual void requestRefreshConfiguration(uint32_t changes);
|
||||
|
||||
virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
|
||||
ssize_t repeat, int32_t token);
|
||||
virtual void cancelVibrate(int32_t deviceId, int32_t token);
|
||||
|
||||
protected:
|
||||
// These members are protected so they can be instrumented by test cases.
|
||||
virtual InputDevice* createDeviceLocked(int32_t deviceId,
|
||||
@@ -466,6 +483,8 @@ public:
|
||||
int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
|
||||
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
const int32_t* keyCodes, uint8_t* outFlags);
|
||||
void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
|
||||
void cancelVibrate(int32_t token);
|
||||
|
||||
int32_t getMetaState();
|
||||
|
||||
@@ -848,6 +867,9 @@ public:
|
||||
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
|
||||
virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
|
||||
const int32_t* keyCodes, uint8_t* outFlags);
|
||||
virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token);
|
||||
virtual void cancelVibrate(int32_t token);
|
||||
|
||||
virtual int32_t getMetaState();
|
||||
|
||||
@@ -880,6 +902,35 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class VibratorInputMapper : public InputMapper {
|
||||
public:
|
||||
VibratorInputMapper(InputDevice* device);
|
||||
virtual ~VibratorInputMapper();
|
||||
|
||||
virtual uint32_t getSources();
|
||||
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
|
||||
virtual void process(const RawEvent* rawEvent);
|
||||
|
||||
virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
|
||||
int32_t token);
|
||||
virtual void cancelVibrate(int32_t token);
|
||||
virtual void timeoutExpired(nsecs_t when);
|
||||
virtual void dump(String8& dump);
|
||||
|
||||
private:
|
||||
bool mVibrating;
|
||||
nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE];
|
||||
size_t mPatternSize;
|
||||
ssize_t mRepeat;
|
||||
int32_t mToken;
|
||||
ssize_t mIndex;
|
||||
nsecs_t mNextStepTime;
|
||||
|
||||
void nextStep();
|
||||
void stopVibrating();
|
||||
};
|
||||
|
||||
|
||||
class KeyboardInputMapper : public InputMapper {
|
||||
public:
|
||||
KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
|
||||
|
||||
@@ -646,6 +646,12 @@ private:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration) {
|
||||
}
|
||||
|
||||
virtual void cancelVibrate(int32_t deviceId) {
|
||||
}
|
||||
|
||||
virtual bool isExternal(int32_t deviceId) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,12 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
mTempInputDevicesChangedListenersToNotify =
|
||||
new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
|
||||
|
||||
// State for vibrator tokens.
|
||||
private Object mVibratorLock = new Object();
|
||||
private HashMap<IBinder, VibratorToken> mVibratorTokens =
|
||||
new HashMap<IBinder, VibratorToken>();
|
||||
private int mNextVibratorTokenValue;
|
||||
|
||||
// State for the currently installed input filter.
|
||||
final Object mInputFilterLock = new Object();
|
||||
InputFilter mInputFilter; // guarded by mInputFilterLock
|
||||
@@ -142,6 +148,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
InputChannel fromChannel, InputChannel toChannel);
|
||||
private static native void nativeSetPointerSpeed(int ptr, int speed);
|
||||
private static native void nativeSetShowTouches(int ptr, boolean enabled);
|
||||
private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
|
||||
int repeat, int token);
|
||||
private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
|
||||
private static native String nativeDump(int ptr);
|
||||
private static native void nativeMonitor(int ptr);
|
||||
|
||||
@@ -792,6 +801,65 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
|
||||
if (repeat >= pattern.length) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
VibratorToken v;
|
||||
synchronized (mVibratorLock) {
|
||||
v = mVibratorTokens.get(token);
|
||||
if (v == null) {
|
||||
v = new VibratorToken(deviceId, token, mNextVibratorTokenValue++);
|
||||
try {
|
||||
token.linkToDeath(v, 0);
|
||||
} catch (RemoteException ex) {
|
||||
// give up
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
mVibratorTokens.put(token, v);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (v) {
|
||||
v.mVibrating = true;
|
||||
nativeVibrate(mPtr, deviceId, pattern, repeat, v.mTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void cancelVibrate(int deviceId, IBinder token) {
|
||||
VibratorToken v;
|
||||
synchronized (mVibratorLock) {
|
||||
v = mVibratorTokens.get(token);
|
||||
if (v == null || v.mDeviceId != deviceId) {
|
||||
return; // nothing to cancel
|
||||
}
|
||||
}
|
||||
|
||||
cancelVibrateIfNeeded(v);
|
||||
}
|
||||
|
||||
void onVibratorTokenDied(VibratorToken v) {
|
||||
synchronized (mVibratorLock) {
|
||||
mVibratorTokens.remove(v.mToken);
|
||||
}
|
||||
|
||||
cancelVibrateIfNeeded(v);
|
||||
}
|
||||
|
||||
private void cancelVibrateIfNeeded(VibratorToken v) {
|
||||
synchronized (v) {
|
||||
if (v.mVibrating) {
|
||||
nativeCancelVibrate(mPtr, v.mDeviceId, v.mTokenValue);
|
||||
v.mVibrating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
|
||||
@@ -1108,4 +1176,26 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class VibratorToken implements DeathRecipient {
|
||||
public final int mDeviceId;
|
||||
public final IBinder mToken;
|
||||
public final int mTokenValue;
|
||||
|
||||
public boolean mVibrating;
|
||||
|
||||
public VibratorToken(int deviceId, IBinder token, int tokenValue) {
|
||||
mDeviceId = deviceId;
|
||||
mToken = token;
|
||||
mTokenValue = tokenValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Vibrator token died.");
|
||||
}
|
||||
onVibratorTokenDied(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1226,6 +1226,38 @@ static void nativeSetShowTouches(JNIEnv* env,
|
||||
im->setShowTouches(enabled);
|
||||
}
|
||||
|
||||
static void nativeVibrate(JNIEnv* env,
|
||||
jclass clazz, jint ptr, jint deviceId, jlongArray patternObj,
|
||||
jint repeat, jint token) {
|
||||
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
|
||||
|
||||
size_t patternSize = env->GetArrayLength(patternObj);
|
||||
if (patternSize > MAX_VIBRATE_PATTERN_SIZE) {
|
||||
ALOGI("Skipped requested vibration because the pattern size is %d "
|
||||
"which is more than the maximum supported size of %d.",
|
||||
patternSize, MAX_VIBRATE_PATTERN_SIZE);
|
||||
return; // limit to reasonable size
|
||||
}
|
||||
|
||||
jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
|
||||
patternObj, NULL));
|
||||
nsecs_t pattern[patternSize];
|
||||
for (size_t i = 0; i < patternSize; i++) {
|
||||
pattern[i] = max(jlong(0), min(patternMillis[i],
|
||||
MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL)) * 1000000LL;
|
||||
}
|
||||
env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
|
||||
|
||||
im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token);
|
||||
}
|
||||
|
||||
static void nativeCancelVibrate(JNIEnv* env,
|
||||
jclass clazz, jint ptr, jint deviceId, jint token) {
|
||||
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
|
||||
|
||||
im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
|
||||
}
|
||||
|
||||
static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
|
||||
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
|
||||
|
||||
@@ -1287,6 +1319,10 @@ static JNINativeMethod gInputManagerMethods[] = {
|
||||
(void*) nativeSetPointerSpeed },
|
||||
{ "nativeSetShowTouches", "(IZ)V",
|
||||
(void*) nativeSetShowTouches },
|
||||
{ "nativeVibrate", "(II[JII)V",
|
||||
(void*) nativeVibrate },
|
||||
{ "nativeCancelVibrate", "(III)V",
|
||||
(void*) nativeCancelVibrate },
|
||||
{ "nativeDump", "(I)Ljava/lang/String;",
|
||||
(void*) nativeDump },
|
||||
{ "nativeMonitor", "(I)V",
|
||||
|
||||
Reference in New Issue
Block a user