Merge "Add vibrator state listener support for input device vibrator" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-08 19:05:34 +00:00
committed by Android (Google) Code Review
5 changed files with 230 additions and 7 deletions

View File

@@ -26,6 +26,7 @@ import android.os.CombinedVibrationEffect;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -92,6 +93,8 @@ interface IInputManager {
void cancelVibrate(int deviceId, IBinder token);
int[] getVibratorIds(int deviceId);
boolean isVibrating(int deviceId);
boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
// Input device battery query.
int getBatteryStatus(int deviceId);

View File

@@ -18,10 +18,18 @@ package android.hardware.input;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.Context;
import android.os.Binder;
import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.util.concurrent.Executor;
@@ -29,12 +37,18 @@ import java.util.concurrent.Executor;
* Vibrator implementation that communicates with the input device vibrators.
*/
final class InputDeviceVibrator extends Vibrator {
private static final String TAG = "InputDeviceVibrator";
// mDeviceId represents InputDevice ID the vibrator belongs to
private final int mDeviceId;
private final int mVibratorId;
private final Binder mToken;
private final InputManager mInputManager;
@GuardedBy("mDelegates")
private final ArrayMap<OnVibratorStateChangedListener,
OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
mInputManager = inputManager;
mDeviceId = deviceId;
@@ -42,6 +56,23 @@ final class InputDeviceVibrator extends Vibrator {
mToken = new Binder();
}
private class OnVibratorStateChangedListenerDelegate extends
IVibratorStateListener.Stub {
private final Executor mExecutor;
private final OnVibratorStateChangedListener mListener;
OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
@NonNull Executor executor) {
mExecutor = executor;
mListener = listener;
}
@Override
public void onVibrating(boolean isVibrating) {
mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
}
}
@Override
public boolean hasVibrator() {
return true;
@@ -52,25 +83,73 @@ final class InputDeviceVibrator extends Vibrator {
return mInputManager.isVibrating(mDeviceId);
}
/* TODO: b/161634264 Support Vibrator listener API in input devices */
/**
* Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
* If the listener was previously added and not removed, this call will be ignored.
*
* @param listener listener to be added
*/
@Override
public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
throw new UnsupportedOperationException(
"addVibratorStateListener not supported in InputDeviceVibrator");
Preconditions.checkNotNull(listener);
Context context = ActivityThread.currentApplication();
addVibratorStateListener(context.getMainExecutor(), listener);
}
/**
* Adds a listener for vibrator state change. If the listener was previously added and not
* removed, this call will be ignored.
*
* @param listener Listener to be added.
* @param executor The {@link Executor} on which the listener's callbacks will be executed on.
*/
@Override
public void addVibratorStateListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnVibratorStateChangedListener listener) {
throw new UnsupportedOperationException(
"addVibratorStateListener not supported in InputDeviceVibrator");
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
synchronized (mDelegates) {
// If listener is already registered, reject and return.
if (mDelegates.containsKey(listener)) {
Log.w(TAG, "Listener already registered.");
return;
}
final OnVibratorStateChangedListenerDelegate delegate =
new OnVibratorStateChangedListenerDelegate(listener, executor);
if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) {
Log.w(TAG, "Failed to register vibrate state listener");
return;
}
mDelegates.put(listener, delegate);
}
}
/**
* Removes the listener for vibrator state changes. If the listener was not previously
* registered, this call will do nothing.
*
* @param listener Listener to be removed.
*/
@Override
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
throw new UnsupportedOperationException(
"removeVibratorStateListener not supported in InputDeviceVibrator");
Preconditions.checkNotNull(listener);
synchronized (mDelegates) {
// Check if the listener is registered, otherwise will return.
if (mDelegates.containsKey(listener)) {
final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) {
Log.w(TAG, "Failed to unregister vibrate state listener");
return;
}
mDelegates.remove(listener);
}
}
}
@Override

View File

@@ -35,6 +35,7 @@ import android.os.Build;
import android.os.CombinedVibrationEffect;
import android.os.Handler;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.InputEventInjectionSync;
import android.os.Looper;
import android.os.Message;
@@ -1483,6 +1484,32 @@ public final class InputManager {
}
}
/**
* Register input device vibrator state listener
*
* @hide
*/
public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.registerVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Unregister input device vibrator state listener
*
* @hide
*/
public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.unregisterVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Gets a sensor manager service associated with an input device, always create a new instance.
* @return The sensor manager, never null.

