summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl3
-rw-r--r--core/java/android/hardware/input/InputDeviceVibrator.java93
-rw-r--r--core/java/android/hardware/input/InputManager.java27
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java98
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp16
5 files changed, 230 insertions, 7 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c69c47f80d01..eaa38f3e862c 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -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);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index c60d6ce46fdb..f4d8a65d54c6 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -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
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 185c59d8ccfc..8a01c660ebd0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -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;
@@ -1484,6 +1485,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.
* @hide
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2e4200c1f7d9..5ca255c6cdbe 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -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) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5b587e9859d8..643503d18bed 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -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");