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

Commit 6ccbe780 authored by Lais Andrade's avatar Lais Andrade Committed by Automerger Merge Worker
Browse files

Merge "Fix concurrent external vibrations on ExternalVibratorService" into sc-dev am: 1dd6f20e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15243095

Change-Id: Ide89db80b5d8b965a7107f9d1208c9a1dc8a787f
parents 454d31be 1dd6f20e
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -1301,7 +1301,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    }

    /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
    private final class ExternalVibratorService extends IExternalVibratorService.Stub {
    @VisibleForTesting
    final class ExternalVibratorService extends IExternalVibratorService.Stub {
        ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;

        @Override
@@ -1332,6 +1333,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                return vibHolder.scale;
            }

            ExternalVibrationHolder cancelingExternalVibration = null;
            VibrationThread cancelingVibration = null;
            int scale;
            synchronized (mLock) {
@@ -1350,16 +1352,18 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                        cancelingVibration = mCurrentVibration;
                    }
                } else {
                    endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
                }
                // At this point we either have an externally controlled vibration playing, or
                // no vibration playing. Since the interface defines that only one externally
                // controlled vibration can play at a time, by returning something other than
                // SCALE_MUTE from this function we can be assured that if we are currently
                // playing vibration, it will be muted in favor of the new vibration.
                    // At this point we have an externally controlled vibration playing already.
                    // Since the interface defines that only one externally controlled vibration can
                    // play at a time, we need to first mute the ongoing vibration and then return
                    // a scale from this function for the new one. Ee can be assured that the
                    // ongoing it will be muted in favor of the new vibration.
                    //
                    // Note that this doesn't support multiple concurrent external controls, as we
                    // would need to mute the old one still if it came from a different controller.
                    mCurrentExternalVibration.externalVibration.mute();
                    endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
                    cancelingExternalVibration = mCurrentExternalVibration;
                }
                mCurrentExternalVibration = new ExternalVibrationHolder(vib);
                mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
                vib.linkToDeath(mCurrentExternalDeathRecipient);
@@ -1376,10 +1380,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                            + "external control", e);
                }
            }
            if (cancelingExternalVibration == null) {
                // We only need to set external control if it was not already set by another
                // external vibration.
                if (DEBUG) {
                    Slog.d(TAG, "Vibrator going under external control.");
                }
                setExternalControl(true);
            }
            if (DEBUG) {
                Slog.e(TAG, "Playing external vibration: " + vib);
            }
+7 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ final class FakeVibratorControllerProvider {
    private final List<VibrationEffectSegment> mEffectSegments = new ArrayList<>();
    private final List<Integer> mBraking = new ArrayList<>();
    private final List<Float> mAmplitudes = new ArrayList<>();
    private final List<Boolean> mExternalControlStates = new ArrayList<>();
    private final Handler mHandler;
    private final FakeNativeWrapper mNativeWrapper;

@@ -139,6 +140,7 @@ final class FakeVibratorControllerProvider {

        @Override
        public void setExternalControl(boolean enabled) {
            mExternalControlStates.add(enabled);
        }

        @Override
@@ -301,6 +303,11 @@ final class FakeVibratorControllerProvider {
        return new ArrayList<>(mEffectSegments);
    }

    /** Return list of states set for external control to the fake vibrator hardware. */
    public List<Boolean> getExternalControlStates() {
        return mExternalControlStates;
    }

    /**
     * Return the {@link PrebakedSegment} effect enabled with given id, or {@code null} if
     * missing or disabled.
+73 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ 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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import android.app.AppOpsManager;
@@ -50,8 +51,11 @@ import android.hardware.vibrator.IVibratorManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibrationController;
import android.os.IExternalVibratorService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
@@ -108,6 +112,8 @@ public class VibratorManagerServiceTest {
    private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
    private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
            .setBatterySaverEnabled(true).build();
    private static final AudioAttributes AUDIO_ATTRS =
            new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build();
    private static final VibrationAttributes ALARM_ATTRS =
            new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
    private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
@@ -136,6 +142,7 @@ public class VibratorManagerServiceTest {
    private TestLooper mTestLooper;
    private FakeVibrator mVibrator;
    private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
    private VibratorManagerService.ExternalVibratorService mExternalVibratorService;

    @Before
    public void setUp() throws Exception {
@@ -208,6 +215,9 @@ public class VibratorManagerServiceTest {

                    @Override
                    void addService(String name, IBinder service) {
                        Object serviceInstance = service;
                        mExternalVibratorService =
                                (VibratorManagerService.ExternalVibratorService) serviceInstance;
                    }
                });
    }
@@ -967,6 +977,69 @@ public class VibratorManagerServiceTest {
        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
    }

    @Test
    public void onExternalVibration_setsExternalControl() {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
        createSystemReadyService();

        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                mock(IExternalVibrationController.class));
        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
        mExternalVibratorService.onExternalVibrationStop(externalVibration);

        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
        assertEquals(Arrays.asList(true, false),
                mVibratorProviders.get(1).getExternalControlStates());
    }

    @Test
    public void onExternalVibration_withOngoingExternalVibration_mutesPreviousVibration()
            throws Exception {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
        createSystemReadyService();

        IExternalVibrationController firstController = mock(IExternalVibrationController.class);
        IExternalVibrationController secondController = mock(IExternalVibrationController.class);
        ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                firstController);
        int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);

        ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                secondController);
        int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);

        assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
        assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
        verify(firstController).mute();
        verifyNoMoreInteractions(secondController);
        // Set external control called only once.
        assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
    }

    @Test
    public void onExternalVibration_withOngoingVibration_cancelsOngoingVibrationImmediately()
            throws Exception {
        mockVibrators(1);
        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
                IVibrator.CAP_AMPLITUDE_CONTROL);
        VibratorManagerService service = createSystemReadyService();

        VibrationEffect effect = VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100);
        vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));

        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                mock(IExternalVibrationController.class));
        int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
        assertEquals(IExternalVibratorService.SCALE_NONE, scale);

        // Vibration is cancelled.
        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
        assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
    }

    private VibrationEffectSegment expectedPrebaked(int effectId) {
        return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
    }