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

Commit 3b31cf4d authored by Pranav Madapurmath's avatar Pranav Madapurmath
Browse files

Resolve separate binding for BT ICS.

Improve the work that was done as part of the call audio refactoring
work to separately bind to the BT ICS. This involves ensuring that the
BT ICS is bound to prior to the Dialer ICS, namely, for handling MT
calls. This will allow the BT call audio to properly handle ringing as
well as the disconnect tone for SCO.

This CL also modifies the new call audio routing logic to ensure that
the call audio route is properly updated to bluetooth when the SCO audio
is connected. It is possible that the headset state isn't updated when
Telecom tries to connect to the SCO device, resulting in Telecom
receiving an indication that the connection failed. Once the headset
state is updated, the SCO audio will be connected and the audio will be
routed to the device. Telecom should ensure that the UI is properly
routed to reflect this. This does open up other possible race conditions
like if an app requests audio to be routed to BT and Telecom requests
SPEAKER_ON shortly after; if the SPEAKER_ON message is processed first,
then the audio will be routed to BT instead if BT_AUDIO_CONNECTED is
received after. This issue is difficult to resolve since if we ignore
the BT_AUDIO_CONNECTED message when we don't expect a pending route
switch, it opens ourselves to the aforementioned issue of the UI not
reflecting that the audio has been routed to BT, which I think is a more
prominent issue. This would probably require further discussion and
possibly help from the BT team to help provide relevant information to
the receiver which Telecom could use to decide when to ignore the
connection state change.

Bug: 328287261
Test: atest TelecomUnitTests
Test: atest CtsTelecomTestCases
Test: Manual to ensure disconnect tone is heard via HFP headset and
verified logs to make sure that BT ICS is bound before Dialer ICS.
Test: Manual toggling BT on/off in personal/work profile to ensure BT
ICS is bound.

Change-Id: I676159ad85edb962b2c7a1c782c8edd065e9c144
parent 058c4154
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -80,12 +80,16 @@
     callers are combined into a single toggle. -->
    <bool name="combine_options_to_block_unavailable_and_unknown_callers">true</bool>

    <!-- System bluetooth stack package name -->
    <string name="system_bluetooth_stack">com.android.bluetooth</string>

    <!-- When true, skip fetching quick reply response -->
    <bool name="skip_loading_canned_text_response">false</bool>

    <!-- When true, skip fetching incoming caller info -->
    <bool name="skip_incoming_caller_info_query">false</bool>

    <string-array name="system_bluetooth_stack_package_name" translatable="false">
        <!-- AOSP -->
        <item>com.android.bluetooth</item>
        <!-- Used for internal targets -->
        <item>com.google.android.bluetooth</item>
    </string-array>
