Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b17a213 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add vibrator state listener support for input device vibrator" into sc-dev am: 2fa12f8e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13476235

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Id5c39d696345d833ac18d9211c868dd616b45889
parents e8639661 2fa12f8e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
+86 −7
Original line number Diff line number Diff line
@@ -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
+27 −0
Original line number Diff line number Diff line
@@ -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.
+98 −0
Original line number Diff line number Diff line
@@ -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) {
+16 −0
Original line number Diff line number Diff line
@@ -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");