Merge "Add support for input devices that have vibrators."

This commit is contained in:
Jeff Brown
2012-04-13 17:34:20 -07:00
committed by Android (Google) Code Review
15 changed files with 589 additions and 7 deletions

View File

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

View File

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

View File

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

View 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() {
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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