Loading services/core/java/com/android/server/VibratorService.java +71 −57 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class VibratorService extends IVibratorService.Stub implements InputManager.InputDeviceListener { Loading Loading @@ -114,6 +115,9 @@ public class VibratorService extends IVibratorService.Stub private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); // Used to generate globally unique vibration ids. private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so Loading Loading @@ -171,34 +175,34 @@ public class VibratorService extends IVibratorService.Stub private int mRingIntensity; private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); static native long vibratorInit(); static native long vibratorInit(OnCompleteListener listener); static native long vibratorGetFinalizer(); static native boolean vibratorExists(long controllerPtr); static native boolean vibratorExists(long nativeServicePtr); static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration); static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId); static native void vibratorOff(long controllerPtr); static native void vibratorOff(long nativeServicePtr); static native void vibratorSetAmplitude(long controllerPtr, int amplitude); static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude); static native int[] vibratorGetSupportedEffects(long controllerPtr); static native int[] vibratorGetSupportedEffects(long nativeServicePtr); static native int[] vibratorGetSupportedPrimitives(long controllerPtr); static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr); static native long vibratorPerformEffect( long controllerPtr, long effect, long strength, Vibration vibration); long nativeServicePtr, long effect, long strength, long vibrationId); static native void vibratorPerformComposedEffect(long controllerPtr, VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); static native void vibratorPerformComposedEffect(long nativeServicePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled); static native long vibratorGetCapabilities(long controllerPtr); static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, static native long vibratorGetCapabilities(long nativeServicePtr); static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long controllerPtr, long id); static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, Loading @@ -220,12 +224,19 @@ public class VibratorService extends IVibratorService.Stub } }; /** Listener for vibration completion callbacks from native. */ public interface OnCompleteListener { /** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */ void onComplete(long vibrationId); } /** * Holder for a vibration to be played. This class can be shared with native methods for * hardware callback support. */ @VisibleForTesting public final class Vibration implements IBinder.DeathRecipient { private final class Vibration implements IBinder.DeathRecipient { public final IBinder token; // Start time in CLOCK_BOOTTIME base. public final long startTime; Loading @@ -234,6 +245,7 @@ public class VibratorService extends IVibratorService.Stub // not to be affected by discontinuities created by RTC adjustments. public final long startTimeDebug; public final VibrationAttributes attrs; public final long id; public final int uid; public final String opPkg; public final String reason; Loading @@ -248,6 +260,7 @@ public class VibratorService extends IVibratorService.Stub VibrationAttributes attrs, int uid, String opPkg, String reason) { this.token = token; this.effect = effect; this.id = mNextVibrationId.getAndIncrement(); this.startTime = SystemClock.elapsedRealtime(); this.startTimeDebug = System.currentTimeMillis(); this.attrs = attrs; Loading @@ -268,19 +281,6 @@ public class VibratorService extends IVibratorService.Stub } } /** Callback for when vibration is complete, to be called by native. */ @VisibleForTesting public void onComplete() { synchronized (mLock) { if (this == mCurrentVibration) { if (DEBUG) { Slog.d(TAG, "Vibration finished by callback, cleaning up"); } doCancelVibrateLocked(); } } } public boolean hasTimeoutLongerThan(long millis) { final long duration = effect.getDuration(); return duration >= 0 && duration > millis; Loading Loading @@ -385,14 +385,14 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); long controllerPtr = mNativeWrapper.vibratorInit(); long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete); long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced( VibratorService.class.getClassLoader(), finalizerPtr); registry.registerNativeAllocation(this, controllerPtr); registry.registerNativeAllocation(this, nativeServicePtr); } // Reset the hardware to a default state, in case this is a runtime Loading Loading @@ -549,6 +549,19 @@ public class VibratorService extends IVibratorService.Stub } } /** Callback for when vibration is complete, to be called by native. */ @VisibleForTesting public void onVibrationComplete(long vibrationId) { synchronized (mLock) { if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { if (DEBUG) { Slog.d(TAG, "Vibration finished by callback, cleaning up"); } doCancelVibrateLocked(); } } } @Override // Binder call public boolean hasVibrator() { return doVibratorExists(); Loading Loading @@ -1266,18 +1279,18 @@ public class VibratorService extends IVibratorService.Stub return mNativeWrapper.vibratorExists(); } /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */ /** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */ private void doVibratorOn(long millis, int amplitude, Vibration vib) { doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib); doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id); } /** Vibrates without native callback. */ private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null); doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0); } private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs, @Nullable Vibration vib) { long vibrationId) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { synchronized (mInputDeviceVibrators) { Loading @@ -1299,7 +1312,7 @@ public class VibratorService extends IVibratorService.Stub // 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, vib); mNativeWrapper.vibratorOn(millis, vibrationId); doVibratorSetAmplitude(amplitude); } } Loading Loading @@ -1348,7 +1361,7 @@ public class VibratorService extends IVibratorService.Stub // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { long duration = mNativeWrapper.vibratorPerformEffect( prebaked.getId(), prebaked.getEffectStrength(), vib); prebaked.getId(), prebaked.getEffectStrength(), vib.id); if (duration > 0) { noteVibratorOnLocked(vib.uid, duration); return; Loading Loading @@ -1395,7 +1408,7 @@ public class VibratorService extends IVibratorService.Stub PrimitiveEffect[] primitiveEffects = composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib); mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id); // Composed effects don't actually give us an estimated duration, so we just guess here. noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); Loading Loading @@ -1726,20 +1739,20 @@ public class VibratorService extends IVibratorService.Stub @VisibleForTesting public static class NativeWrapper { private long mNativeControllerPtr = 0; private long mNativeServicePtr = 0; /** Checks if vibrator exists on device. */ public boolean vibratorExists() { return VibratorService.vibratorExists(mNativeControllerPtr); return VibratorService.vibratorExists(mNativeServicePtr); } /** * Returns native pointer to newly created controller and initializes connection to vibrator * HAL service. */ public long vibratorInit() { mNativeControllerPtr = VibratorService.vibratorInit(); return mNativeControllerPtr; public long vibratorInit(OnCompleteListener listener) { mNativeServicePtr = VibratorService.vibratorInit(listener); return mNativeServicePtr; } /** Returns pointer to native finalizer function to be called by GC. */ Loading @@ -1748,60 +1761,61 @@ public class VibratorService extends IVibratorService.Stub } /** Turns vibrator on for given time. */ public void vibratorOn(long milliseconds, @Nullable Vibration vibration) { VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration); public void vibratorOn(long milliseconds, long vibrationId) { VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void vibratorOff() { VibratorService.vibratorOff(mNativeControllerPtr); VibratorService.vibratorOff(mNativeServicePtr); } /** Sets the amplitude for the vibrator to run. */ public void vibratorSetAmplitude(int amplitude) { VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude); VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] vibratorGetSupportedEffects() { return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] vibratorGetSupportedPrimitives() { return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr); return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long vibratorPerformEffect(long effect, long strength, Vibration vibration) { public long vibratorPerformEffect(long effect, long strength, long vibrationId) { return VibratorService.vibratorPerformEffect( mNativeControllerPtr, effect, strength, vibration); mNativeServicePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public void vibratorPerformComposedEffect( VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration); VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect, vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void vibratorSetExternalControl(boolean enabled) { VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled); VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long vibratorGetCapabilities() { return VibratorService.vibratorGetCapabilities(mNativeControllerPtr); return VibratorService.vibratorGetCapabilities(mNativeServicePtr); } /** Enable always-on vibration with given id and effect. */ public void vibratorAlwaysOnEnable(long id, long effect, long strength) { VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength); VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void vibratorAlwaysOnDisable(long id) { VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id); VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id); } } Loading services/core/jni/com_android_server_VibratorService.cpp +121 −111 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +30 −59 Original line number Diff line number Diff line Loading @@ -20,12 +20,13 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalMatchers.gt; import static org.mockito.ArgumentMatchers.any; 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.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; Loading Loading @@ -167,7 +168,7 @@ public class VibratorServiceTest { @Test public void createService_initializesNativeService() { createService(); verify(mNativeWrapperMock).vibratorInit(); verify(mNativeWrapperMock).vibratorInit(notNull()); verify(mNativeWrapperMock).vibratorOff(); } Loading Loading @@ -294,7 +295,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); } Loading @@ -307,7 +308,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); } Loading @@ -321,10 +322,8 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); } @Test Loading @@ -343,7 +342,7 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorPerformComposedEffect( primitivesCaptor.capture(), any(VibratorService.Vibration.class)); primitivesCaptor.capture(), gt(0L)); // Check all primitive effect fields are passed down to the HAL. assertEquals(1, primitivesCaptor.getValue().length); Loading @@ -368,7 +367,7 @@ public class VibratorServiceTest { // Wait for VibrateThread to turn vibrator ON with total timing and no callback. Thread.sleep(5); verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull()); verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L)); // First amplitude set right away. verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); Loading @@ -384,11 +383,11 @@ public class VibratorServiceTest { @Test public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); Loading @@ -396,7 +395,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading @@ -404,12 +403,11 @@ public class VibratorServiceTest { public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { when(mNativeWrapperMock.vibratorGetSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(2)).onComplete(); service.onVibrationComplete(invocation.getArgument(2)); return 10_000L; // 10s }).when(mNativeWrapperMock).vibratorPerformEffect( anyLong(), anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); Loading @@ -419,7 +417,7 @@ public class VibratorServiceTest { inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(VibratorService.Vibration.class)); gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading @@ -436,44 +434,19 @@ public class VibratorServiceTest { 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).vibratorOn(eq(3L), eq(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L)); 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); VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) .compose(); vibrate(service, effect); 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_whenBinderDies_cancelsVibration() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorPerformComposedEffect( any(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() Loading @@ -484,8 +457,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( any(VibrationEffect.Composition.PrimitiveEffect[].class), any(VibratorService.Vibration.class)); any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading Loading @@ -513,12 +485,11 @@ public class VibratorServiceTest { @Test public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); service.registerVibratorStateListener(mVibratorStateListenerMock); verify(mVibratorStateListenerMock).onVibrating(false); Mockito.clearInvocations(mVibratorStateListenerMock); Loading Loading @@ -569,15 +540,15 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_TICK), eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong()); verify(mNativeWrapperMock, never()).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any()); eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong()); } @Test Loading Loading @@ -644,7 +615,7 @@ public class VibratorServiceTest { // Ringtone vibration is off, so only the other 3 are propagated to native. verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( primitivesCaptor.capture(), any()); primitivesCaptor.capture(), anyLong()); List<VibrationEffect.Composition.PrimitiveEffect[]> values = primitivesCaptor.getAllValues(); Loading Loading
services/core/java/com/android/server/VibratorService.java +71 −57 Original line number Diff line number Diff line Loading @@ -85,6 +85,7 @@ import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class VibratorService extends IVibratorService.Stub implements InputManager.InputDeviceListener { Loading Loading @@ -114,6 +115,9 @@ public class VibratorService extends IVibratorService.Stub private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); // Used to generate globally unique vibration ids. private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so Loading Loading @@ -171,34 +175,34 @@ public class VibratorService extends IVibratorService.Stub private int mRingIntensity; private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); static native long vibratorInit(); static native long vibratorInit(OnCompleteListener listener); static native long vibratorGetFinalizer(); static native boolean vibratorExists(long controllerPtr); static native boolean vibratorExists(long nativeServicePtr); static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration); static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId); static native void vibratorOff(long controllerPtr); static native void vibratorOff(long nativeServicePtr); static native void vibratorSetAmplitude(long controllerPtr, int amplitude); static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude); static native int[] vibratorGetSupportedEffects(long controllerPtr); static native int[] vibratorGetSupportedEffects(long nativeServicePtr); static native int[] vibratorGetSupportedPrimitives(long controllerPtr); static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr); static native long vibratorPerformEffect( long controllerPtr, long effect, long strength, Vibration vibration); long nativeServicePtr, long effect, long strength, long vibrationId); static native void vibratorPerformComposedEffect(long controllerPtr, VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); static native void vibratorPerformComposedEffect(long nativeServicePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled); static native long vibratorGetCapabilities(long controllerPtr); static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, static native long vibratorGetCapabilities(long nativeServicePtr); static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect, long strength); static native void vibratorAlwaysOnDisable(long controllerPtr, long id); static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, Loading @@ -220,12 +224,19 @@ public class VibratorService extends IVibratorService.Stub } }; /** Listener for vibration completion callbacks from native. */ public interface OnCompleteListener { /** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */ void onComplete(long vibrationId); } /** * Holder for a vibration to be played. This class can be shared with native methods for * hardware callback support. */ @VisibleForTesting public final class Vibration implements IBinder.DeathRecipient { private final class Vibration implements IBinder.DeathRecipient { public final IBinder token; // Start time in CLOCK_BOOTTIME base. public final long startTime; Loading @@ -234,6 +245,7 @@ public class VibratorService extends IVibratorService.Stub // not to be affected by discontinuities created by RTC adjustments. public final long startTimeDebug; public final VibrationAttributes attrs; public final long id; public final int uid; public final String opPkg; public final String reason; Loading @@ -248,6 +260,7 @@ public class VibratorService extends IVibratorService.Stub VibrationAttributes attrs, int uid, String opPkg, String reason) { this.token = token; this.effect = effect; this.id = mNextVibrationId.getAndIncrement(); this.startTime = SystemClock.elapsedRealtime(); this.startTimeDebug = System.currentTimeMillis(); this.attrs = attrs; Loading @@ -268,19 +281,6 @@ public class VibratorService extends IVibratorService.Stub } } /** Callback for when vibration is complete, to be called by native. */ @VisibleForTesting public void onComplete() { synchronized (mLock) { if (this == mCurrentVibration) { if (DEBUG) { Slog.d(TAG, "Vibration finished by callback, cleaning up"); } doCancelVibrateLocked(); } } } public boolean hasTimeoutLongerThan(long millis) { final long duration = effect.getDuration(); return duration >= 0 && duration > millis; Loading Loading @@ -385,14 +385,14 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); long controllerPtr = mNativeWrapper.vibratorInit(); long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete); long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced( VibratorService.class.getClassLoader(), finalizerPtr); registry.registerNativeAllocation(this, controllerPtr); registry.registerNativeAllocation(this, nativeServicePtr); } // Reset the hardware to a default state, in case this is a runtime Loading Loading @@ -549,6 +549,19 @@ public class VibratorService extends IVibratorService.Stub } } /** Callback for when vibration is complete, to be called by native. */ @VisibleForTesting public void onVibrationComplete(long vibrationId) { synchronized (mLock) { if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) { if (DEBUG) { Slog.d(TAG, "Vibration finished by callback, cleaning up"); } doCancelVibrateLocked(); } } } @Override // Binder call public boolean hasVibrator() { return doVibratorExists(); Loading Loading @@ -1266,18 +1279,18 @@ public class VibratorService extends IVibratorService.Stub return mNativeWrapper.vibratorExists(); } /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */ /** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */ private void doVibratorOn(long millis, int amplitude, Vibration vib) { doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib); doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id); } /** Vibrates without native callback. */ private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null); doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0); } private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs, @Nullable Vibration vib) { long vibrationId) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { synchronized (mInputDeviceVibrators) { Loading @@ -1299,7 +1312,7 @@ public class VibratorService extends IVibratorService.Stub // 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, vib); mNativeWrapper.vibratorOn(millis, vibrationId); doVibratorSetAmplitude(amplitude); } } Loading Loading @@ -1348,7 +1361,7 @@ public class VibratorService extends IVibratorService.Stub // Input devices don't support prebaked effect, so skip trying it with them. if (!usingInputDeviceVibrators) { long duration = mNativeWrapper.vibratorPerformEffect( prebaked.getId(), prebaked.getEffectStrength(), vib); prebaked.getId(), prebaked.getEffectStrength(), vib.id); if (duration > 0) { noteVibratorOnLocked(vib.uid, duration); return; Loading Loading @@ -1395,7 +1408,7 @@ public class VibratorService extends IVibratorService.Stub PrimitiveEffect[] primitiveEffects = composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib); mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id); // Composed effects don't actually give us an estimated duration, so we just guess here. noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); Loading Loading @@ -1726,20 +1739,20 @@ public class VibratorService extends IVibratorService.Stub @VisibleForTesting public static class NativeWrapper { private long mNativeControllerPtr = 0; private long mNativeServicePtr = 0; /** Checks if vibrator exists on device. */ public boolean vibratorExists() { return VibratorService.vibratorExists(mNativeControllerPtr); return VibratorService.vibratorExists(mNativeServicePtr); } /** * Returns native pointer to newly created controller and initializes connection to vibrator * HAL service. */ public long vibratorInit() { mNativeControllerPtr = VibratorService.vibratorInit(); return mNativeControllerPtr; public long vibratorInit(OnCompleteListener listener) { mNativeServicePtr = VibratorService.vibratorInit(listener); return mNativeServicePtr; } /** Returns pointer to native finalizer function to be called by GC. */ Loading @@ -1748,60 +1761,61 @@ public class VibratorService extends IVibratorService.Stub } /** Turns vibrator on for given time. */ public void vibratorOn(long milliseconds, @Nullable Vibration vibration) { VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration); public void vibratorOn(long milliseconds, long vibrationId) { VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void vibratorOff() { VibratorService.vibratorOff(mNativeControllerPtr); VibratorService.vibratorOff(mNativeServicePtr); } /** Sets the amplitude for the vibrator to run. */ public void vibratorSetAmplitude(int amplitude) { VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude); VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] vibratorGetSupportedEffects() { return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] vibratorGetSupportedPrimitives() { return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr); return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long vibratorPerformEffect(long effect, long strength, Vibration vibration) { public long vibratorPerformEffect(long effect, long strength, long vibrationId) { return VibratorService.vibratorPerformEffect( mNativeControllerPtr, effect, strength, vibration); mNativeServicePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public void vibratorPerformComposedEffect( VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration); VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect, vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void vibratorSetExternalControl(boolean enabled) { VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled); VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long vibratorGetCapabilities() { return VibratorService.vibratorGetCapabilities(mNativeControllerPtr); return VibratorService.vibratorGetCapabilities(mNativeServicePtr); } /** Enable always-on vibration with given id and effect. */ public void vibratorAlwaysOnEnable(long id, long effect, long strength) { VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength); VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void vibratorAlwaysOnDisable(long id) { VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id); VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id); } } Loading
services/core/jni/com_android_server_VibratorService.cpp +121 −111 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +30 −59 Original line number Diff line number Diff line Loading @@ -20,12 +20,13 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalMatchers.gt; import static org.mockito.ArgumentMatchers.any; 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.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; Loading Loading @@ -167,7 +168,7 @@ public class VibratorServiceTest { @Test public void createService_initializesNativeService() { createService(); verify(mNativeWrapperMock).vibratorInit(); verify(mNativeWrapperMock).vibratorInit(notNull()); verify(mNativeWrapperMock).vibratorOff(); } Loading Loading @@ -294,7 +295,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); } Loading @@ -307,7 +308,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); } Loading @@ -321,10 +322,8 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); } @Test Loading @@ -343,7 +342,7 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorOff(); verify(mNativeWrapperMock).vibratorPerformComposedEffect( primitivesCaptor.capture(), any(VibratorService.Vibration.class)); primitivesCaptor.capture(), gt(0L)); // Check all primitive effect fields are passed down to the HAL. assertEquals(1, primitivesCaptor.getValue().length); Loading @@ -368,7 +367,7 @@ public class VibratorServiceTest { // Wait for VibrateThread to turn vibrator ON with total timing and no callback. Thread.sleep(5); verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull()); verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L)); // First amplitude set right away. verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); Loading @@ -384,11 +383,11 @@ public class VibratorServiceTest { @Test public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); Loading @@ -396,7 +395,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading @@ -404,12 +403,11 @@ public class VibratorServiceTest { public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { when(mNativeWrapperMock.vibratorGetSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(2)).onComplete(); service.onVibrationComplete(invocation.getArgument(2)); return 10_000L; // 10s }).when(mNativeWrapperMock).vibratorPerformEffect( anyLong(), anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); Loading @@ -419,7 +417,7 @@ public class VibratorServiceTest { inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(VibratorService.Vibration.class)); gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading @@ -436,44 +434,19 @@ public class VibratorServiceTest { 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).vibratorOn(eq(3L), eq(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L)); 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); VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) .compose(); vibrate(service, effect); 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_whenBinderDies_cancelsVibration() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorPerformComposedEffect( any(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() Loading @@ -484,8 +457,7 @@ public class VibratorServiceTest { InOrder inOrderVerifier = inOrder(mNativeWrapperMock); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( any(VibrationEffect.Composition.PrimitiveEffect[].class), any(VibratorService.Vibration.class)); any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L)); inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } Loading Loading @@ -513,12 +485,11 @@ public class VibratorServiceTest { @Test public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { VibratorService service = createService(); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); service.onVibrationComplete(invocation.getArgument(1)); return null; }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); service.registerVibratorStateListener(mVibratorStateListenerMock); verify(mVibratorStateListenerMock).onVibrating(false); Mockito.clearInvocations(mVibratorStateListenerMock); Loading Loading @@ -569,15 +540,15 @@ public class VibratorServiceTest { verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_TICK), eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong()); verify(mNativeWrapperMock).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any()); eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong()); verify(mNativeWrapperMock, never()).vibratorPerformEffect( eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any()); eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong()); } @Test Loading Loading @@ -644,7 +615,7 @@ public class VibratorServiceTest { // Ringtone vibration is off, so only the other 3 are propagated to native. verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( primitivesCaptor.capture(), any()); primitivesCaptor.capture(), anyLong()); List<VibrationEffect.Composition.PrimitiveEffect[]> values = primitivesCaptor.getAllValues(); Loading