Merge "Fix concurrent external vibrations on ExternalVibratorService" into sc-dev

This commit is contained in:
Lais Andrade
2021-07-09 13:44:56 +00:00
committed by Android (Google) Code Review
3 changed files with 100 additions and 12 deletions

View File

@@ -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 {
// 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;
}
// 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.
//
// 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 = new ExternalVibrationHolder(vib);
mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
vib.linkToDeath(mCurrentExternalDeathRecipient);
@@ -1376,10 +1380,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
+ "external control", e);
}
}
if (DEBUG) {
Slog.d(TAG, "Vibrator going under external control.");
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);
}
setExternalControl(true);
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}

View File

@@ -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.

View File

@@ -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);
}