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

Commit 2d6380b9 authored by Brad Ebinger's avatar Brad Ebinger Committed by Automerger Merge Worker
Browse files

Wait for SCO connected signal to ring when BT is connected am: c8f6d3f2

parents b04b48f2 c8f6d3f2
Loading
Loading
Loading
Loading
+105 −4
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

@@ -39,6 +42,9 @@ import java.util.function.Supplier;
 */
@VisibleForTesting
public class AsyncRingtonePlayer {
    // Maximum amount of time we will delay playing a ringtone while waiting for audio routing to
    // be ready.
    private static final int PLAY_DELAY_TIMEOUT_MS = 1000;
    // Message codes used with the ringtone thread.
    private static final int EVENT_PLAY = 1;
    private static final int EVENT_STOP = 2;
@@ -49,6 +55,23 @@ public class AsyncRingtonePlayer {
    /** The current ringtone. Only used by the ringtone thread. */
    private Ringtone mRingtone;

    /**
     * Set to true if we are setting up to play or are currently playing. False if we are stopping
     * or have stopped playing.
     */
    private boolean mIsPlaying = false;

    /**
     * Set to true if BT HFP is active and audio is connected.
     */
    private boolean mIsBtActive = false;

    /**
     * A list of pending ringing ready latches, which are used to delay the ringing command until
     * audio paths are set and ringing is ready.
     */
    private final ArrayList<CountDownLatch> mPendingRingingLatches = new ArrayList<>();

    public AsyncRingtonePlayer() {
        // Empty
    }
@@ -60,21 +83,81 @@ public class AsyncRingtonePlayer {
     *
     * @param ringtoneSupplier 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) {
            BiConsumer<Ringtone, Boolean> ringtoneConsumer,  boolean isHfpDeviceConnected) {
        Log.d(this, "Posting play.");
        mIsPlaying = true;
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = ringtoneSupplier;
        args.arg2 = ringtoneConsumer;
        args.arg3 = Log.createSubsession();
        args.arg4 = prepareRingingReadyLatch(isHfpDeviceConnected);
        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
    }

    /** Stops playing the ringtone. */
    public void stop() {
        Log.d(this, "Posting stop.");
        mIsPlaying = false;
        postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
        // Clear any pending ringing latches so that we do not have to wait for its timeout to pass
        // before calling stop.
        clearPendingRingingLatches();
    }

    /**
     * Called when the BT HFP profile active state changes.
     * @param isBtActive A BT device is connected and audio is active.
     */
    public void updateBtActiveState(boolean isBtActive) {
        Log.i(this, "updateBtActiveState: " + isBtActive);
        synchronized (mPendingRingingLatches) {
            mIsBtActive = isBtActive;
            if (isBtActive) mPendingRingingLatches.forEach(CountDownLatch::countDown);
        }
    }

    /**
     * Prepares a new ringing ready latch and tracks it in a list. Once the ready latch has been
     * used, {@link #removePendingRingingReadyLatch(CountDownLatch)} must be called on this latch.
     * @param isHfpDeviceConnected true if there is a HFP device connected.
     * @return the newly prepared CountDownLatch
     */
    private CountDownLatch prepareRingingReadyLatch(boolean isHfpDeviceConnected) {
        CountDownLatch latch = new CountDownLatch(1);
        synchronized (mPendingRingingLatches) {
            // We only want to delay ringing if BT is connected but not active yet.
            boolean isDelayRequired = isHfpDeviceConnected && !mIsBtActive;
            Log.i(this, "prepareRingingReadyLatch:"
                    + " connected=" + isHfpDeviceConnected
                    + ", BT active=" + mIsBtActive
                    + ", isDelayRequired=" + isDelayRequired);
            if (!isDelayRequired) latch.countDown();
            mPendingRingingLatches.add(latch);
        }
        return latch;
    }

    /**
     * Remove a ringing ready latch that has been used and is no longer pending.
     * @param l The latch to remove.
     */
    private void removePendingRingingReadyLatch(CountDownLatch l) {
        synchronized (mPendingRingingLatches) {
            mPendingRingingLatches.remove(l);
        }
    }

    /**
     * Count down all pending ringing ready latches and then clear the list.
     */
    private void clearPendingRingingLatches() {
        synchronized (mPendingRingingLatches) {
            mPendingRingingLatches.forEach(CountDownLatch::countDown);
            mPendingRingingLatches.clear();
        }
    }

    /**
@@ -129,6 +212,7 @@ public class AsyncRingtonePlayer {
        Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1;
        BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2;
        Session session = (Session) args.arg3;
        CountDownLatch ringingReadyLatch = (CountDownLatch) args.arg4;
        args.recycle();

        Log.continueSession(session, "ARP.hP");
@@ -136,17 +220,29 @@ public class AsyncRingtonePlayer {
            // Don't bother with any of this if there is an EVENT_STOP waiting, but give the
            // consumer a chance to do anything no matter what.
            if (mHandler.hasMessages(EVENT_STOP)) {
                Log.i(this, "handlePlay: skipping play early due to pending STOP");
                removePendingRingingReadyLatch(ringingReadyLatch);
                ringtoneConsumer.accept(null, /* stopped= */ true);
                return;
            }
            Ringtone ringtone = null;
            boolean hasStopped = false;
            try {
                try {
                    Log.i(this, "handlePlay: delay ring for ready signal...");
                    boolean reachedZero = ringingReadyLatch.await(PLAY_DELAY_TIMEOUT_MS,
                            TimeUnit.MILLISECONDS);
                    Log.i(this, "handlePlay: ringing ready, timeout=" + !reachedZero);
                } catch (InterruptedException e) {
                    Log.w(this, "handlePlay: latch exception: " + e);
                }
                ringtone = ringtoneSupplier.get();
                // Ringtone supply can be slow. Re-check for stop event.
                // 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)) {
                    Log.i(this, "handlePlay: skipping play due to pending STOP");
                    hasStopped = true;
                    ringtone.stop();  // proactively release the ringtone.
                    if (ringtone != null) ringtone.stop();  // proactively release the ringtone.
                    return;
                }
                // setRingtone even if null - it also stops any current ringtone to be consistent
