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

Commit 64db2da3 authored by Lais Andrade's avatar Lais Andrade Committed by Automerger Merge Worker
Browse files

Merge "Fix concurrent external vibrations on ExternalVibratorService" into...

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

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

Change-Id: Ifc4083e34a5f0816c4c7540532f930109c6de2ca
parents c542c51c 6ccbe780
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);
    }