349 lines
12 KiB
Java
349 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2021 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.annotation.CallbackExecutor;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.content.Context;
|
|
import android.util.ArrayMap;
|
|
import android.util.Log;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.internal.annotations.GuardedBy;
|
|
|
|
import java.util.Objects;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/**
|
|
* VibratorManager implementation that controls the system vibrators.
|
|
*
|
|
* @hide
|
|
*/
|
|
public class SystemVibratorManager extends VibratorManager {
|
|
private static final String TAG = "VibratorManager";
|
|
|
|
private final IVibratorManagerService mService;
|
|
private final Context mContext;
|
|
private final int mUid;
|
|
private final Binder mToken = new Binder();
|
|
private final Object mLock = new Object();
|
|
@GuardedBy("mLock")
|
|
private int[] mVibratorIds;
|
|
@GuardedBy("mLock")
|
|
private final SparseArray<Vibrator> mVibrators = new SparseArray<>();
|
|
|
|
@GuardedBy("mLock")
|
|
private final ArrayMap<Vibrator.OnVibratorStateChangedListener,
|
|
OnVibratorStateChangedListenerDelegate> mListeners = new ArrayMap<>();
|
|
|
|
/**
|
|
* @hide to prevent subclassing from outside of the framework
|
|
*/
|
|
public SystemVibratorManager(Context context) {
|
|
super(context);
|
|
mContext = context;
|
|
mUid = Process.myUid();
|
|
mService = IVibratorManagerService.Stub.asInterface(
|
|
ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE));
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public int[] getVibratorIds() {
|
|
synchronized (mLock) {
|
|
if (mVibratorIds != null) {
|
|
return mVibratorIds;
|
|
}
|
|
try {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to retrieve vibrator ids; no vibrator manager service.");
|
|
} else {
|
|
return mVibratorIds = mService.getVibratorIds();
|
|
}
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
return new int[0];
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public Vibrator getVibrator(int vibratorId) {
|
|
synchronized (mLock) {
|
|
Vibrator vibrator = mVibrators.get(vibratorId);
|
|
if (vibrator != null) {
|
|
return vibrator;
|
|
}
|
|
VibratorInfo info = null;
|
|
try {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to retrieve vibrator; no vibrator manager service.");
|
|
} else {
|
|
info = mService.getVibratorInfo(vibratorId);
|
|
}
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
if (info != null) {
|
|
vibrator = new SingleVibrator(info);
|
|
mVibrators.put(vibratorId, vibrator);
|
|
} else {
|
|
vibrator = NullVibrator.getInstance();
|
|
}
|
|
return vibrator;
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public Vibrator getDefaultVibrator() {
|
|
return mContext.getSystemService(Vibrator.class);
|
|
}
|
|
|
|
@Override
|
|
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
|
|
@Nullable CombinedVibration effect, @Nullable VibrationAttributes attributes) {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to set always-on effect; no vibrator manager service.");
|
|
return false;
|
|
}
|
|
try {
|
|
return mService.setAlwaysOnEffect(uid, opPkg, alwaysOnId, effect, attributes);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failed to set always-on effect.", e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
|
|
String reason, @Nullable VibrationAttributes attributes) {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
|
|
return;
|
|
}
|
|
try {
|
|
mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
|
|
mToken);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failed to vibrate.", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void performHapticFeedback(int constant, boolean always, String reason,
|
|
boolean fromIme) {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
|
|
return;
|
|
}
|
|
try {
|
|
mService.performHapticFeedback(
|
|
mUid, mContext.getDeviceId(), mPackageName, constant, always, reason, fromIme);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failed to perform haptic feedback.", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void cancel() {
|
|
cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL);
|
|
}
|
|
|
|
@Override
|
|
public void cancel(int usageFilter) {
|
|
cancelVibration(usageFilter);
|
|
}
|
|
|
|
private void cancelVibration(int usageFilter) {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to cancel vibration; no vibrator manager service.");
|
|
return;
|
|
}
|
|
try {
|
|
mService.cancelVibrate(usageFilter, mToken);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Failed to cancel vibration.", e);
|
|
}
|
|
}
|
|
|
|
/** Listener for vibrations on a single vibrator. */
|
|
private static class OnVibratorStateChangedListenerDelegate extends
|
|
IVibratorStateListener.Stub {
|
|
private final Executor mExecutor;
|
|
private final Vibrator.OnVibratorStateChangedListener mListener;
|
|
|
|
OnVibratorStateChangedListenerDelegate(
|
|
@NonNull Vibrator.OnVibratorStateChangedListener listener,
|
|
@NonNull Executor executor) {
|
|
mExecutor = executor;
|
|
mListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void onVibrating(boolean isVibrating) {
|
|
mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
|
|
}
|
|
}
|
|
|
|
/** Controls vibrations on a single vibrator. */
|
|
private final class SingleVibrator extends Vibrator {
|
|
private final VibratorInfo mVibratorInfo;
|
|
|
|
SingleVibrator(@NonNull VibratorInfo vibratorInfo) {
|
|
mVibratorInfo = vibratorInfo;
|
|
}
|
|
|
|
@Override
|
|
public VibratorInfo getInfo() {
|
|
return mVibratorInfo;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasVibrator() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasAmplitudeControl() {
|
|
return mVibratorInfo.hasAmplitudeControl();
|
|
}
|
|
|
|
@Override
|
|
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
|
|
@Nullable VibrationEffect effect, @Nullable VibrationAttributes attrs) {
|
|
CombinedVibration combined = CombinedVibration.startParallel()
|
|
.addVibrator(mVibratorInfo.getId(), effect)
|
|
.combine();
|
|
return SystemVibratorManager.this.setAlwaysOnEffect(uid, opPkg, alwaysOnId, combined,
|
|
attrs);
|
|
}
|
|
|
|
@Override
|
|
public void vibrate(int uid, String opPkg, @NonNull VibrationEffect vibe, String reason,
|
|
@NonNull VibrationAttributes attributes) {
|
|
CombinedVibration combined = CombinedVibration.startParallel()
|
|
.addVibrator(mVibratorInfo.getId(), vibe)
|
|
.combine();
|
|
SystemVibratorManager.this.vibrate(uid, opPkg, combined, reason, attributes);
|
|
}
|
|
|
|
@Override
|
|
public void performHapticFeedback(int effectId, boolean always, String reason,
|
|
boolean fromIme) {
|
|
SystemVibratorManager.this.performHapticFeedback(effectId, always, reason, fromIme);
|
|
}
|
|
|
|
@Override
|
|
public void cancel() {
|
|
SystemVibratorManager.this.cancel();
|
|
}
|
|
|
|
@Override
|
|
public void cancel(int usageFilter) {
|
|
SystemVibratorManager.this.cancel(usageFilter);
|
|
}
|
|
|
|
@Override
|
|
public boolean isVibrating() {
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to check status of vibrator " + mVibratorInfo.getId()
|
|
+ "; no vibrator service.");
|
|
return false;
|
|
}
|
|
try {
|
|
return mService.isVibrating(mVibratorInfo.getId());
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
|
|
Objects.requireNonNull(listener);
|
|
if (mContext == null) {
|
|
Log.w(TAG, "Failed to add vibrate state listener; no vibrator context.");
|
|
return;
|
|
}
|
|
addVibratorStateListener(mContext.getMainExecutor(), listener);
|
|
}
|
|
|
|
@Override
|
|
public void addVibratorStateListener(
|
|
@NonNull @CallbackExecutor Executor executor,
|
|
@NonNull OnVibratorStateChangedListener listener) {
|
|
Objects.requireNonNull(listener);
|
|
Objects.requireNonNull(executor);
|
|
if (mService == null) {
|
|
Log.w(TAG,
|
|
"Failed to add vibrate state listener to vibrator " + mVibratorInfo.getId()
|
|
+ "; no vibrator service.");
|
|
return;
|
|
}
|
|
synchronized (mLock) {
|
|
// If listener is already registered, reject and return.
|
|
if (mListeners.containsKey(listener)) {
|
|
Log.w(TAG, "Listener already registered.");
|
|
return;
|
|
}
|
|
try {
|
|
OnVibratorStateChangedListenerDelegate delegate =
|
|
new OnVibratorStateChangedListenerDelegate(listener, executor);
|
|
if (!mService.registerVibratorStateListener(mVibratorInfo.getId(), delegate)) {
|
|
Log.w(TAG, "Failed to add vibrate state listener to vibrator "
|
|
+ mVibratorInfo.getId());
|
|
return;
|
|
}
|
|
mListeners.put(listener, delegate);
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
|
|
Objects.requireNonNull(listener);
|
|
if (mService == null) {
|
|
Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
|
|
+ mVibratorInfo.getId() + "; no vibrator service.");
|
|
return;
|
|
}
|
|
synchronized (mLock) {
|
|
// Check if the listener is registered, otherwise will return.
|
|
if (mListeners.containsKey(listener)) {
|
|
OnVibratorStateChangedListenerDelegate delegate = mListeners.get(listener);
|
|
try {
|
|
if (!mService.unregisterVibratorStateListener(mVibratorInfo.getId(),
|
|
delegate)) {
|
|
Log.w(TAG, "Failed to remove vibrate state listener from vibrator "
|
|
+ mVibratorInfo.getId());
|
|
return;
|
|
}
|
|
mListeners.remove(listener);
|
|
} catch (RemoteException e) {
|
|
e.rethrowFromSystemServer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|