@@ -168,6 +264,7 @@ public class AsyncRingtonePlayer {
                mRingtone.play();
                Log.i(this, "Play ringtone, looping.");
            } finally {
                removePendingRingingReadyLatch(ringingReadyLatch);
                ringtoneConsumer.accept(ringtone, hasStopped);
            }
        } finally {
@@ -196,11 +293,15 @@ public class AsyncRingtonePlayer {
        }
    }

    /**
     * @return true if we are currently preparing or playing a ringtone, false if we are not.
     */
    public boolean isPlaying() {
        return mRingtone != null;
        return mIsPlaying;
    }

    private void setRingtone(@Nullable Ringtone ringtone) {
        Log.i(this, "setRingtone: ringtone null="  + (ringtone == null));
        // Make sure that any previously created instance of Ringtone is stopped so the MediaPlayer
        // can be released, before replacing mRingtone with a new instance. This is always created
        // as a looping Ringtone, so if not stopped it will keep playing on the background.
+14 −1
Original line number Diff line number Diff line
@@ -27,14 +27,17 @@ public class CallAudioRoutePeripheralAdapter implements WiredHeadsetManager.List

    private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
    private final BluetoothRouteManager mBluetoothRouteManager;
    private final AsyncRingtonePlayer mRingtonePlayer;

    public CallAudioRoutePeripheralAdapter(
            CallAudioRouteStateMachine callAudioRouteStateMachine,
            BluetoothRouteManager bluetoothManager,
            WiredHeadsetManager wiredHeadsetManager,
            DockManager dockManager) {
            DockManager dockManager,
            AsyncRingtonePlayer ringtonePlayer) {
        mCallAudioRouteStateMachine = callAudioRouteStateMachine;
        mBluetoothRouteManager = bluetoothManager;
        mRingtonePlayer = ringtonePlayer;

        mBluetoothRouteManager.setListener(this);
        wiredHeadsetManager.addListener(this);
@@ -75,12 +78,22 @@ public class CallAudioRoutePeripheralAdapter implements WiredHeadsetManager.List

    @Override
    public void onBluetoothAudioConnected() {
        mRingtonePlayer.updateBtActiveState(true);
        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
    }

    @Override
    public void onBluetoothAudioConnecting() {
        mRingtonePlayer.updateBtActiveState(false);
        // Pretend like audio is connected when communicating w/ CARSM.
        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
    }

    @Override
    public void onBluetoothAudioDisconnected() {
        mRingtonePlayer.updateBtActiveState(false);
        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
    }
+2 −1
Original line number Diff line number Diff line
@@ -615,7 +615,8 @@ public class CallsManager extends Call.ListenerBase
                        callAudioRouteStateMachine,
                        bluetoothManager,
                        wiredHeadsetManager,
                        mDockManager);
                        mDockManager,
                        asyncRingtonePlayer);
        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        InCallTonePlayer.MediaPlayerFactory mediaPlayerFactory =
                (resourceId, attributes) ->
+1 −1
Original line number Diff line number Diff line
@@ -456,7 +456,7 @@ public class Ringer {
            };
            deferBlockOnRingingFuture = true;  // Run in vibrationLogic.
            if (ringtoneSupplier != null) {
                mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic);
                mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached);
            } else {
                afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false);
            }
+2 −2
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public class BluetoothRouteManager extends StateMachine {
        void onBluetoothActiveDevicePresent();
        void onBluetoothActiveDeviceGone();
        void onBluetoothAudioConnected();
        void onBluetoothAudioConnecting();
        void onBluetoothAudioDisconnected();
        /**
         * This gets called when we get an unexpected state change from Bluetooth. Their stack does
@@ -231,8 +232,7 @@ public class BluetoothRouteManager extends StateMachine {
            sendMessageDelayed(CONNECTION_TIMEOUT, args,
                    mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
                            mContext.getContentResolver()));
            // Pretend like audio is connected when communicating w/ CARSM.
            mListener.onBluetoothAudioConnected();
            mListener.onBluetoothAudioConnecting();
        }

        @Override
Loading