Loading src/com/android/server/telecom/Ringer.java +18 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.content.res.Resources; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.Ringtone; import android.media.Utils; import android.media.VolumeShaper; import android.media.audio.Flags; import android.net.Uri; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -188,6 +190,7 @@ public class Ringer { private final VibrationEffectProxy mVibrationEffectProxy; private final boolean mIsHapticPlaybackSupportedByDevice; private final FeatureFlags mFlags; private final boolean mRingtoneVibrationSupported; /** * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}. Loading Loading @@ -259,6 +262,8 @@ public class Ringer { mAudioManager = mContext.getSystemService(AudioManager.class); mFlags = featureFlags; mRingtoneVibrationSupported = mContext.getResources().getBoolean( com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported); } @VisibleForTesting Loading Loading @@ -420,6 +425,9 @@ public class Ringer { if (!isHapticOnly) { ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted); } else if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported) { ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, null, false); } // If vibration will be done, reserve the vibrator. Loading Loading @@ -471,7 +479,8 @@ public class Ringer { boolean isUsingAudioCoupledHaptics = !finalHapticChannelsMuted && ringtone != null && ringtone.hasHapticChannels(); vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect); vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect, ringtoneUri); } finally { // This is used to signal to tests that the async play() call has completed. if (mBlockOnRingingFuture != null) { Loading Loading @@ -523,13 +532,20 @@ public class Ringer { } private void vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, Call foregroundCall, VibrationEffect effect) { VibrationEffect effect, Uri ringtoneUri) { if (isUsingAudioCoupledHaptics) { Log.addEvent( foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using audio-coupled haptics"); return; } if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported && Utils.hasVibration(ringtoneUri)) { Log.addEvent( foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using custom haptics"); return; } synchronized (mLock) { // Ensure the reservation is live. The mIsVibrating check should be redundant. if (foregroundCall == mVibratingCall && !mIsVibrating) { Loading tests/src/com/android/server/telecom/tests/RingerTest.java +41 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.media.Ringtone; import android.media.VolumeShaper; import android.media.audio.Flags; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; Loading @@ -55,8 +56,10 @@ import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Pair; Loading Loading @@ -91,7 +94,14 @@ public class RingerTest extends TelecomTestCase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final Uri FAKE_RINGTONE_URI = Uri.parse("content://media/fake/audio/1729"); private static final Uri FAKE_VIBRATION_URI = Uri.parse("file://media/fake/vibration/1729"); private static final String VIBRATION_PARAM = "vibration_uri"; // Returned when the a URI-based VibrationEffect is attempted, to avoid depending on actual // device configuration for ringtone URIs. The actual Uri can be verified via the // VibrationEffectProxy mock invocation. Loading Loading @@ -805,6 +815,37 @@ public class RingerTest extends TelecomTestCase { .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); } @SmallTest @Test @EnableFlags(Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION) public void testNoVibrateForSilentRingtoneIfRingtoneHasVibration() throws Exception { Uri FAKE_RINGTONE_VIBRATION_URI = FAKE_RINGTONE_URI.buildUpon().appendQueryParameter( VIBRATION_PARAM, FAKE_VIBRATION_URI.toString()).build(); Ringtone mockRingtone = mock(Ringtone.class); Pair<Uri, Ringtone> ringtoneInfo = new Pair(FAKE_RINGTONE_VIBRATION_URI, mockRingtone); when(mockRingtoneFactory.getRingtone( any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())) .thenReturn(ringtoneInfo); mComponentContextFixture.putBooleanResource( com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported, true); createRingerUnderTest(); // Needed after mock the config. mRingerUnderTest.startCallWaiting(mockCall1); when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), eq(null), eq(false)); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); // Skip vibration play in Ringer if a vibration was specified to the ringtone verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); } /** * Call startRinging and wait for its effects to have played out, to allow reliable assertions * after it. The effects are generally "start playing ringtone" and "start vibration" - not Loading Loading
src/com/android/server/telecom/Ringer.java +18 −2 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.content.res.Resources; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.Ringtone; import android.media.Utils; import android.media.VolumeShaper; import android.media.audio.Flags; import android.net.Uri; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -188,6 +190,7 @@ public class Ringer { private final VibrationEffectProxy mVibrationEffectProxy; private final boolean mIsHapticPlaybackSupportedByDevice; private final FeatureFlags mFlags; private final boolean mRingtoneVibrationSupported; /** * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}. Loading Loading @@ -259,6 +262,8 @@ public class Ringer { mAudioManager = mContext.getSystemService(AudioManager.class); mFlags = featureFlags; mRingtoneVibrationSupported = mContext.getResources().getBoolean( com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported); } @VisibleForTesting Loading Loading @@ -420,6 +425,9 @@ public class Ringer { if (!isHapticOnly) { ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted); } else if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported) { ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, null, false); } // If vibration will be done, reserve the vibrator. Loading Loading @@ -471,7 +479,8 @@ public class Ringer { boolean isUsingAudioCoupledHaptics = !finalHapticChannelsMuted && ringtone != null && ringtone.hasHapticChannels(); vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect); vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect, ringtoneUri); } finally { // This is used to signal to tests that the async play() call has completed. if (mBlockOnRingingFuture != null) { Loading Loading @@ -523,13 +532,20 @@ public class Ringer { } private void vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, Call foregroundCall, VibrationEffect effect) { VibrationEffect effect, Uri ringtoneUri) { if (isUsingAudioCoupledHaptics) { Log.addEvent( foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using audio-coupled haptics"); return; } if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported && Utils.hasVibration(ringtoneUri)) { Log.addEvent( foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using custom haptics"); return; } synchronized (mLock) { // Ensure the reservation is live. The mIsVibrating check should be redundant. if (foregroundCall == mVibratingCall && !mIsVibrating) { Loading
tests/src/com/android/server/telecom/tests/RingerTest.java +41 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.media.AudioAttributes; import android.media.AudioManager; import android.media.Ringtone; import android.media.VolumeShaper; import android.media.audio.Flags; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; Loading @@ -55,8 +56,10 @@ import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorInfo; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Pair; Loading Loading @@ -91,7 +94,14 @@ public class RingerTest extends TelecomTestCase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final Uri FAKE_RINGTONE_URI = Uri.parse("content://media/fake/audio/1729"); private static final Uri FAKE_VIBRATION_URI = Uri.parse("file://media/fake/vibration/1729"); private static final String VIBRATION_PARAM = "vibration_uri"; // Returned when the a URI-based VibrationEffect is attempted, to avoid depending on actual // device configuration for ringtone URIs. The actual Uri can be verified via the // VibrationEffectProxy mock invocation. Loading Loading @@ -805,6 +815,37 @@ public class RingerTest extends TelecomTestCase { .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); } @SmallTest @Test @EnableFlags(Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION) public void testNoVibrateForSilentRingtoneIfRingtoneHasVibration() throws Exception { Uri FAKE_RINGTONE_VIBRATION_URI = FAKE_RINGTONE_URI.buildUpon().appendQueryParameter( VIBRATION_PARAM, FAKE_VIBRATION_URI.toString()).build(); Ringtone mockRingtone = mock(Ringtone.class); Pair<Uri, Ringtone> ringtoneInfo = new Pair(FAKE_RINGTONE_VIBRATION_URI, mockRingtone); when(mockRingtoneFactory.getRingtone( any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())) .thenReturn(ringtoneInfo); mComponentContextFixture.putBooleanResource( com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported, true); createRingerUnderTest(); // Needed after mock the config. mRingerUnderTest.startCallWaiting(mockCall1); when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), eq(null), eq(false)); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); // Skip vibration play in Ringer if a vibration was specified to the ringtone verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(VibrationAttributes.class)); } /** * Call startRinging and wait for its effects to have played out, to allow reliable assertions * after it. The effects are generally "start playing ringtone" and "start vibration" - not Loading