Merge "Use vibrator HAL controller for vibrator on and waveform"
This commit is contained in:
@@ -167,7 +167,7 @@ public class VibratorService extends IVibratorService.Stub
|
||||
private boolean mIsVibrating;
|
||||
@GuardedBy("mLock")
|
||||
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
|
||||
new RemoteCallbackList<>();
|
||||
new RemoteCallbackList<>();
|
||||
private int mHapticFeedbackIntensity;
|
||||
private int mNotificationIntensity;
|
||||
private int mRingIntensity;
|
||||
@@ -176,16 +176,25 @@ public class VibratorService extends IVibratorService.Stub
|
||||
static native long vibratorInit();
|
||||
|
||||
static native long vibratorGetFinalizer();
|
||||
|
||||
static native boolean vibratorExists(long controllerPtr);
|
||||
static native void vibratorOn(long milliseconds);
|
||||
|
||||
static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration);
|
||||
|
||||
static native void vibratorOff(long controllerPtr);
|
||||
|
||||
static native void vibratorSetAmplitude(long controllerPtr, int amplitude);
|
||||
|
||||
static native int[] vibratorGetSupportedEffects(long controllerPtr);
|
||||
|
||||
static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
|
||||
boolean withCallback);
|
||||
|
||||
static native void vibratorPerformComposedEffect(long controllerPtr,
|
||||
VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
|
||||
|
||||
static native void vibratorSetExternalControl(long controllerPtr, boolean enabled);
|
||||
|
||||
static native long vibratorGetCapabilities(long controllerPtr);
|
||||
static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect,
|
||||
long strength);
|
||||
@@ -231,8 +240,7 @@ public class VibratorService extends IVibratorService.Stub
|
||||
|
||||
// The actual effect to be played.
|
||||
public VibrationEffect effect;
|
||||
// The original effect that was requested. This is non-null only when the original effect
|
||||
// differs from the effect that's being played. Typically these two things differ because
|
||||
// The original effect that was requested. Typically these two things differ because
|
||||
// the effect was scaled based on the users vibration intensity settings.
|
||||
public VibrationEffect originalEffect;
|
||||
|
||||
@@ -248,6 +256,7 @@ public class VibratorService extends IVibratorService.Stub
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (mLock) {
|
||||
if (this == mCurrentVibration) {
|
||||
@@ -949,25 +958,22 @@ public class VibratorService extends IVibratorService.Stub
|
||||
private void startVibrationInnerLocked(Vibration vib) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
|
||||
try {
|
||||
long timeout = 0;
|
||||
mCurrentVibration = vib;
|
||||
if (vib.effect instanceof VibrationEffect.OneShot) {
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
|
||||
doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs);
|
||||
mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
|
||||
doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib);
|
||||
timeout = oneShot.getDuration() * ASYNC_TIMEOUT_MULTIPLIER;
|
||||
} else if (vib.effect instanceof VibrationEffect.Waveform) {
|
||||
// mThread better be null here. doCancelVibrate should always be
|
||||
// called before startNextVibrationLocked or startVibrationLocked.
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
|
||||
mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
|
||||
mThread.start();
|
||||
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
long timeout = doVibratorPrebakedEffectLocked(vib);
|
||||
if (timeout > 0) {
|
||||
mH.postDelayed(mVibrationEndRunnable, timeout);
|
||||
}
|
||||
timeout = doVibratorPrebakedEffectLocked(vib);
|
||||
} else if (vib.effect instanceof VibrationEffect.Composed) {
|
||||
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
|
||||
doVibratorComposedEffectLocked(vib);
|
||||
@@ -975,10 +981,16 @@ public class VibratorService extends IVibratorService.Stub
|
||||
// devices which support composition also support the completion callback. If we
|
||||
// ever get a device that supports the former but not the latter, then we have no
|
||||
// real way of knowing how long a given effect should last.
|
||||
mH.postDelayed(mVibrationEndRunnable, 10000);
|
||||
timeout = 10_000;
|
||||
} else {
|
||||
Slog.e(TAG, "Unknown vibration type, ignoring");
|
||||
}
|
||||
// Post extra runnable to ensure vibration will end even if the HAL or native controller
|
||||
// never triggers the callback.
|
||||
// TODO: Move ASYNC_TIMEOUT_MULTIPLIER here once native controller is fully integrated.
|
||||
if (timeout > 0) {
|
||||
mH.postDelayed(mVibrationEndRunnable, timeout);
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
@@ -1271,7 +1283,18 @@ public class VibratorService extends IVibratorService.Stub
|
||||
return mNativeWrapper.vibratorExists();
|
||||
}
|
||||
|
||||
/** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */
|
||||
private void doVibratorOn(long millis, int amplitude, Vibration vib) {
|
||||
doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib);
|
||||
}
|
||||
|
||||
/** Vibrates without native callback. */
|
||||
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
|
||||
doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null);
|
||||
}
|
||||
|
||||
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs,
|
||||
@Nullable Vibration vib) {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
|
||||
try {
|
||||
synchronized (mInputDeviceVibrators) {
|
||||
@@ -1286,13 +1309,14 @@ public class VibratorService extends IVibratorService.Stub
|
||||
final int vibratorCount = mInputDeviceVibrators.size();
|
||||
if (vibratorCount != 0) {
|
||||
for (int i = 0; i < vibratorCount; i++) {
|
||||
mInputDeviceVibrators.get(i).vibrate(millis, attrs.getAudioAttributes());
|
||||
Vibrator inputDeviceVibrator = mInputDeviceVibrators.get(i);
|
||||
inputDeviceVibrator.vibrate(millis, attrs.getAudioAttributes());
|
||||
}
|
||||
} 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(millis);
|
||||
mNativeWrapper.vibratorOn(millis, vib);
|
||||
doVibratorSetAmplitude(amplitude);
|
||||
}
|
||||
}
|
||||
@@ -1576,6 +1600,7 @@ public class VibratorService extends IVibratorService.Stub
|
||||
proto.flush();
|
||||
}
|
||||
|
||||
/** Thread that plays a single {@link VibrationEffect.Waveform}. */
|
||||
private class VibrateThread extends Thread {
|
||||
private final VibrationEffect.Waveform mWaveform;
|
||||
private final int mUid;
|
||||
@@ -1744,8 +1769,8 @@ public class VibratorService extends IVibratorService.Stub
|
||||
}
|
||||
|
||||
/** Turns vibrator on for given time. */
|
||||
public void vibratorOn(long milliseconds) {
|
||||
VibratorService.vibratorOn(milliseconds);
|
||||
public void vibratorOn(long milliseconds, @Nullable Vibration vibration) {
|
||||
VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration);
|
||||
}
|
||||
|
||||
/** Turns vibrator off. */
|
||||
|
||||
@@ -231,6 +231,9 @@ bool isValidEffect(jlong effect) {
|
||||
}
|
||||
|
||||
static void callVibrationOnComplete(jobject vibration) {
|
||||
if (vibration == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
|
||||
jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete);
|
||||
jniEnv->DeleteGlobalRef(vibration);
|
||||
@@ -281,18 +284,16 @@ static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controller
|
||||
return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) {
|
||||
if (auto hal = getHal<aidl::IVibrator>()) {
|
||||
auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr);
|
||||
if (!status.isOk()) {
|
||||
ALOGE("vibratorOn command failed: %s", status.toString8().string());
|
||||
}
|
||||
} else {
|
||||
Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
|
||||
if (retStatus != Status::OK) {
|
||||
ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
|
||||
}
|
||||
static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs,
|
||||
jobject vibration) {
|
||||
vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
|
||||
if (controller == nullptr) {
|
||||
ALOGE("vibratorOn failed because controller was not initialized");
|
||||
return;
|
||||
}
|
||||
jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
|
||||
auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
|
||||
controller->on(std::chrono::milliseconds(timeoutMs), callback);
|
||||
}
|
||||
|
||||
static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
|
||||
@@ -420,9 +421,8 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong
|
||||
jobject element = env->GetObjectArrayElement(composition, i);
|
||||
effects.push_back(effectFromJavaPrimitive(env, element));
|
||||
}
|
||||
auto callback = [vibrationRef(MakeGlobalRefOrDie(env, vibration))]() {
|
||||
callVibrationOnComplete(vibrationRef);
|
||||
};
|
||||
jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
|
||||
auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
|
||||
controller->performComposedEffect(effects, callback);
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ static const JNINativeMethod method_table[] = {
|
||||
{"vibratorInit", "()J", (void*)vibratorInit},
|
||||
{"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
|
||||
{"vibratorExists", "(J)Z", (void*)vibratorExists},
|
||||
{"vibratorOn", "(J)V", (void*)vibratorOn},
|
||||
{"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn},
|
||||
{"vibratorOff", "(J)V", (void*)vibratorOff},
|
||||
{"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
|
||||
{"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J",
|
||||
|
||||
@@ -26,7 +26,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.intThat;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -65,6 +67,7 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
@@ -277,7 +280,7 @@ public class VibratorServiceTest {
|
||||
assertTrue(service.isVibrating());
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(100L));
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
|
||||
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
|
||||
}
|
||||
|
||||
@@ -290,7 +293,7 @@ public class VibratorServiceTest {
|
||||
assertTrue(service.isVibrating());
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(100L));
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
|
||||
verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
|
||||
}
|
||||
|
||||
@@ -344,76 +347,157 @@ public class VibratorServiceTest {
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||
new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1);
|
||||
new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1);
|
||||
vibrate(service, effect);
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
|
||||
// Wait for VibrateThread to turn vibrator ON with total timing and no callback.
|
||||
Thread.sleep(5);
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(30L));
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull());
|
||||
|
||||
// First amplitude set right away.
|
||||
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
|
||||
|
||||
// Second amplitude set after first timing is finished.
|
||||
Thread.sleep(10);
|
||||
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
|
||||
|
||||
// Third amplitude set after second timing is finished.
|
||||
Thread.sleep(10);
|
||||
verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
|
||||
doAnswer(invocation -> {
|
||||
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
|
||||
return null;
|
||||
}).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
|
||||
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L),
|
||||
any(VibratorService.Vibration.class));
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withOneShotAndNativeCallbackNotTriggered_finishesVibrationViaFallback() {
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
// Run the scheduled callback to finish one-shot vibration.
|
||||
mTestLooper.moveTimeForward(200);
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withWaveformAndNativeCallback_callbackCannotBeTriggeredByNative()
|
||||
throws Exception {
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1);
|
||||
vibrate(service, effect);
|
||||
|
||||
// Wait for VibrateThread to finish: 1ms OFF, 3ms ON, 1ms OFF, 2ms ON.
|
||||
Thread.sleep(15);
|
||||
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull());
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull());
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
doAnswer(invocation -> {
|
||||
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
|
||||
return null;
|
||||
}).when(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
any(), any(VibratorService.Vibration.class));
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
// Use vibration with delay so there is time for the callback to be triggered.
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
|
||||
.compose();
|
||||
vibrate(service, effect);
|
||||
|
||||
// Vibration canceled once before perform and once by native callback.
|
||||
verify(mNativeWrapperMock, times(2)).vibratorOff();
|
||||
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
any(VibrationEffect.Composition.PrimitiveEffect[].class),
|
||||
any(VibratorService.Vibration.class));
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withComposedAndNativeCallbackNotTriggered_finishesVibrationViaFallback() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
|
||||
.compose();
|
||||
vibrate(service, effect);
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
verify(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
any(VibrationEffect.Composition.PrimitiveEffect[].class),
|
||||
any(VibratorService.Vibration.class));
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
// Run the scheduled callback to finish one-shot vibration.
|
||||
mTestLooper.moveTimeForward(10000); // 10s
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_whenBinderDies_cancelsVibration() {
|
||||
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
|
||||
return null;
|
||||
}).when(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
any(), any(VibratorService.Vibration.class));
|
||||
VibratorService service = createService();
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
// Use vibration with delay so there is time for the callback to be triggered.
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
|
||||
.compose();
|
||||
vibrate(service, effect);
|
||||
|
||||
// Vibration canceled once before perform and once by native binder death.
|
||||
verify(mNativeWrapperMock, times(2)).vibratorOff();
|
||||
verify(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
|
||||
any(VibrationEffect.Composition.PrimitiveEffect[].class),
|
||||
any(VibratorService.Vibration.class));
|
||||
inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelVibrate_withDeviceVibrating_callsVibratorOff() {
|
||||
VibratorService service = createService();
|
||||
vibrate(service, VibrationEffect.createOneShot(100, 128));
|
||||
vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
assertTrue(service.isVibrating());
|
||||
Mockito.clearInvocations(mNativeWrapperMock);
|
||||
|
||||
@@ -434,18 +518,20 @@ public class VibratorServiceTest {
|
||||
|
||||
@Test
|
||||
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
|
||||
doAnswer(invocation -> {
|
||||
((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
|
||||
return null;
|
||||
}).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
|
||||
VibratorService service = createService();
|
||||
|
||||
service.registerVibratorStateListener(mVibratorStateListenerMock);
|
||||
verify(mVibratorStateListenerMock).onVibrating(false);
|
||||
Mockito.clearInvocations(mVibratorStateListenerMock);
|
||||
|
||||
vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
verify(mVibratorStateListenerMock).onVibrating(true);
|
||||
|
||||
// Run the scheduled callback to finish one-shot vibration.
|
||||
mTestLooper.moveTimeForward(10);
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mVibratorStateListenerMock, times(2)).onVibrating(false);
|
||||
InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
|
||||
inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true));
|
||||
inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user