Merge "Extract input device vibrators code from VibratorService"
This commit is contained in:
committed by
Android (Google) Code Review
commit
54db67bd6c
@@ -56,6 +56,7 @@ import android.view.PointerIcon;
|
||||
import android.view.VerifiedInputEvent;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
@@ -268,6 +269,21 @@ public final class InputManager {
|
||||
mIm = im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the input manager.
|
||||
*
|
||||
* @return The input manager instance.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static InputManager resetInstance(IInputManager inputManagerService) {
|
||||
synchronized (InputManager.class) {
|
||||
sInstance = new InputManager(inputManagerService);
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the input manager.
|
||||
*
|
||||
@@ -1428,7 +1444,7 @@ public final class InputManager {
|
||||
|
||||
@Override
|
||||
public boolean hasAmplitudeControl() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,8 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Vibrator;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
@@ -423,9 +425,13 @@ public final class InputDevice implements Parcelable {
|
||||
}
|
||||
};
|
||||
|
||||
// Called by native code.
|
||||
/**
|
||||
* Called by native code
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
|
||||
@VisibleForTesting
|
||||
public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
|
||||
int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
|
||||
KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone,
|
||||
boolean hasButtonUnderPad) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Binder;
|
||||
@@ -60,13 +59,13 @@ import android.os.WorkSource;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.server.vibrator.InputDeviceDelegate;
|
||||
import com.android.server.vibrator.VibrationScaler;
|
||||
import com.android.server.vibrator.VibrationSettings;
|
||||
|
||||
@@ -82,15 +81,15 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class VibratorService extends IVibratorService.Stub
|
||||
implements InputManager.InputDeviceListener {
|
||||
/** System implementation of {@link IVibratorService}. */
|
||||
public class VibratorService extends IVibratorService.Stub {
|
||||
private static final String TAG = "VibratorService";
|
||||
private static final SimpleDateFormat DEBUG_DATE_FORMAT =
|
||||
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
|
||||
|
||||
private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
|
||||
private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = {0, 30, 100, 30};
|
||||
|
||||
// Default vibration attributes. Used when vibration is requested without attributes
|
||||
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
|
||||
@@ -122,19 +121,13 @@ public class VibratorService extends IVibratorService.Stub
|
||||
private final IBatteryStats mBatteryStatsService;
|
||||
private final String mSystemUiPackage;
|
||||
private PowerManagerInternal mPowerManagerInternal;
|
||||
private InputManager mIm;
|
||||
private VibrationSettings mVibrationSettings;
|
||||
private VibrationScaler mVibrationScaler;
|
||||
private InputDeviceDelegate mInputDeviceDelegate;
|
||||
|
||||
private final NativeWrapper mNativeWrapper;
|
||||
private volatile VibrateWaveformThread mThread;
|
||||
|
||||
// mInputDeviceVibrators lock should be acquired after mLock, if both are
|
||||
// to be acquired
|
||||
private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<>();
|
||||
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
|
||||
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private Vibration mCurrentVibration;
|
||||
private int mCurVibUid = -1;
|
||||
@@ -343,6 +336,7 @@ public class VibratorService extends IVibratorService.Stub
|
||||
public enum Status {
|
||||
RUNNING,
|
||||
FINISHED,
|
||||
FORWARDED_TO_INPUT_DEVICES,
|
||||
CANCELLED,
|
||||
ERROR_APP_OPS,
|
||||
IGNORED,
|
||||
@@ -577,9 +571,9 @@ public class VibratorService extends IVibratorService.Stub
|
||||
public void systemReady() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
|
||||
try {
|
||||
mIm = mContext.getSystemService(InputManager.class);
|
||||
mVibrationSettings = new VibrationSettings(mContext, mH);
|
||||
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
|
||||
mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH);
|
||||
|
||||
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
|
||||
mPowerManagerInternal.registerLowPowerModeObserver(
|
||||
@@ -707,12 +701,8 @@ public class VibratorService extends IVibratorService.Stub
|
||||
|
||||
@Override // Binder call
|
||||
public boolean hasAmplitudeControl() {
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
// Input device vibrators don't support amplitude controls yet, but are still used over
|
||||
// the system vibrator when connected.
|
||||
return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||
&& mInputDeviceVibrators.isEmpty();
|
||||
}
|
||||
// Input device vibrators always support amplitude controls.
|
||||
return mInputDeviceDelegate.isAvailable() || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -1059,10 +1049,8 @@ public class VibratorService extends IVibratorService.Stub
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
doVibratorOn(vib);
|
||||
} else if (vib.effect instanceof VibrationEffect.Waveform) {
|
||||
// mThread better be null here. doCancelVibrate should always be
|
||||
// called before startNextVibrationLocked or startVibrationLocked.
|
||||
mThread = new VibrateWaveformThread(vib);
|
||||
mThread.start();
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
doVibratorWaveformEffectLocked(vib);
|
||||
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
doVibratorPrebakedEffectLocked(vib);
|
||||
@@ -1193,7 +1181,8 @@ public class VibratorService extends IVibratorService.Stub
|
||||
|
||||
private void updateVibrators() {
|
||||
synchronized (mLock) {
|
||||
boolean devicesUpdated = updateInputDeviceVibratorsLocked();
|
||||
boolean devicesUpdated = mInputDeviceDelegate.updateInputDeviceVibrators(
|
||||
mVibrationSettings.shouldVibrateInputDevices());
|
||||
boolean lowPowerModeUpdated = updateLowPowerModeLocked();
|
||||
|
||||
if (devicesUpdated || lowPowerModeUpdated) {
|
||||
@@ -1205,41 +1194,6 @@ public class VibratorService extends IVibratorService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updateInputDeviceVibratorsLocked() {
|
||||
boolean changed = false;
|
||||
boolean vibrateInputDevices = mVibrationSettings.shouldVibrateInputDevices();
|
||||
if (vibrateInputDevices != mVibrateInputDevicesSetting) {
|
||||
changed = true;
|
||||
mVibrateInputDevicesSetting = vibrateInputDevices;
|
||||
}
|
||||
|
||||
if (mVibrateInputDevicesSetting) {
|
||||
if (!mInputDeviceListenerRegistered) {
|
||||
mInputDeviceListenerRegistered = true;
|
||||
mIm.registerInputDeviceListener(this, mH);
|
||||
}
|
||||
} else {
|
||||
if (mInputDeviceListenerRegistered) {
|
||||
mInputDeviceListenerRegistered = false;
|
||||
mIm.unregisterInputDeviceListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
mInputDeviceVibrators.clear();
|
||||
if (mVibrateInputDevicesSetting) {
|
||||
int[] ids = mIm.getInputDeviceIds();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
InputDevice device = mIm.getInputDevice(ids[i]);
|
||||
Vibrator vibrator = device.getVibrator();
|
||||
if (vibrator.hasVibrator()) {
|
||||
mInputDeviceVibrators.add(vibrator);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private boolean updateLowPowerModeLocked() {
|
||||
boolean lowPowerMode = mPowerManagerInternal
|
||||
.getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
|
||||
@@ -1268,57 +1222,36 @@ public class VibratorService extends IVibratorService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
updateVibrators();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
updateVibrators();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
updateVibrators();
|
||||
}
|
||||
|
||||
private boolean doVibratorExists() {
|
||||
// For now, we choose to ignore the presence of input devices that have vibrators
|
||||
// when reporting whether the device has a vibrator. Applications often use this
|
||||
// information to decide whether to enable certain features so they expect the
|
||||
// result of hasVibrator() to be constant. For now, just report whether
|
||||
// the device has a built-in vibrator.
|
||||
//synchronized (mInputDeviceVibrators) {
|
||||
// return !mInputDeviceVibrators.isEmpty() || vibratorExists();
|
||||
//}
|
||||
return mNativeWrapper.vibratorExists();
|
||||
}
|
||||
|
||||
private void doVibratorOn(Vibration vib) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
|
||||
try {
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
final VibrationEffect.OneShot oneShot = vib.effect.resolve(
|
||||
mDefaultVibrationAmplitude);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms"
|
||||
+ " with amplitude " + oneShot.getAmplitude() + ".");
|
||||
}
|
||||
final VibrationEffect.OneShot oneShot = vib.effect.resolve(mDefaultVibrationAmplitude);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms"
|
||||
+ " with amplitude " + oneShot.getAmplitude() + ".");
|
||||
}
|
||||
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
|
||||
vib.uid, vib.opPkg, oneShot, vib.reason, vib.attrs);
|
||||
if (inputDevicesAvailable) {
|
||||
// The set current vibration is no longer being played by this service, so drop it.
|
||||
mCurrentVibration = null;
|
||||
endVibrationLocked(vib, VibrationInfo.Status.FORWARDED_TO_INPUT_DEVICES);
|
||||
} else {
|
||||
noteVibratorOnLocked(vib.uid, oneShot.getDuration());
|
||||
final int vibratorCount = mInputDeviceVibrators.size();
|
||||
if (vibratorCount != 0) {
|
||||
for (int i = 0; i < vibratorCount; i++) {
|
||||
mInputDeviceVibrators.get(i).vibrate(vib.uid, vib.opPkg, oneShot,
|
||||
vib.reason, vib.attrs);
|
||||
}
|
||||
} else {
|
||||
// Note: ordering is important here! Many haptic drivers will reset their
|
||||
// amplitude when enabled, so we always have to enable first, then set the
|
||||
// amplitude.
|
||||
mNativeWrapper.vibratorOn(oneShot.getDuration(), vib.id);
|
||||
doVibratorSetAmplitude(oneShot.getAmplitude());
|
||||
}
|
||||
// Note: ordering is important here! Many haptic drivers will reset their
|
||||
// amplitude when enabled, so we always have to enable first, then set the
|
||||
// amplitude.
|
||||
mNativeWrapper.vibratorOn(oneShot.getDuration(), vib.id);
|
||||
doVibratorSetAmplitude(oneShot.getAmplitude());
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
@@ -1334,19 +1267,34 @@ public class VibratorService extends IVibratorService.Stub
|
||||
private void doVibratorOff() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
|
||||
try {
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Turning vibrator off.");
|
||||
}
|
||||
noteVibratorOffLocked();
|
||||
final int vibratorCount = mInputDeviceVibrators.size();
|
||||
if (vibratorCount != 0) {
|
||||
for (int i = 0; i < vibratorCount; i++) {
|
||||
mInputDeviceVibrators.get(i).cancel();
|
||||
}
|
||||
} else {
|
||||
mNativeWrapper.vibratorOff();
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Turning vibrator off.");
|
||||
}
|
||||
noteVibratorOffLocked();
|
||||
boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable();
|
||||
if (!inputDevicesAvailable) {
|
||||
mNativeWrapper.vibratorOff();
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void doVibratorWaveformEffectLocked(Vibration vib) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorWaveformEffectLocked");
|
||||
try {
|
||||
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
|
||||
vib.uid, vib.opPkg, vib.effect, vib.reason, vib.attrs);
|
||||
if (inputDevicesAvailable) {
|
||||
// The set current vibration is no longer being played by this service, so drop it.
|
||||
mCurrentVibration = null;
|
||||
endVibrationLocked(vib, VibrationInfo.Status.FORWARDED_TO_INPUT_DEVICES);
|
||||
} else {
|
||||
// mThread better be null here. doCancelVibrate should always be
|
||||
// called before startNextVibrationLocked or startVibrationLocked.
|
||||
mThread = new VibrateWaveformThread(vib);
|
||||
mThread.start();
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
@@ -1358,12 +1306,9 @@ public class VibratorService extends IVibratorService.Stub
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
|
||||
try {
|
||||
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
|
||||
final boolean usingInputDeviceVibrators;
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
|
||||
}
|
||||
// Input devices don't support prebaked effect, so skip trying it with them.
|
||||
if (!usingInputDeviceVibrators) {
|
||||
// Input devices don't support prebaked effect, so skip trying it with them and allow
|
||||
// fallback to be attempted.
|
||||
if (!mInputDeviceDelegate.isAvailable()) {
|
||||
long duration = mNativeWrapper.vibratorPerformEffect(
|
||||
prebaked.getId(), prebaked.getEffectStrength(), vib.id);
|
||||
if (duration > 0) {
|
||||
@@ -1401,15 +1346,17 @@ public class VibratorService extends IVibratorService.Stub
|
||||
|
||||
try {
|
||||
final VibrationEffect.Composed composed = (VibrationEffect.Composed) vib.effect;
|
||||
final boolean usingInputDeviceVibrators;
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
|
||||
}
|
||||
// Input devices don't support composed effect, so skip trying it with them.
|
||||
if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
|
||||
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
|
||||
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
|
||||
vib.uid, vib.opPkg, composed, vib.reason, vib.attrs);
|
||||
if (inputDevicesAvailable) {
|
||||
// The set current vibration is no longer being played by this service, so drop it.
|
||||
mCurrentVibration = null;
|
||||
endVibrationLocked(vib, VibrationInfo.Status.FORWARDED_TO_INPUT_DEVICES);
|
||||
return;
|
||||
} else if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
|
||||
// The set current vibration is not actually playing, so drop it.
|
||||
mCurrentVibration = null;
|
||||
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 com.android.server.vibrator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
/** Delegates vibrations to all connected {@link InputDevice} with available {@link Vibrator}. */
|
||||
// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
|
||||
public final class InputDeviceDelegate implements InputManager.InputDeviceListener {
|
||||
private static final String TAG = "InputDeviceDelegate";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Handler mHandler;
|
||||
private final InputManager mInputManager;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<Vibrator> mInputDeviceVibrators = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Flag updated via {@link #updateInputDeviceVibrators(boolean)}, holding the value of {@link
|
||||
* android.provider.Settings.System#VIBRATE_INPUT_DEVICES}.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
private boolean mShouldVibrateInputDevices;
|
||||
|
||||
public InputDeviceDelegate(Context context, Handler handler) {
|
||||
mHandler = handler;
|
||||
mInputManager = context.getSystemService(InputManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
updateInputDevice(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
updateInputDevice(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
mInputDeviceVibrators.remove(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} is there are input devices with vibrators available and vibrations should
|
||||
* be delegated to them.
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
synchronized (mLock) {
|
||||
// mInputDeviceVibrators is cleared when settings are disabled, so this check is enough.
|
||||
return mInputDeviceVibrators.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate all {@link InputDevice} with {@link Vibrator} available using given effect.
|
||||
*
|
||||
* @return {@link #isAvailable()}
|
||||
*/
|
||||
public boolean vibrateIfAvailable(int uid, String opPkg, VibrationEffect effect,
|
||||
String reason, VibrationAttributes attrs) {
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
|
||||
mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, effect, reason, attrs);
|
||||
}
|
||||
return mInputDeviceVibrators.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel vibration on all {@link InputDevice} with {@link Vibrator} available.
|
||||
*
|
||||
* @return {@link #isAvailable()}
|
||||
*/
|
||||
public boolean cancelVibrateIfAvailable() {
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
|
||||
mInputDeviceVibrators.valueAt(i).cancel();
|
||||
}
|
||||
return mInputDeviceVibrators.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of {@link InputDevice} vibrators based on the {@link
|
||||
* VibrationSettings#shouldVibrateInputDevices()} setting current value and the
|
||||
* devices currently available in {@link InputManager#getInputDeviceIds()}.
|
||||
*
|
||||
* @return true if there was any change in input devices available or related settings.
|
||||
*/
|
||||
public boolean updateInputDeviceVibrators(boolean vibrateInputDevices) {
|
||||
synchronized (mLock) {
|
||||
if (vibrateInputDevices == mShouldVibrateInputDevices) {
|
||||
// No need to update if settings haven't changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
mShouldVibrateInputDevices = vibrateInputDevices;
|
||||
mInputDeviceVibrators.clear();
|
||||
|
||||
if (vibrateInputDevices) {
|
||||
// Register the listener first so any device added/updated/removed after the call to
|
||||
// getInputDeviceIds() will trigger the callbacks (which will wait on the lock for
|
||||
// this loop to finish).
|
||||
mInputManager.registerInputDeviceListener(this, mHandler);
|
||||
|
||||
for (int deviceId : mInputManager.getInputDeviceIds()) {
|
||||
InputDevice device = mInputManager.getInputDevice(deviceId);
|
||||
if (device == null) {
|
||||
continue;
|
||||
}
|
||||
Vibrator vibrator = device.getVibrator();
|
||||
if (vibrator.hasVibrator()) {
|
||||
mInputDeviceVibrators.put(device.getId(), vibrator);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mInputManager.unregisterInputDeviceListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateInputDevice(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
if (!mShouldVibrateInputDevices) {
|
||||
// No need to keep this device vibrator if setting is off.
|
||||
return;
|
||||
}
|
||||
InputDevice device = mInputManager.getInputDevice(deviceId);
|
||||
if (device == null) {
|
||||
mInputDeviceVibrators.remove(deviceId);
|
||||
return;
|
||||
}
|
||||
Vibrator vibrator = device.getVibrator();
|
||||
if (vibrator.hasVibrator()) {
|
||||
mInputDeviceVibrators.put(deviceId, vibrator);
|
||||
} else {
|
||||
mInputDeviceVibrators.remove(deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.hardware.input.IInputManager;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
@@ -63,6 +65,7 @@ import android.os.Vibrator;
|
||||
import android.os.test.TestLooper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.Settings;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
@@ -120,6 +123,7 @@ public class VibratorServiceTest {
|
||||
@Mock private AppOpsManager mAppOpsManagerMock;
|
||||
@Mock private VibratorService.NativeWrapper mNativeWrapperMock;
|
||||
@Mock private IVibratorStateListener mVibratorStateListenerMock;
|
||||
@Mock private IInputManager mIInputManagerMock;
|
||||
@Mock private IBinder mVibratorStateListenerBinderMock;
|
||||
|
||||
private TestLooper mTestLooper;
|
||||
@@ -129,10 +133,12 @@ public class VibratorServiceTest {
|
||||
public void setUp() throws Exception {
|
||||
mTestLooper = new TestLooper();
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
|
||||
InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
|
||||
|
||||
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
|
||||
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
|
||||
when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
|
||||
when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
|
||||
when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
|
||||
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
|
||||
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
|
||||
@@ -145,6 +151,7 @@ public class VibratorServiceTest {
|
||||
.thenReturn(new ComponentName("", ""));
|
||||
when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
|
||||
.thenReturn(mPowerSaveStateMock);
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
|
||||
|
||||
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
|
||||
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
|
||||
@@ -216,6 +223,15 @@ public class VibratorServiceTest {
|
||||
assertFalse(createService().hasAmplitudeControl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasAmplitudeControl_withInputDevices_returnsTrue() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
|
||||
mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
|
||||
assertTrue(createService().hasAmplitudeControl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() {
|
||||
when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null);
|
||||
@@ -374,6 +390,22 @@ public class VibratorServiceTest {
|
||||
eq(AudioAttributes.USAGE_UNKNOWN), anyInt(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withOneShotAndInputDevices_vibratesInputDevices() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
|
||||
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(100, 128);
|
||||
vibrate(service, effect);
|
||||
assertFalse(service.isVibrating());
|
||||
|
||||
verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
|
||||
verify(mNativeWrapperMock, never()).vibratorOn(anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
@@ -415,6 +447,25 @@ public class VibratorServiceTest {
|
||||
eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withPrebakedAndInputDevices_vibratesFallbackWaveformOnInputDevices()
|
||||
throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
|
||||
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
|
||||
assertFalse(service.isVibrating());
|
||||
|
||||
// Wait for VibrateThread to turn input device vibrator ON.
|
||||
Thread.sleep(5);
|
||||
verify(mIInputManagerMock).vibrate(eq(1), any(), any());
|
||||
verify(mNativeWrapperMock, never()).vibratorOn(anyLong(), anyLong());
|
||||
verify(mNativeWrapperMock, never()).vibratorPerformEffect(anyLong(), anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withComposed_performsEffect() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
@@ -441,6 +492,27 @@ public class VibratorServiceTest {
|
||||
assertEquals(10, primitive.delay);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withComposedAndInputDevices_vibratesInputDevices()
|
||||
throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
|
||||
when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
|
||||
when(mIInputManagerMock.getInputDevice(2)).thenReturn(createInputDeviceWithVibrator(2));
|
||||
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
|
||||
.compose();
|
||||
vibrate(service, effect);
|
||||
assertFalse(service.isVibrating());
|
||||
|
||||
verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
|
||||
verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
|
||||
verify(mNativeWrapperMock, never()).vibratorPerformComposedEffect(any(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
|
||||
throws Exception {
|
||||
@@ -499,6 +571,25 @@ public class VibratorServiceTest {
|
||||
delay < maxDelay);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withWaveformAndInputDevices_vibratesInputDevices() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1));
|
||||
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||
new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
|
||||
vibrate(service, effect);
|
||||
assertFalse(service.isVibrating());
|
||||
|
||||
// Wait for VibrateThread to turn input device vibrator ON.
|
||||
Thread.sleep(5);
|
||||
verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
|
||||
verify(mNativeWrapperMock, never()).vibratorOn(anyLong(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
|
||||
VibratorService service = createService();
|
||||
@@ -795,6 +886,11 @@ public class VibratorServiceTest {
|
||||
when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities);
|
||||
}
|
||||
|
||||
private InputDevice createInputDeviceWithVibrator(int id) {
|
||||
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
|
||||
null, /* hasVibrator= */ true, false, false);
|
||||
}
|
||||
|
||||
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
|
||||
LocalServices.removeServiceForTest(clazz);
|
||||
LocalServices.addService(clazz, mock);
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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 com.android.server.vibrator;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.hardware.input.IInputDevicesChangedListener;
|
||||
import android.hardware.input.IInputManager;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Process;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.test.TestLooper;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
/**
|
||||
* Tests for {@link InputDeviceDelegate}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksServicesTests:InputDeviceDelegateTest
|
||||
*/
|
||||
@Presubmit
|
||||
public class InputDeviceDelegateTest {
|
||||
|
||||
private static final int UID = Process.ROOT_UID;
|
||||
private static final String PACKAGE_NAME = "package";
|
||||
private static final String REASON = "some reason";
|
||||
private static final VibrationAttributes VIBRATION_ATTRIBUTES =
|
||||
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
|
||||
|
||||
@Rule public MockitoRule rule = MockitoJUnit.rule();
|
||||
|
||||
@Mock private IInputManager mIInputManagerMock;
|
||||
|
||||
private TestLooper mTestLooper;
|
||||
private ContextWrapper mContextSpy;
|
||||
private InputDeviceDelegate mInputDeviceDelegate;
|
||||
private IInputDevicesChangedListener mIInputDevicesChangedListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mTestLooper = new TestLooper();
|
||||
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
|
||||
InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
|
||||
|
||||
when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
|
||||
doAnswer(invocation -> mIInputDevicesChangedListener = invocation.getArgument(0))
|
||||
.when(mIInputManagerMock).registerInputDevicesChangedListener(any());
|
||||
|
||||
mInputDeviceDelegate = new InputDeviceDelegate(
|
||||
mContextSpy, new Handler(mTestLooper.getLooper()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceAdded_withSettingsDisabled_ignoresNewDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
mInputDeviceDelegate.onInputDeviceAdded(1);
|
||||
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock, never()).getInputDevice(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceAdded_withDeviceWithoutVibrator_ignoresNewDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
when(mIInputManagerMock.getInputDevice(eq(1)))
|
||||
.thenReturn(createInputDeviceWithoutVibrator(1));
|
||||
updateInputDevices(new int[]{1});
|
||||
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock).getInputDevice(eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceAdded_withDeviceWithVibrator_addsNewDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]);
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
updateInputDevices(new int[]{1});
|
||||
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock).getInputDevice(eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceChanged_withSettingsDisabled_ignoresDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
|
||||
|
||||
updateInputDevices(new int[]{1});
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock, never()).getInputDevice(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceChanged_deviceLosesVibrator_removesDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1)))
|
||||
.thenReturn(createInputDeviceWithVibrator(1), createInputDeviceWithoutVibrator(1));
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
updateInputDevices(new int[]{1});
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock, times(2)).getInputDevice(eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceChanged_deviceLost_removesDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1)))
|
||||
.thenReturn(createInputDeviceWithVibrator(1), (InputDevice) null);
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
updateInputDevices(new int[]{1});
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock, times(2)).getInputDevice(eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceChanged_deviceAddsVibrator_addsDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1)))
|
||||
.thenReturn(createInputDeviceWithoutVibrator(1), createInputDeviceWithVibrator(1));
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
updateInputDevices(new int[]{1});
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
verify(mIInputManagerMock, times(2)).getInputDevice(eq(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInputDeviceRemoved_removesDevice() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(
|
||||
createInputDeviceWithoutVibrator(1));
|
||||
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
updateInputDevices(new int[]{1});
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateInputDeviceVibrators_usesFlagToLoadDeviceList() throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateInputDeviceVibrators_withDeviceWithoutVibrator_deviceIsIgnored()
|
||||
throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1)))
|
||||
.thenReturn(createInputDeviceWithoutVibrator(1));
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrateIfAvailable_withNoInputDevice_returnsFalse() {
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(100, 255);
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
assertFalse(mInputDeviceDelegate.vibrateIfAvailable(
|
||||
UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrateIfAvailable_withInputDevices_returnsTrueAndVibratesAllDevices()
|
||||
throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(100, 255);
|
||||
assertTrue(mInputDeviceDelegate.vibrateIfAvailable(
|
||||
UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES));
|
||||
verify(mIInputManagerMock).vibrate(eq(1), same(effect), any());
|
||||
verify(mIInputManagerMock).vibrate(eq(2), same(effect), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelVibrateIfAvailable_withNoInputDevice_returnsFalse() throws Exception {
|
||||
assertFalse(mInputDeviceDelegate.isAvailable());
|
||||
assertFalse(mInputDeviceDelegate.cancelVibrateIfAvailable());
|
||||
verify(mIInputManagerMock, never()).cancelVibrate(anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelVibrateIfAvailable_withInputDevices_returnsTrueAndStopsAllDevices()
|
||||
throws Exception {
|
||||
when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2});
|
||||
when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1));
|
||||
when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2));
|
||||
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
|
||||
|
||||
assertTrue(mInputDeviceDelegate.isAvailable());
|
||||
assertTrue(mInputDeviceDelegate.cancelVibrateIfAvailable());
|
||||
verify(mIInputManagerMock).cancelVibrate(eq(1), any());
|
||||
verify(mIInputManagerMock).cancelVibrate(eq(2), any());
|
||||
}
|
||||
|
||||
private void updateInputDevices(int[] deviceIds) throws Exception {
|
||||
int[] deviceIdsAndGenerations = new int[deviceIds.length * 2];
|
||||
for (int i = 0; i < deviceIdsAndGenerations.length; i += 2) {
|
||||
deviceIdsAndGenerations[i] = deviceIds[i / 2];
|
||||
deviceIdsAndGenerations[i + 1] = 2; // update by increasing it's generation to 2.
|
||||
}
|
||||
// Force initialization of mIInputDevicesChangedListener, if it still haven't
|
||||
InputManager.getInstance().getInputDeviceIds();
|
||||
mIInputDevicesChangedListener.onInputDevicesChanged(deviceIdsAndGenerations);
|
||||
// Makes sure all callbacks from InputDeviceDelegate are executed.
|
||||
mTestLooper.dispatchAll();
|
||||
}
|
||||
|
||||
private InputDevice createInputDeviceWithVibrator(int id) {
|
||||
return createInputDevice(id, /* hasVibrator= */ true);
|
||||
}
|
||||
|
||||
private InputDevice createInputDeviceWithoutVibrator(int id) {
|
||||
return createInputDevice(id, /* hasVibrator= */ false);
|
||||
}
|
||||
|
||||
private InputDevice createInputDevice(int id, boolean hasVibrator) {
|
||||
return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0,
|
||||
null, hasVibrator, false, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user