Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb8f25c0 authored by Lais Andrade's avatar Lais Andrade
Browse files

Add support for vibration in session playback

Add support to VibrationStepConductor to play vibrations without delays,
sending all HAL requests in order and skipping all calls to off().

The vibrator should handle the vibration playback and queueing once it's
in a vibrator manager session.

Bug: 345414356
Test: FrameworksVibratorServicesTests
Flag: android.os.vibrator.vendor_vibration_effects
Change-Id: Id6ae0699060594edb74ee86ef3476c19cc173d3b
parent 3829f33b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -112,6 +112,14 @@ abstract class AbstractVibratorStep extends Step {
    }

    protected void stopVibrating() {
        if (conductor.isInSession) {
            if (VibrationThread.DEBUG) {
                Slog.d(VibrationThread.TAG,
                        "Vibration in session, skipping request to turn off vibrator "
                                + getVibratorId());
            }
            return;
        }
        if (VibrationThread.DEBUG) {
            Slog.d(VibrationThread.TAG,
                    "Turning off vibrator " + getVibratorId());
+8 −3
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ final class VibrationStepConductor {
    // Used within steps.
    public final VibrationSettings vibrationSettings;
    public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
    public final boolean isInSession;

    private final DeviceAdapter mDeviceAdapter;
    private final VibrationScaler mVibrationScaler;
@@ -105,12 +106,13 @@ final class VibrationStepConductor {
    private int mRemainingStartSequentialEffectSteps;
    private int mSuccessfulVibratorOnSteps;

    VibrationStepConductor(HalVibration vib, VibrationSettings vibrationSettings,
            DeviceAdapter deviceAdapter, VibrationScaler vibrationScaler,
            VibratorFrameworkStatsLogger statsLogger,
    VibrationStepConductor(HalVibration vib, boolean isInSession,
            VibrationSettings vibrationSettings, DeviceAdapter deviceAdapter,
            VibrationScaler vibrationScaler, VibratorFrameworkStatsLogger statsLogger,
            CompletableFuture<Void> requestVibrationParamsFuture,
            VibrationThread.VibratorManagerHooks vibratorManagerHooks) {
        this.mVibration = vib;
        this.isInSession = isInSession;
        this.vibrationSettings = vibrationSettings;
        this.mDeviceAdapter = deviceAdapter;
        mVibrationScaler = vibrationScaler;
@@ -286,6 +288,9 @@ final class VibrationStepConductor {
        if (nextStep == null) {
            return true;  // Finished
        }
        if (isInSession) {
            return true;  // Don't wait to play session vibration steps
        }
        long waitMillis = nextStep.calculateWaitTime();
        if (waitMillis <= 0) {
            return true;  // Regular step ready
+3 −2
Original line number Diff line number Diff line
@@ -1114,8 +1114,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                            mVibrationSettings.getRequestVibrationParamsTimeoutMs());
        }

        return new VibrationStepConductor(vib, mVibrationSettings, mDeviceAdapter, mVibrationScaler,
                mFrameworkStatsLogger, requestVibrationParamsFuture, mVibrationThreadCallbacks);
        return new VibrationStepConductor(vib, /* isInSession= */ false, mVibrationSettings,
                mDeviceAdapter, mVibrationScaler, mFrameworkStatsLogger,
                requestVibrationParamsFuture, mVibrationThreadCallbacks);
    }

    private Status startVibrationOnInputDevicesLocked(HalVibration vib) {
+62 −5
Original line number Diff line number Diff line
@@ -1913,6 +1913,55 @@ public class VibrationThreadTest {
                fakeVibrator.getEffectSegments(vibration5.id));
    }

    @Test
    public void vibrate_multipleVibratorsSequentialInSession_runsInOrderWithoutDelaysAndNoOffs() {
        mockVibrators(1, 2, 3);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
        mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
        mVibratorProviders.get(2).setSupportedPrimitives(
                VibrationEffect.Composition.PRIMITIVE_CLICK);
        mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);

        CombinedVibration effect = CombinedVibration.startSequential()
                .addNext(3,
                        VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
                        /* delay= */ TEST_TIMEOUT_MILLIS)
                .addNext(1,
                        VibrationEffect.createWaveform(
                                new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS},
                                /* repeat= */ -1),
                        /* delay= */ TEST_TIMEOUT_MILLIS)
                .addNext(2,
                        VibrationEffect.startComposition()
                                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1,
                                        /* delay= */ TEST_TIMEOUT_MILLIS)
                                .compose(),
                        /* delay= */ TEST_TIMEOUT_MILLIS)
                .combine();
        HalVibration vibration = startThreadAndDispatcher(effect, /* isInSession= */ true);

        // Should not timeout as delays will not affect in session playback time.
        waitForCompletion();

        // Vibrating state remains ON until session resets it.
        verifyCallbacksTriggered(vibration, Status.FINISHED);
        assertTrue(mControllers.get(1).isVibrating());
        assertTrue(mControllers.get(2).isVibrating());
        assertTrue(mControllers.get(3).isVibrating());

        assertEquals(0, mVibratorProviders.get(1).getOffCount());
        assertEquals(0, mVibratorProviders.get(2).getOffCount());
        assertEquals(0, mVibratorProviders.get(3).getOffCount());
        assertEquals(Arrays.asList(expectedOneShot(TEST_TIMEOUT_MILLIS)),
                mVibratorProviders.get(1).getEffectSegments(vibration.id));
        assertEquals(expectedAmplitudes(255), mVibratorProviders.get(1).getAmplitudes());
        assertEquals(Arrays.asList(expectedPrimitive(
                VibrationEffect.Composition.PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)),
                mVibratorProviders.get(2).getEffectSegments(vibration.id));
        assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                mVibratorProviders.get(3).getEffectSegments(vibration.id));
    }

    private void mockVibrators(int... vibratorIds) {
        for (int vibratorId : vibratorIds) {
            mVibratorProviders.put(vibratorId,
@@ -1935,8 +1984,14 @@ public class VibrationThreadTest {
        return startThreadAndDispatcher(createVibration(effect));
    }

    private HalVibration startThreadAndDispatcher(CombinedVibration effect, boolean isInSession) {
        return startThreadAndDispatcher(createVibration(effect), isInSession,
                /* requestVibrationParamsFuture= */ null);
    }

    private HalVibration startThreadAndDispatcher(HalVibration vib) {
        return startThreadAndDispatcher(vib, /* requestVibrationParamsFuture= */ null);
        return startThreadAndDispatcher(vib, /* isInSession= */ false,
                /* requestVibrationParamsFuture= */ null);
    }

    private HalVibration startThreadAndDispatcher(VibrationEffect effect,
@@ -1947,15 +2002,17 @@ public class VibrationThreadTest {
        HalVibration vib = new HalVibration(
                new CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"),
                CombinedVibration.createParallel(effect));
        return startThreadAndDispatcher(vib, requestVibrationParamsFuture);
        return startThreadAndDispatcher(vib, /* isInSession= */ false,
                requestVibrationParamsFuture);
    }

    private HalVibration startThreadAndDispatcher(HalVibration vib,
    private HalVibration startThreadAndDispatcher(HalVibration vib, boolean isInSession,
            CompletableFuture<Void> requestVibrationParamsFuture) {
        mControllers = createVibratorControllers();
        DeviceAdapter deviceAdapter = new DeviceAdapter(mVibrationSettings, mControllers);
        mVibrationConductor = new VibrationStepConductor(vib, mVibrationSettings, deviceAdapter,
                mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture, mManagerHooks);
        mVibrationConductor = new VibrationStepConductor(vib, isInSession, mVibrationSettings,
                deviceAdapter, mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture,
                mManagerHooks);
        assertTrue(mThread.runVibrationOnVibrationThread(mVibrationConductor));
        return mVibrationConductor.getVibration();
    }