Loading .gitignore 0 → 100644 +2 −0 Original line number Diff line number Diff line .idea *.iml src/com/android/server/telecom/AsyncRingtonePlayer.java +50 −41 Original line number Diff line number Diff line Loading @@ -77,8 +77,10 @@ public class AsyncRingtonePlayer { * {@code False} indicates that a haptic track is NOT present on the ringtone; * in this case the default vibration in {@link Ringer} should be trigger if needed. */ public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isVibrationEnabled) { public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isRingerAudible, boolean isVibrationEnabled) { Log.d(this, "Posting play."); if (mHapticsFuture == null) { mHapticsFuture = new CompletableFuture<>(); Loading @@ -88,7 +90,8 @@ public class AsyncRingtonePlayer { args.arg2 = incomingCall; args.arg3 = volumeShaperConfig; args.arg4 = isVibrationEnabled; args.arg5 = Log.createSubsession(); args.arg5 = isRingerAudible; args.arg6 = Log.createSubsession(); postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args); return mHapticsFuture; } Loading Loading @@ -152,31 +155,34 @@ public class AsyncRingtonePlayer { Call incomingCall = (Call) args.arg2; VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3; boolean isVibrationEnabled = (boolean) args.arg4; Session session = (Session) args.arg5; boolean isRingerAudible = (boolean) args.arg5; Session session = (Session) args.arg6; args.recycle(); Log.continueSession(session, "ARP.hP"); try { // don't bother with any of this if there is an EVENT_STOP waiting. if (mHandler.hasMessages(EVENT_STOP)) { if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; } completeHapticFuture(false /* ringtoneHasHaptics */); return; } // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play // anything. if (Uri.EMPTY.equals(incomingCall.getRingtone())) { mRingtone = null; if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. // If ringer is not audible for this call, then the phone is in "Vibrate" mode. // Use haptic-only ringtone or do not play anything. if (!isRingerAudible || Uri.EMPTY.equals(incomingCall.getRingtone())) { if (isVibrationEnabled) { mRingtone = factory.getHapticOnlyRingtone(); if (mRingtone == null) { completeHapticFuture(false /* ringtoneHasHaptics */); return; } } else { mRingtone = null; completeHapticFuture(false /* ringtoneHasHaptics */); return; } } ThreadUtil.checkNotOnMainThread(); Log.i(this, "handlePlay: Play ringtone."); Loading @@ -189,16 +195,14 @@ public class AsyncRingtonePlayer { ringtoneUri.toSafeString(); Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " + "factory. Skipping ringing. Uri was: " + ringtoneUriString); if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; } completeHapticFuture(false /* ringtoneHasHaptics */); return; } } // With the ringtone to play now known, we can determine if it has haptic channels or // not; we will complete the haptics future so the default vibration code in Ringer // can know whether to trigger the vibrator. // not; we will complete the haptics future so the default vibration code in Ringer can // know whether to trigger the vibrator. if (mHapticsFuture != null && !mHapticsFuture.isDone()) { boolean hasHaptics = factory.hasHapticChannels(mRingtone); Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics, Loading @@ -214,9 +218,7 @@ public class AsyncRingtonePlayer { .setHapticChannelsMuted(!isVibrationEnabled) .build()); } mHapticsFuture.complete(hasHaptics); mHapticsFuture = null; } completeHapticFuture(hasHaptics); } mRingtone.setLooping(true); Loading Loading @@ -259,4 +261,11 @@ public class AsyncRingtonePlayer { public boolean isPlaying() { return mRingtone != null; } private void completeHapticFuture(boolean ringtoneHasHaptics) { if (mHapticsFuture != null) { mHapticsFuture.complete(ringtoneHasHaptics); mHapticsFuture = null; } } } src/com/android/server/telecom/Ringer.java +16 −8 Original line number Diff line number Diff line Loading @@ -282,23 +282,31 @@ public class Ringer { float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION) / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION); mVolumeShaperConfig = new VolumeShaper.Configuration.Builder() .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) .setDuration( RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) .setCurve(new float[]{0.f, silencePoint + EPSILON /*keep monotonicity*/, 1.f}, new float[]{0.f, 0.f, 1.f}) .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) .setInterpolatorType( VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) .build(); } hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, mVolumeShaperConfig, isVibratorEnabled); mVolumeShaperConfig, attributes.isRingerAudible(), isVibratorEnabled); } else { // Ramping ringtone is not enabled. hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, isVibratorEnabled); attributes.isRingerAudible(), isVibratorEnabled); effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall); } } else { Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + attributes.getInaudibleReason()); if (isVibratorEnabled && mIsHapticPlaybackSupportedByDevice) { // Attempt to run the attentional haptic ringtone first and fallback to the default // vibration effect if hapticFuture is completed with false. hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, attributes.isRingerAudible(), isVibratorEnabled); } effect = mDefaultVibrationEffect; } Loading src/com/android/server/telecom/RingtoneFactory.java +21 −4 Original line number Diff line number Diff line Loading @@ -103,6 +103,27 @@ public class RingtoneFactory { Log.e(this, npe, "getRingtone: NPE while getting ringtone."); } } return setRingtoneAudioAttributes(ringtone); } public Ringtone getRingtone(Call incomingCall) { return getRingtone(incomingCall, null); } /** Returns a ringtone to be used when ringer is not audible for the incoming call. */ @Nullable public Ringtone getHapticOnlyRingtone() { Uri ringtoneUri = Uri.parse("file://" + mContext.getString( com.android.internal.R.string.config_defaultRingtoneVibrationSound)); Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null); if (ringtone != null) { // Make sure the sound is muted. ringtone.setVolume(0); } return setRingtoneAudioAttributes(ringtone); } private Ringtone setRingtoneAudioAttributes(Ringtone ringtone) { if (ringtone != null) { ringtone.setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) Loading @@ -112,10 +133,6 @@ public class RingtoneFactory { return ringtone; } public Ringtone getRingtone(Call incomingCall) { return getRingtone(incomingCall, null); } private Context getWorkProfileContextForUser(UserHandle userHandle) { // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle // itself (so we must filter out the user). Loading tests/src/com/android/server/telecom/tests/RingerTest.java +46 −20 Original line number Diff line number Diff line Loading @@ -149,7 +149,8 @@ public class RingerTest extends TelecomTestCase { when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true); when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false); when(mockRingtonePlayer.play(any(RingtoneFactory.class), any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())).thenReturn(mFuture); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean())) .thenReturn(mFuture); mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil, mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy, mockInCallController); Loading @@ -174,7 +175,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(false)); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -192,7 +193,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -208,7 +209,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -225,7 +226,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -241,7 +242,7 @@ public class RingerTest extends TelecomTestCase { assertTrue(mRingerUnderTest.startRinging(mockCall2, true)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -260,7 +261,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -276,8 +277,8 @@ public class RingerTest extends TelecomTestCase { assertTrue(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play( any(RingtoneFactory.class), any(Call.class), isNull(), eq(true)); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -293,8 +294,10 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Play default vibration when future completes with no audio coupled haptics verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect), any(AudioAttributes.class)); } Loading @@ -305,19 +308,42 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startCallWaiting(mockCall1); Ringtone mockRingtone = mock(Ringtone.class); when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); mFuture.complete(false); // not using audio coupled haptics enableVibrationWhenRinging(); assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Play default vibration when future completes with no audio coupled haptics verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect), any(AudioAttributes.class)); } @SmallTest @Test public void testAudioCoupledHapticsForSilentRingtone() throws Exception { mRingerUnderTest.startCallWaiting(mockCall1); Ringtone mockRingtone = mock(Ringtone.class); when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); mFuture.complete(true); // using audio coupled haptics enableVibrationWhenRinging(); assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Skip vibration for audio coupled haptics verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } @SmallTest @Test public void testStopRingingBeforeHapticsLookupComplete() throws Exception { Loading @@ -329,7 +355,7 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startRinging(mockCall1, false); // Make sure we haven't started the vibrator yet, but have started ringing. verify(mockRingtonePlayer).play(nullable(RingtoneFactory.class), nullable(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()).vibrate(nullable(VibrationEffect.class), nullable(AudioAttributes.class)); // Simulate something stopping the ringer Loading Loading @@ -357,7 +383,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(true)); eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)), any(AudioAttributes.class)); } Loading @@ -373,7 +399,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(false)); eq(true) /* isRingerAudible */, eq(false) /* isVibrationEnabled */); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -391,7 +417,7 @@ public class RingerTest extends TelecomTestCase { verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play( any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), eq(true)); eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); } @SmallTest Loading @@ -408,7 +434,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -426,7 +452,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading Loading
src/com/android/server/telecom/AsyncRingtonePlayer.java +50 −41 Original line number Diff line number Diff line Loading @@ -77,8 +77,10 @@ public class AsyncRingtonePlayer { * {@code False} indicates that a haptic track is NOT present on the ringtone; * in this case the default vibration in {@link Ringer} should be trigger if needed. */ public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isVibrationEnabled) { public @NonNull CompletableFuture<Boolean> play(RingtoneFactory factory, Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean isRingerAudible, boolean isVibrationEnabled) { Log.d(this, "Posting play."); if (mHapticsFuture == null) { mHapticsFuture = new CompletableFuture<>(); Loading @@ -88,7 +90,8 @@ public class AsyncRingtonePlayer { args.arg2 = incomingCall; args.arg3 = volumeShaperConfig; args.arg4 = isVibrationEnabled; args.arg5 = Log.createSubsession(); args.arg5 = isRingerAudible; args.arg6 = Log.createSubsession(); postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args); return mHapticsFuture; } Loading Loading @@ -152,31 +155,34 @@ public class AsyncRingtonePlayer { Call incomingCall = (Call) args.arg2; VolumeShaper.Configuration volumeShaperConfig = (VolumeShaper.Configuration) args.arg3; boolean isVibrationEnabled = (boolean) args.arg4; Session session = (Session) args.arg5; boolean isRingerAudible = (boolean) args.arg5; Session session = (Session) args.arg6; args.recycle(); Log.continueSession(session, "ARP.hP"); try { // don't bother with any of this if there is an EVENT_STOP waiting. if (mHandler.hasMessages(EVENT_STOP)) { if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; } completeHapticFuture(false /* ringtoneHasHaptics */); return; } // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. Do not play // anything. if (Uri.EMPTY.equals(incomingCall.getRingtone())) { mRingtone = null; if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; // If the Ringtone Uri is EMPTY, then the "None" Ringtone has been selected. // If ringer is not audible for this call, then the phone is in "Vibrate" mode. // Use haptic-only ringtone or do not play anything. if (!isRingerAudible || Uri.EMPTY.equals(incomingCall.getRingtone())) { if (isVibrationEnabled) { mRingtone = factory.getHapticOnlyRingtone(); if (mRingtone == null) { completeHapticFuture(false /* ringtoneHasHaptics */); return; } } else { mRingtone = null; completeHapticFuture(false /* ringtoneHasHaptics */); return; } } ThreadUtil.checkNotOnMainThread(); Log.i(this, "handlePlay: Play ringtone."); Loading @@ -189,16 +195,14 @@ public class AsyncRingtonePlayer { ringtoneUri.toSafeString(); Log.addEvent(null, LogUtils.Events.ERROR_LOG, "Failed to get ringtone from " + "factory. Skipping ringing. Uri was: " + ringtoneUriString); if (mHapticsFuture != null) { mHapticsFuture.complete(false /* ringtoneHasHaptics */); mHapticsFuture = null; } completeHapticFuture(false /* ringtoneHasHaptics */); return; } } // With the ringtone to play now known, we can determine if it has haptic channels or // not; we will complete the haptics future so the default vibration code in Ringer // can know whether to trigger the vibrator. // not; we will complete the haptics future so the default vibration code in Ringer can // know whether to trigger the vibrator. if (mHapticsFuture != null && !mHapticsFuture.isDone()) { boolean hasHaptics = factory.hasHapticChannels(mRingtone); Log.i(this, "handlePlay: hasHaptics=%b, isVibrationEnabled=%b", hasHaptics, Loading @@ -214,9 +218,7 @@ public class AsyncRingtonePlayer { .setHapticChannelsMuted(!isVibrationEnabled) .build()); } mHapticsFuture.complete(hasHaptics); mHapticsFuture = null; } completeHapticFuture(hasHaptics); } mRingtone.setLooping(true); Loading Loading @@ -259,4 +261,11 @@ public class AsyncRingtonePlayer { public boolean isPlaying() { return mRingtone != null; } private void completeHapticFuture(boolean ringtoneHasHaptics) { if (mHapticsFuture != null) { mHapticsFuture.complete(ringtoneHasHaptics); mHapticsFuture = null; } } }
src/com/android/server/telecom/Ringer.java +16 −8 Original line number Diff line number Diff line Loading @@ -282,23 +282,31 @@ public class Ringer { float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION) / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION); mVolumeShaperConfig = new VolumeShaper.Configuration.Builder() .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) .setDuration( RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) .setCurve(new float[]{0.f, silencePoint + EPSILON /*keep monotonicity*/, 1.f}, new float[]{0.f, 0.f, 1.f}) .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) .setInterpolatorType( VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) .build(); } hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, mVolumeShaperConfig, isVibratorEnabled); mVolumeShaperConfig, attributes.isRingerAudible(), isVibratorEnabled); } else { // Ramping ringtone is not enabled. hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, isVibratorEnabled); attributes.isRingerAudible(), isVibratorEnabled); effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall); } } else { Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + attributes.getInaudibleReason()); if (isVibratorEnabled && mIsHapticPlaybackSupportedByDevice) { // Attempt to run the attentional haptic ringtone first and fallback to the default // vibration effect if hapticFuture is completed with false. hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, attributes.isRingerAudible(), isVibratorEnabled); } effect = mDefaultVibrationEffect; } Loading
src/com/android/server/telecom/RingtoneFactory.java +21 −4 Original line number Diff line number Diff line Loading @@ -103,6 +103,27 @@ public class RingtoneFactory { Log.e(this, npe, "getRingtone: NPE while getting ringtone."); } } return setRingtoneAudioAttributes(ringtone); } public Ringtone getRingtone(Call incomingCall) { return getRingtone(incomingCall, null); } /** Returns a ringtone to be used when ringer is not audible for the incoming call. */ @Nullable public Ringtone getHapticOnlyRingtone() { Uri ringtoneUri = Uri.parse("file://" + mContext.getString( com.android.internal.R.string.config_defaultRingtoneVibrationSound)); Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri, null); if (ringtone != null) { // Make sure the sound is muted. ringtone.setVolume(0); } return setRingtoneAudioAttributes(ringtone); } private Ringtone setRingtoneAudioAttributes(Ringtone ringtone) { if (ringtone != null) { ringtone.setAudioAttributes(new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) Loading @@ -112,10 +133,6 @@ public class RingtoneFactory { return ringtone; } public Ringtone getRingtone(Call incomingCall) { return getRingtone(incomingCall, null); } private Context getWorkProfileContextForUser(UserHandle userHandle) { // UserManager.getEnabledProfiles returns the enabled profiles along with the user's handle // itself (so we must filter out the user). Loading
tests/src/com/android/server/telecom/tests/RingerTest.java +46 −20 Original line number Diff line number Diff line Loading @@ -149,7 +149,8 @@ public class RingerTest extends TelecomTestCase { when(notificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true); when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false); when(mockRingtonePlayer.play(any(RingtoneFactory.class), any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())).thenReturn(mFuture); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean())) .thenReturn(mFuture); mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil, mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy, mockInCallController); Loading @@ -174,7 +175,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(false)); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -192,7 +193,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -208,7 +209,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -225,7 +226,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -241,7 +242,7 @@ public class RingerTest extends TelecomTestCase { assertTrue(mRingerUnderTest.startRinging(mockCall2, true)); verify(mockTonePlayer, never()).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -260,7 +261,7 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -276,8 +277,8 @@ public class RingerTest extends TelecomTestCase { assertTrue(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play( any(RingtoneFactory.class), any(Call.class), isNull(), eq(true)); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -293,8 +294,10 @@ public class RingerTest extends TelecomTestCase { assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Play default vibration when future completes with no audio coupled haptics verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect), any(AudioAttributes.class)); } Loading @@ -305,19 +308,42 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startCallWaiting(mockCall1); Ringtone mockRingtone = mock(Ringtone.class); when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); mFuture.complete(false); // not using audio coupled haptics enableVibrationWhenRinging(); assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Play default vibration when future completes with no audio coupled haptics verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect), any(AudioAttributes.class)); } @SmallTest @Test public void testAudioCoupledHapticsForSilentRingtone() throws Exception { mRingerUnderTest.startCallWaiting(mockCall1); Ringtone mockRingtone = mock(Ringtone.class); when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone); when(mockAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); mFuture.complete(true); // using audio coupled haptics enableVibrationWhenRinging(); assertFalse(mRingerUnderTest.startRinging(mockCall2, false)); mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), isNull(), eq(false) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); // Skip vibration for audio coupled haptics verify(mockVibrator, never()).vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } @SmallTest @Test public void testStopRingingBeforeHapticsLookupComplete() throws Exception { Loading @@ -329,7 +355,7 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startRinging(mockCall1, false); // Make sure we haven't started the vibrator yet, but have started ringing. verify(mockRingtonePlayer).play(nullable(RingtoneFactory.class), nullable(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()).vibrate(nullable(VibrationEffect.class), nullable(AudioAttributes.class)); // Simulate something stopping the ringer Loading Loading @@ -357,7 +383,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(true)); eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)), any(AudioAttributes.class)); } Loading @@ -373,7 +399,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class), eq(null), eq(false)); eq(true) /* isRingerAudible */, eq(false) /* isVibrationEnabled */); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -391,7 +417,7 @@ public class RingerTest extends TelecomTestCase { verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer).play( any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), eq(true)); eq(true) /* isRingerAudible */, eq(true) /* isVibrationEnabled */); } @SmallTest Loading @@ -408,7 +434,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading @@ -426,7 +452,7 @@ public class RingerTest extends TelecomTestCase { mRingCompletionFuture.get(); verify(mockTonePlayer).stopTone(); verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class), any(VolumeShaper.Configuration.class), anyBoolean()); nullable(VolumeShaper.Configuration.class), anyBoolean(), anyBoolean()); verify(mockVibrator, never()) .vibrate(any(VibrationEffect.class), any(AudioAttributes.class)); } Loading