View File

@@ -57,6 +57,7 @@ import android.os.CombinedVibrationEffect;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.InputEventInjectionResult;
import android.os.InputEventInjectionSync;
import android.os.LocaleList;
@@ -64,6 +65,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -77,6 +79,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
@@ -100,6 +103,7 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -221,6 +225,14 @@ public class InputManagerService extends IInputManager.Stub
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
// List of currently registered vibrator state changed listeners by device id.
@GuardedBy("mVibratorLock")
private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
// List of vibrator states by device id.
@GuardedBy("mVibratorLock")
private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
// State for lid switch
// Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
// are delivered in order. For ex, when a new lid switch callback is registered the lock is held
@@ -2008,6 +2020,92 @@ public class InputManagerService extends IInputManager.Stub
}
}
// Native callback.
private void notifyVibratorState(int deviceId, boolean isOn) {
if (DEBUG) {
Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
}
synchronized (mVibratorLock) {
mIsVibrating.put(deviceId, isOn);
notifyVibratorStateListenersLocked(deviceId);
}
}
@GuardedBy("mVibratorLock")
private void notifyVibratorStateListenersLocked(int deviceId) {
if (!mVibratorStateListeners.contains(deviceId)) {
if (DEBUG) {
Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener.");
}
return;
}
RemoteCallbackList<IVibratorStateListener> listeners =
mVibratorStateListeners.get(deviceId);
final int length = listeners.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i));
}
} finally {
listeners.finishBroadcast();
}
}
@GuardedBy("mVibratorLock")
private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) {
try {
listener.onVibrating(mIsVibrating.get(deviceId));
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Vibrator state listener failed to call", e);
}
}
@Override // Binder call
public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
Preconditions.checkNotNull(listener, "listener must not be null");
RemoteCallbackList<IVibratorStateListener> listeners;
synchronized (mVibratorLock) {
if (!mVibratorStateListeners.contains(deviceId)) {
listeners = new RemoteCallbackList<>();
mVibratorStateListeners.put(deviceId, listeners);
} else {
listeners = mVibratorStateListeners.get(deviceId);
}
final long token = Binder.clearCallingIdentity();
try {
if (!listeners.register(listener)) {
Slog.e(TAG, "Could not register vibrator state listener " + listener);
return false;
}
// Notify its callback after new client registered.
notifyVibratorStateListenerLocked(deviceId, listener);
return true;
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override // Binder call
public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
synchronized (mVibratorLock) {
final long token = Binder.clearCallingIdentity();
try {
if (!mVibratorStateListeners.contains(deviceId)) {
Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist");
return false;
}
RemoteCallbackList<IVibratorStateListener> listeners =
mVibratorStateListeners.get(deviceId);
return listeners.unregister(listener);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
// Binder call
@Override
public int getBatteryStatus(int deviceId) {

View File

@@ -106,6 +106,7 @@ static struct {
jmethodID notifyFocusChanged;
jmethodID notifySensorEvent;
jmethodID notifySensorAccuracy;
jmethodID notifyVibratorState;
jmethodID notifyUntrustedTouch;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
@@ -305,6 +306,7 @@ public:
const std::vector<float>& values) override;
void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
InputDeviceSensorAccuracy accuracy) override;
void notifyVibratorState(int32_t deviceId, bool isOn) override;
void notifyUntrustedTouch(const std::string& obscuringPackage) override;
bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -918,6 +920,18 @@ void NativeInputManager::notifySensorAccuracy(int32_t deviceId, InputDeviceSenso
checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
}
void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyVibratorState isOn:%d", isOn);
#endif
ATRACE_CALL();
JNIEnv* env = jniEnv();
ScopedLocalFrame localFrame(env);
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState,
static_cast<jint>(deviceId), static_cast<jboolean>(isOn));
checkAndClearExceptionFromCallback(env, "notifyVibratorState");
}
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -2248,6 +2262,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
"(Ljava/lang/String;)V");