Loading src/com/android/server/telecom/AsyncRingtonePlayer.java +19 −10 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import android.os.HandlerThread; import android.os.Message; import android.telecom.Log; import android.telecom.Logging.Session; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; Loading Loading @@ -81,16 +83,17 @@ public class AsyncRingtonePlayer { * If {@link VolumeShaper.Configuration} is specified, it is applied to the ringtone to change * the volume of the ringtone as it plays. * * @param ringtoneSupplier The {@link Ringtone} factory. * @param ringtoneInfoSupplier The {@link Ringtone} factory. * @param ringtoneConsumer The {@link Ringtone} post-creation callback (to start the vibration). * @param isHfpDeviceConnected True if there is a HFP BT device connected, false otherwise. */ public void play(@NonNull Supplier<Ringtone> ringtoneSupplier, BiConsumer<Ringtone, Boolean> ringtoneConsumer, boolean isHfpDeviceConnected) { public void play(@NonNull Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier, BiConsumer<Pair<Uri, Ringtone>, Boolean> ringtoneConsumer, boolean isHfpDeviceConnected) { Log.d(this, "Posting play."); mIsPlaying = true; SomeArgs args = SomeArgs.obtain(); args.arg1 = ringtoneSupplier; args.arg1 = ringtoneInfoSupplier; args.arg2 = ringtoneConsumer; args.arg3 = Log.createSubsession(); args.arg4 = prepareRingingReadyLatch(isHfpDeviceConnected); Loading Loading @@ -209,8 +212,10 @@ public class AsyncRingtonePlayer { * Starts the actual playback of the ringtone. Executes on ringtone-thread. */ private void handlePlay(SomeArgs args) { Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1; BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2; Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier = (Supplier<Pair<Uri, Ringtone>>) args.arg1; BiConsumer<Pair<Uri, Ringtone>, Boolean> ringtoneConsumer = (BiConsumer<Pair<Uri, Ringtone>, Boolean>) args.arg2; Session session = (Session) args.arg3; CountDownLatch ringingReadyLatch = (CountDownLatch) args.arg4; args.recycle(); Loading @@ -226,6 +231,7 @@ public class AsyncRingtonePlayer { return; } Ringtone ringtone = null; Uri ringtoneUri = null; boolean hasStopped = false; try { try { Loading @@ -236,7 +242,11 @@ public class AsyncRingtonePlayer { } catch (InterruptedException e) { Log.w(this, "handlePlay: latch exception: " + e); } ringtone = ringtoneSupplier.get(); if (ringtoneInfoSupplier != null && ringtoneInfoSupplier.get() != null) { ringtoneUri = ringtoneInfoSupplier.get().first; ringtone = ringtoneInfoSupplier.get().second; } // Ringtone supply can be slow or stop command could have been issued while waiting // for BT to move to CONNECTED state. Re-check for stop event. if (mHandler.hasMessages(EVENT_STOP)) { Loading @@ -253,8 +263,7 @@ public class AsyncRingtonePlayer { Log.w(this, "No ringtone was found bail out from playing."); return; } Uri uri = mRingtone.getUri(); String uriString = (uri != null ? uri.toSafeString() : ""); String uriString = ringtoneUri != null ? ringtoneUri.toSafeString() : ""; Log.i(this, "handlePlay: Play ringtone. Uri: " + uriString); mRingtone.setLooping(true); if (mRingtone.isPlaying()) { Loading @@ -265,7 +274,7 @@ public class AsyncRingtonePlayer { Log.i(this, "Play ringtone, looping."); } finally { removePendingRingingReadyLatch(ringingReadyLatch); ringtoneConsumer.accept(ringtone, hasStopped); ringtoneConsumer.accept(new Pair(ringtoneUri, ringtone), hasStopped); } } finally { Log.cancelSubsession(session); Loading src/com/android/server/telecom/Ringer.java +21 −12 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.os.vibrator.persistence.ParsedVibration; import android.os.vibrator.persistence.VibrationXmlParser; import android.telecom.Log; import android.telecom.TelecomManager; import android.util.Pair; import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -413,18 +414,18 @@ public class Ringer { isVibratorEnabled, mIsHapticPlaybackSupportedByDevice); } // Defer ringtone creation to the async player thread. Supplier<Ringtone> ringtoneSupplier; Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier; final boolean finalHapticChannelsMuted = hapticChannelsMuted; if (isHapticOnly) { if (hapticChannelsMuted) { Log.i(this, "want haptic only ringtone but haptics are muted, skip ringtone play"); ringtoneSupplier = null; ringtoneInfoSupplier = null; } else { ringtoneSupplier = mRingtoneFactory::getHapticOnlyRingtone; ringtoneInfoSupplier = mRingtoneFactory::getHapticOnlyRingtone; } } else { ringtoneSupplier = () -> mRingtoneFactory.getRingtone( ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted); } Loading @@ -447,9 +448,18 @@ public class Ringer { // if the loaded ringtone is null. However if a stop event arrives before the ringtone // creation finishes, then this consumer can be skipped. final boolean finalUseCustomVibrationEffect = useCustomVibrationEffect; BiConsumer<Ringtone, Boolean> afterRingtoneLogic = (Ringtone ringtone, Boolean stopped) -> { BiConsumer<Pair<Uri, Ringtone>, Boolean> afterRingtoneLogic = (Pair<Uri, Ringtone> ringtoneInfo, Boolean stopped) -> { try { Uri ringtoneUri = null; Ringtone ringtone = null; if (ringtoneInfo != null) { ringtoneUri = ringtoneInfo.first; ringtone = ringtoneInfo.second; } else { Log.w(this, "The ringtone could not be loaded."); } if (stopped.booleanValue() || !vibratorReserved) { // don't start vibration if the ringing is already abandoned, or the // vibrator wasn't reserved. This still triggers the mBlockOnRingingFuture. Loading @@ -460,7 +470,7 @@ public class Ringer { if (DEBUG_RINGER) { Log.d(this, "Using ringtone defined vibration effect."); } vibrationEffect = getVibrationEffectForRingtone(ringtone); vibrationEffect = getVibrationEffectForRingtone(ringtoneUri); } else { vibrationEffect = mDefaultVibrationEffect; } Loading @@ -477,10 +487,10 @@ public class Ringer { } }; deferBlockOnRingingFuture = true; // Run in vibrationLogic. if (ringtoneSupplier != null) { mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached); if (ringtoneInfoSupplier != null) { mRingtonePlayer.play(ringtoneInfoSupplier, afterRingtoneLogic, isHfpDeviceAttached); } else { afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false); afterRingtoneLogic.accept(/* ringtoneUri, ringtone = */ null, /* stopped= */ false); } // shouldAcquireAudioFocus is meant to be true, but that check is deferred to here Loading Loading @@ -542,8 +552,7 @@ public class Ringer { } } private VibrationEffect getVibrationEffectForRingtone(@NonNull Ringtone ringtone) { Uri ringtoneUri = ringtone.getUri(); private VibrationEffect getVibrationEffectForRingtone(Uri ringtoneUri) { if (ringtoneUri == null) { return mDefaultVibrationEffect; } Loading src/com/android/server/telecom/RingtoneFactory.java +9 −18 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import android.telecom.CallerInfo; import android.util.Pair; import java.util.List; Loading @@ -53,18 +54,7 @@ public class RingtoneFactory { mCallsManager = callsManager; } /** * Determines if a ringtone has haptic channels. * @param ringtone The ringtone URI. * @return {@code true} if there is a haptic channel, {@code false} otherwise. */ public boolean hasHapticChannels(Ringtone ringtone) { boolean hasHapticChannels = RingtoneManager.hasHapticChannels(ringtone.getUri()); Log.i(this, "hasHapticChannels %s -> %b", ringtone.getUri(), hasHapticChannels); return hasHapticChannels; } public Ringtone getRingtone(Call incomingCall, public Pair<Uri, Ringtone> getRingtone(Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted) { // Initializing ringtones on the main thread can deadlock ThreadUtil.checkNotOnMainThread(); Loading Loading @@ -106,18 +96,19 @@ public class RingtoneFactory { } } if (defaultRingtoneUri == null) { ringtoneUri = defaultRingtoneUri; if (ringtoneUri == null) { return null; } try { ringtone = RingtoneManager.getRingtone( contextToUse, defaultRingtoneUri, volumeShaperConfig, audioAttrs); contextToUse, ringtoneUri, volumeShaperConfig, audioAttrs); } catch (Exception e) { Log.e(this, e, "getRingtone: exception while getting ringtone."); } } return ringtone; return new Pair(ringtoneUri, ringtone); } private AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) { Loading @@ -130,7 +121,7 @@ public class RingtoneFactory { /** Returns a ringtone to be used when ringer is not audible for the incoming call. */ @Nullable public Ringtone getHapticOnlyRingtone() { public Pair<Uri, Ringtone> getHapticOnlyRingtone() { // Initializing ringtones on the main thread can deadlock ThreadUtil.checkNotOnMainThread(); Uri ringtoneUri = Uri.parse("file://" + mContext.getString( Loading @@ -143,7 +134,7 @@ public class RingtoneFactory { // Make sure the sound is muted. ringtone.setVolume(0); } return ringtone; return new Pair(ringtoneUri, ringtone); } private Context getWorkProfileContextForUser(UserHandle userHandle) { Loading tests/src/com/android/server/telecom/tests/RingerTest.java +16 −13 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Pair; import androidx.test.filters.SmallTest; Loading @@ -83,6 +84,7 @@ import org.mockito.Spy; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; @RunWith(JUnit4.class) public class RingerTest extends TelecomTestCase { Loading Loading @@ -144,7 +146,6 @@ public class RingerTest extends TelecomTestCase { mockNotificationManager =mContext.getSystemService(NotificationManager.class); when(mockTonePlayer.startTone()).thenReturn(true); when(mockNotificationManager.matchesCallFilter(any(Uri.class))).thenReturn(true); when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false); when(mockCall1.getState()).thenReturn(CallState.RINGING); when(mockCall2.getState()).thenReturn(CallState.RINGING); when(mockCall1.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle()); Loading Loading @@ -426,7 +427,7 @@ public class RingerTest extends TelecomTestCase { // Pretend we're using audio coupled haptics. setIsUsingHaptics(mockRingtone, true); assertTrue(startRingingAndWaitForAsync(mockCall1, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading Loading @@ -468,14 +469,14 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startCallWaiting(mockCall1); when(mockRingtoneFactory.getRingtone(any(Call.class), eq(null), anyBoolean())) .thenReturn(mockRingtone); .thenReturn(new Pair(FAKE_RINGTONE_URI, mockRingtone)); when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone(); verify(mockRingtoneFactory, atLeastOnce()).getHapticOnlyRingtone(); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockRingtone).play(); Loading Loading @@ -514,7 +515,7 @@ public class RingerTest extends TelecomTestCase { enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone(); verify(mockRingtoneFactory, atLeastOnce()).getHapticOnlyRingtone(); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone Loading @@ -534,7 +535,7 @@ public class RingerTest extends TelecomTestCase { enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockTonePlayer).stopTone(); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), isNull(), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockRingtone).play(); Loading @@ -551,7 +552,7 @@ public class RingerTest extends TelecomTestCase { ensureRingerIsAudible(); enableVibrationOnlyWhenNotRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading @@ -570,7 +571,7 @@ public class RingerTest extends TelecomTestCase { enableRampingRinger(); enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading Loading @@ -602,7 +603,7 @@ public class RingerTest extends TelecomTestCase { when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100); enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, true)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), isNull(), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading @@ -623,7 +624,7 @@ public class RingerTest extends TelecomTestCase { asyncRingtonePlayer.updateBtActiveState(true); mRingCompletionFuture.get(); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); Loading Loading @@ -753,7 +754,7 @@ public class RingerTest extends TelecomTestCase { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return mockRingtone; return new Pair(FAKE_RINGTONE_URI, mockRingtone); }); // Start call waiting to make sure that it doesn't stop when we start ringing enableVibrationWhenRinging(); Loading Loading @@ -832,10 +833,12 @@ public class RingerTest extends TelecomTestCase { private Ringtone ensureRingtoneMocked() { Ringtone mockRingtone = mock(Ringtone.class); Pair<Uri, Ringtone> ringtoneInfo = new Pair( FAKE_RINGTONE_URI, mockRingtone); when(mockRingtoneFactory.getRingtone( any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())) .thenReturn(mockRingtone); when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(mockRingtone); .thenReturn(ringtoneInfo); when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(ringtoneInfo); return mockRingtone; } Loading Loading
src/com/android/server/telecom/AsyncRingtonePlayer.java +19 −10 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import android.os.HandlerThread; import android.os.Message; import android.telecom.Log; import android.telecom.Logging.Session; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; Loading Loading @@ -81,16 +83,17 @@ public class AsyncRingtonePlayer { * If {@link VolumeShaper.Configuration} is specified, it is applied to the ringtone to change * the volume of the ringtone as it plays. * * @param ringtoneSupplier The {@link Ringtone} factory. * @param ringtoneInfoSupplier The {@link Ringtone} factory. * @param ringtoneConsumer The {@link Ringtone} post-creation callback (to start the vibration). * @param isHfpDeviceConnected True if there is a HFP BT device connected, false otherwise. */ public void play(@NonNull Supplier<Ringtone> ringtoneSupplier, BiConsumer<Ringtone, Boolean> ringtoneConsumer, boolean isHfpDeviceConnected) { public void play(@NonNull Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier, BiConsumer<Pair<Uri, Ringtone>, Boolean> ringtoneConsumer, boolean isHfpDeviceConnected) { Log.d(this, "Posting play."); mIsPlaying = true; SomeArgs args = SomeArgs.obtain(); args.arg1 = ringtoneSupplier; args.arg1 = ringtoneInfoSupplier; args.arg2 = ringtoneConsumer; args.arg3 = Log.createSubsession(); args.arg4 = prepareRingingReadyLatch(isHfpDeviceConnected); Loading Loading @@ -209,8 +212,10 @@ public class AsyncRingtonePlayer { * Starts the actual playback of the ringtone. Executes on ringtone-thread. */ private void handlePlay(SomeArgs args) { Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1; BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2; Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier = (Supplier<Pair<Uri, Ringtone>>) args.arg1; BiConsumer<Pair<Uri, Ringtone>, Boolean> ringtoneConsumer = (BiConsumer<Pair<Uri, Ringtone>, Boolean>) args.arg2; Session session = (Session) args.arg3; CountDownLatch ringingReadyLatch = (CountDownLatch) args.arg4; args.recycle(); Loading @@ -226,6 +231,7 @@ public class AsyncRingtonePlayer { return; } Ringtone ringtone = null; Uri ringtoneUri = null; boolean hasStopped = false; try { try { Loading @@ -236,7 +242,11 @@ public class AsyncRingtonePlayer { } catch (InterruptedException e) { Log.w(this, "handlePlay: latch exception: " + e); } ringtone = ringtoneSupplier.get(); if (ringtoneInfoSupplier != null && ringtoneInfoSupplier.get() != null) { ringtoneUri = ringtoneInfoSupplier.get().first; ringtone = ringtoneInfoSupplier.get().second; } // Ringtone supply can be slow or stop command could have been issued while waiting // for BT to move to CONNECTED state. Re-check for stop event. if (mHandler.hasMessages(EVENT_STOP)) { Loading @@ -253,8 +263,7 @@ public class AsyncRingtonePlayer { Log.w(this, "No ringtone was found bail out from playing."); return; } Uri uri = mRingtone.getUri(); String uriString = (uri != null ? uri.toSafeString() : ""); String uriString = ringtoneUri != null ? ringtoneUri.toSafeString() : ""; Log.i(this, "handlePlay: Play ringtone. Uri: " + uriString); mRingtone.setLooping(true); if (mRingtone.isPlaying()) { Loading @@ -265,7 +274,7 @@ public class AsyncRingtonePlayer { Log.i(this, "Play ringtone, looping."); } finally { removePendingRingingReadyLatch(ringingReadyLatch); ringtoneConsumer.accept(ringtone, hasStopped); ringtoneConsumer.accept(new Pair(ringtoneUri, ringtone), hasStopped); } } finally { Log.cancelSubsession(session); Loading
src/com/android/server/telecom/Ringer.java +21 −12 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.os.vibrator.persistence.ParsedVibration; import android.os.vibrator.persistence.VibrationXmlParser; import android.telecom.Log; import android.telecom.TelecomManager; import android.util.Pair; import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -413,18 +414,18 @@ public class Ringer { isVibratorEnabled, mIsHapticPlaybackSupportedByDevice); } // Defer ringtone creation to the async player thread. Supplier<Ringtone> ringtoneSupplier; Supplier<Pair<Uri, Ringtone>> ringtoneInfoSupplier; final boolean finalHapticChannelsMuted = hapticChannelsMuted; if (isHapticOnly) { if (hapticChannelsMuted) { Log.i(this, "want haptic only ringtone but haptics are muted, skip ringtone play"); ringtoneSupplier = null; ringtoneInfoSupplier = null; } else { ringtoneSupplier = mRingtoneFactory::getHapticOnlyRingtone; ringtoneInfoSupplier = mRingtoneFactory::getHapticOnlyRingtone; } } else { ringtoneSupplier = () -> mRingtoneFactory.getRingtone( ringtoneInfoSupplier = () -> mRingtoneFactory.getRingtone( foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted); } Loading @@ -447,9 +448,18 @@ public class Ringer { // if the loaded ringtone is null. However if a stop event arrives before the ringtone // creation finishes, then this consumer can be skipped. final boolean finalUseCustomVibrationEffect = useCustomVibrationEffect; BiConsumer<Ringtone, Boolean> afterRingtoneLogic = (Ringtone ringtone, Boolean stopped) -> { BiConsumer<Pair<Uri, Ringtone>, Boolean> afterRingtoneLogic = (Pair<Uri, Ringtone> ringtoneInfo, Boolean stopped) -> { try { Uri ringtoneUri = null; Ringtone ringtone = null; if (ringtoneInfo != null) { ringtoneUri = ringtoneInfo.first; ringtone = ringtoneInfo.second; } else { Log.w(this, "The ringtone could not be loaded."); } if (stopped.booleanValue() || !vibratorReserved) { // don't start vibration if the ringing is already abandoned, or the // vibrator wasn't reserved. This still triggers the mBlockOnRingingFuture. Loading @@ -460,7 +470,7 @@ public class Ringer { if (DEBUG_RINGER) { Log.d(this, "Using ringtone defined vibration effect."); } vibrationEffect = getVibrationEffectForRingtone(ringtone); vibrationEffect = getVibrationEffectForRingtone(ringtoneUri); } else { vibrationEffect = mDefaultVibrationEffect; } Loading @@ -477,10 +487,10 @@ public class Ringer { } }; deferBlockOnRingingFuture = true; // Run in vibrationLogic. if (ringtoneSupplier != null) { mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached); if (ringtoneInfoSupplier != null) { mRingtonePlayer.play(ringtoneInfoSupplier, afterRingtoneLogic, isHfpDeviceAttached); } else { afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false); afterRingtoneLogic.accept(/* ringtoneUri, ringtone = */ null, /* stopped= */ false); } // shouldAcquireAudioFocus is meant to be true, but that check is deferred to here Loading Loading @@ -542,8 +552,7 @@ public class Ringer { } } private VibrationEffect getVibrationEffectForRingtone(@NonNull Ringtone ringtone) { Uri ringtoneUri = ringtone.getUri(); private VibrationEffect getVibrationEffectForRingtone(Uri ringtoneUri) { if (ringtoneUri == null) { return mDefaultVibrationEffect; } Loading
src/com/android/server/telecom/RingtoneFactory.java +9 −18 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import android.telecom.CallerInfo; import android.util.Pair; import java.util.List; Loading @@ -53,18 +54,7 @@ public class RingtoneFactory { mCallsManager = callsManager; } /** * Determines if a ringtone has haptic channels. * @param ringtone The ringtone URI. * @return {@code true} if there is a haptic channel, {@code false} otherwise. */ public boolean hasHapticChannels(Ringtone ringtone) { boolean hasHapticChannels = RingtoneManager.hasHapticChannels(ringtone.getUri()); Log.i(this, "hasHapticChannels %s -> %b", ringtone.getUri(), hasHapticChannels); return hasHapticChannels; } public Ringtone getRingtone(Call incomingCall, public Pair<Uri, Ringtone> getRingtone(Call incomingCall, @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted) { // Initializing ringtones on the main thread can deadlock ThreadUtil.checkNotOnMainThread(); Loading Loading @@ -106,18 +96,19 @@ public class RingtoneFactory { } } if (defaultRingtoneUri == null) { ringtoneUri = defaultRingtoneUri; if (ringtoneUri == null) { return null; } try { ringtone = RingtoneManager.getRingtone( contextToUse, defaultRingtoneUri, volumeShaperConfig, audioAttrs); contextToUse, ringtoneUri, volumeShaperConfig, audioAttrs); } catch (Exception e) { Log.e(this, e, "getRingtone: exception while getting ringtone."); } } return ringtone; return new Pair(ringtoneUri, ringtone); } private AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) { Loading @@ -130,7 +121,7 @@ public class RingtoneFactory { /** Returns a ringtone to be used when ringer is not audible for the incoming call. */ @Nullable public Ringtone getHapticOnlyRingtone() { public Pair<Uri, Ringtone> getHapticOnlyRingtone() { // Initializing ringtones on the main thread can deadlock ThreadUtil.checkNotOnMainThread(); Uri ringtoneUri = Uri.parse("file://" + mContext.getString( Loading @@ -143,7 +134,7 @@ public class RingtoneFactory { // Make sure the sound is muted. ringtone.setVolume(0); } return ringtone; return new Pair(ringtoneUri, ringtone); } private Context getWorkProfileContextForUser(UserHandle userHandle) { Loading
tests/src/com/android/server/telecom/tests/RingerTest.java +16 −13 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.util.Pair; import androidx.test.filters.SmallTest; Loading @@ -83,6 +84,7 @@ import org.mockito.Spy; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; @RunWith(JUnit4.class) public class RingerTest extends TelecomTestCase { Loading Loading @@ -144,7 +146,6 @@ public class RingerTest extends TelecomTestCase { mockNotificationManager =mContext.getSystemService(NotificationManager.class); when(mockTonePlayer.startTone()).thenReturn(true); when(mockNotificationManager.matchesCallFilter(any(Uri.class))).thenReturn(true); when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false); when(mockCall1.getState()).thenReturn(CallState.RINGING); when(mockCall2.getState()).thenReturn(CallState.RINGING); when(mockCall1.getAssociatedUser()).thenReturn(PA_HANDLE.getUserHandle()); Loading Loading @@ -426,7 +427,7 @@ public class RingerTest extends TelecomTestCase { // Pretend we're using audio coupled haptics. setIsUsingHaptics(mockRingtone, true); assertTrue(startRingingAndWaitForAsync(mockCall1, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading Loading @@ -468,14 +469,14 @@ public class RingerTest extends TelecomTestCase { mRingerUnderTest.startCallWaiting(mockCall1); when(mockRingtoneFactory.getRingtone(any(Call.class), eq(null), anyBoolean())) .thenReturn(mockRingtone); .thenReturn(new Pair(FAKE_RINGTONE_URI, mockRingtone)); when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0); enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone(); verify(mockRingtoneFactory, atLeastOnce()).getHapticOnlyRingtone(); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockRingtone).play(); Loading Loading @@ -514,7 +515,7 @@ public class RingerTest extends TelecomTestCase { enableVibrationWhenRinging(); assertFalse(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone(); verify(mockRingtoneFactory, atLeastOnce()).getHapticOnlyRingtone(); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); // Try to play a silent haptics ringtone Loading @@ -534,7 +535,7 @@ public class RingerTest extends TelecomTestCase { enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockTonePlayer).stopTone(); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), isNull(), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockRingtone).play(); Loading @@ -551,7 +552,7 @@ public class RingerTest extends TelecomTestCase { ensureRingerIsAudible(); enableVibrationOnlyWhenNotRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading @@ -570,7 +571,7 @@ public class RingerTest extends TelecomTestCase { enableRampingRinger(); enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, false)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading Loading @@ -602,7 +603,7 @@ public class RingerTest extends TelecomTestCase { when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100); enableVibrationWhenRinging(); assertTrue(startRingingAndWaitForAsync(mockCall2, true)); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), isNull(), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); verify(mockTonePlayer).stopTone(); Loading @@ -623,7 +624,7 @@ public class RingerTest extends TelecomTestCase { asyncRingtonePlayer.updateBtActiveState(true); mRingCompletionFuture.get(); verify(mockRingtoneFactory, times(1)) verify(mockRingtoneFactory, atLeastOnce()) .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()); verifyNoMoreInteractions(mockRingtoneFactory); Loading Loading @@ -753,7 +754,7 @@ public class RingerTest extends TelecomTestCase { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return mockRingtone; return new Pair(FAKE_RINGTONE_URI, mockRingtone); }); // Start call waiting to make sure that it doesn't stop when we start ringing enableVibrationWhenRinging(); Loading Loading @@ -832,10 +833,12 @@ public class RingerTest extends TelecomTestCase { private Ringtone ensureRingtoneMocked() { Ringtone mockRingtone = mock(Ringtone.class); Pair<Uri, Ringtone> ringtoneInfo = new Pair( FAKE_RINGTONE_URI, mockRingtone); when(mockRingtoneFactory.getRingtone( any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean())) .thenReturn(mockRingtone); when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(mockRingtone); .thenReturn(ringtoneInfo); when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(ringtoneInfo); return mockRingtone; } Loading