/* * Copyright (C) 2020 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.hardware.input; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.app.ActivityThread; import android.content.Context; import android.hardware.vibrator.IVibrator; import android.os.Binder; import android.os.IVibratorStateListener; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; 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; /** * 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 VibratorInfo mVibratorInfo; private final Binder mToken; private final InputManagerGlobal mGlobal; @GuardedBy("mDelegates") private final ArrayMap mDelegates = new ArrayMap<>(); InputDeviceVibrator(int deviceId, int vibratorId) { mGlobal = InputManagerGlobal.getInstance(); mDeviceId = deviceId; mVibratorInfo = new VibratorInfo.Builder(vibratorId) .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) // The supported effect and braking lists are known to be empty for input devices, // which is different from not being set (that means the device support is unknown). .setSupportedEffects(new int[0]) .setSupportedBraking(new int[0]) .build(); 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 VibratorInfo getInfo() { return mVibratorInfo; } @Override public boolean hasVibrator() { return true; } @Override public boolean isVibrating() { return mGlobal.isVibrating(mDeviceId); } /** * 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) { 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) { 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 (!mGlobal.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) { 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 (!mGlobal.unregisterVibratorStateListener(mDeviceId, delegate)) { Log.w(TAG, "Failed to unregister vibrate state listener"); return; } mDelegates.remove(listener); } } } @Override public boolean hasAmplitudeControl() { return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL); } /** * @hide */ @Override public void vibrate(int uid, String opPkg, @NonNull VibrationEffect effect, String reason, @NonNull VibrationAttributes attributes) { mGlobal.vibrate(mDeviceId, effect, mToken); } @Override public void cancel() { mGlobal.cancelVibrate(mDeviceId, mToken); } @Override public void cancel(int usageFilter) { cancel(); } }