</resources>
+8 −0
Original line number Diff line number Diff line
@@ -4815,6 +4815,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        mBtIcsFuture = btIcsFuture;
    }

    /**
     * @return The binding {@link CompletableFuture} for the BT ICS.
     */
    public CompletableFuture<Boolean> getBtIcsFuture() {
        return mBtIcsFuture;
    }

    /**
     * Wait for bluetooth {@link android.telecom.InCallService} binding completion or timeout. Used
     * for audio routing operations for a ringing call.
@@ -4822,6 +4829,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    public void waitForBtIcs() {
        if (mBtIcsFuture != null) {
            try {
                Log.i(this, "waitForBtIcs: waiting for BT service to bind");
                mBtIcsFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                // ignore
+59 −4
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.annotation.NonNull;
import android.content.Context;
import android.media.IAudioService;
import android.media.ToneGenerator;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UserHandle;
import android.telecom.CallAudioState;
import android.telecom.Log;
@@ -36,6 +38,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@@ -66,9 +69,13 @@ public class CallAudioManager extends CallsManagerListenerBase {

    private Call mStreamingCall;
    private Call mForegroundCall;
    private CompletableFuture<Boolean> mCallRingingFuture;
    private Thread mBtIcsBindingThread;
    private boolean mIsTonePlaying = false;
    private boolean mIsDisconnectedTonePlaying = false;
    private InCallTonePlayer mHoldTonePlayer;
    private final HandlerThread mHandlerThread;
    private final Handler mHandler;

    public CallAudioManager(CallAudioRouteAdapter callAudioRouteAdapter,
            CallsManager callsManager,
@@ -105,6 +112,9 @@ public class CallAudioManager extends CallsManagerListenerBase {
        mBluetoothStateReceiver = bluetoothStateReceiver;
        mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
        mFeatureFlags = featureFlags;
        mHandlerThread = new HandlerThread(this.getClass().getSimpleName());
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());

        mPlayerFactory.setCallAudioManager(this);
        mCallAudioModeStateMachine.setCallAudioManager(this);
@@ -750,16 +760,44 @@ public class CallAudioManager extends CallsManagerListenerBase {

    private void onCallEnteringRinging() {
        if (mRingingCalls.size() == 1) {
            // Wait until the BT ICS binding completed to request further audio route change
            for (Call ringingCall: mRingingCalls) {
                ringingCall.waitForBtIcs();
            }
            Log.i(this, "onCallEnteringRinging: mFeatureFlags.separatelyBindToBtIncallService() ? %s",
                    mFeatureFlags.separatelyBindToBtIncallService());
            Log.i(this, "onCallEnteringRinging: mRingingCalls.getFirst().getBtIcsFuture() = %s",
                    mRingingCalls.getFirst().getBtIcsFuture());
            if (mFeatureFlags.separatelyBindToBtIncallService()
                    && mRingingCalls.getFirst().getBtIcsFuture() != null) {
                mCallRingingFuture  = mRingingCalls.getFirst().getBtIcsFuture()
                        .thenComposeAsync((completed) -> {
                            mCallAudioModeStateMachine.sendMessageWithArgs(
                                    CallAudioModeStateMachine.NEW_RINGING_CALL,
                                    makeArgsForModeStateMachine());
                            return CompletableFuture.completedFuture(completed);
                        }, new LoggedHandlerExecutor(mHandler, "CAM.oCER", mCallsManager.getLock()))
                        .exceptionally((throwable) -> {
                            Log.e(this, throwable, "Error while executing BT ICS future");
                            // Fallback on performing computation on a separate thread.
                            handleBtBindingWaitFallback();
                            return null;
                        });
            } else {
                mCallAudioModeStateMachine.sendMessageWithArgs(
                        CallAudioModeStateMachine.NEW_RINGING_CALL,
                        makeArgsForModeStateMachine());
            }
        }
    }

    private void handleBtBindingWaitFallback() {
        // Wait until the BT ICS binding completed to request further audio route change
        mBtIcsBindingThread = new Thread(() -> {
            mRingingCalls.getFirst().waitForBtIcs();
            mCallAudioModeStateMachine.sendMessageWithArgs(
                    CallAudioModeStateMachine.NEW_RINGING_CALL,
                    makeArgsForModeStateMachine());
        });
        mBtIcsBindingThread.start();
    }

    private void onCallEnteringHold() {
        if (mHoldingCalls.size() == 1) {
            mCallAudioModeStateMachine.sendMessageWithArgs(
@@ -889,12 +927,14 @@ public class CallAudioManager extends CallsManagerListenerBase {
        // we will not play a disconnect tone.
        if (call.isHandoverInProgress()) {
            Log.i(LOG_TAG, "Omitting tone because %s is being handed over.", call);
            completeDisconnectToneFuture(call);
            return;
        }

        if (mForegroundCall != null && call != mForegroundCall && mCalls.size() > 1) {
            Log.v(LOG_TAG, "Omitting tone because we are not foreground" +
                    " and there is another call.");
            completeDisconnectToneFuture(call);
            return;
        }

@@ -935,6 +975,8 @@ public class CallAudioManager extends CallsManagerListenerBase {
                    mCallsManager.onDisconnectedTonePlaying(call, true);
                    mIsDisconnectedTonePlaying = true;
                }
            } else {
                completeDisconnectToneFuture(call);
            }
        }
    }
@@ -1022,6 +1064,14 @@ public class CallAudioManager extends CallsManagerListenerBase {
                oldState == CallState.ON_HOLD;
    }

    private void completeDisconnectToneFuture(Call call) {
        CompletableFuture<Void> disconnectedToneFuture = mCallsManager.getInCallController()
                .getDisconnectedToneBtFutures().get(call.getId());
        if (disconnectedToneFuture != null) {
            disconnectedToneFuture.complete(null);
        }
    }

    @VisibleForTesting
    public Set<Call> getTrackedCalls() {
        return mCalls;
@@ -1031,4 +1081,9 @@ public class CallAudioManager extends CallsManagerListenerBase {
    public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() {
        return mCallStateToCalls;
    }

    @VisibleForTesting
    public CompletableFuture<Boolean> getCallRingingFuture() {
        return mCallRingingFuture;
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -1034,7 +1034,8 @@ public class CallsManager extends Call.ListenerBase

        if (result.shouldAllowCall) {
            if (mFeatureFlags.separatelyBindToBtIncallService()) {
                incomingCall.setBtIcsFuture(mInCallController.bindToBTService(incomingCall));
                mInCallController.bindToBTService(incomingCall, null);
                incomingCall.setBtIcsFuture(mInCallController.getBtBindingFuture(incomingCall));
                setCallState(incomingCall, CallState.RINGING, "successful incoming call");
            }
            incomingCall.setPostCallPackageName(
+1 −1
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ public class DefaultDialerCache {
                        UserHandle.USER_ALL);
    }

    public String getBTInCallServicePackage() {
    public String[] getBTInCallServicePackages() {
        return mRoleManagerAdapter.getBTInCallService();
    }

Loading