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

Commit fcbfa545 authored by Grzegorz Kołodziejczyk's avatar Grzegorz Kołodziejczyk
Browse files

le_audio: Introduce broadcast audio session created event

This CL contains an native to service event that informs about broadcast
audio HAL session being ready to expose this device to AF.
This moves also start of audio session before establishing Bluetooth Broadcast link.

Bug: 345372436
Bug: 347204335
Flag: com.android.bluetooth.flags.leaudio_big_depends_on_audio_state
Test: atest LeAudioBroadcastServiceTest
Test: atest bluetooth_test_broadcaster
Change-Id: Ia8bdcce9afad026c1a0b47b16f1104c048d0f3d8
parent 79acd643
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -721,6 +721,7 @@ static jmethodID method_onBroadcastCreated;
static jmethodID method_onBroadcastDestroyed;
static jmethodID method_onBroadcastStateChanged;
static jmethodID method_onBroadcastMetadataChanged;
static jmethodID method_onBroadcastAudioSessionCreated;

static LeAudioBroadcasterInterface* sLeAudioBroadcasterInterface = nullptr;
static std::shared_timed_mutex sBroadcasterInterfaceMutex;
@@ -1132,6 +1133,19 @@ public:
    sCallbackEnv->CallVoidMethod(sBroadcasterCallbacksObj, method_onBroadcastMetadataChanged,
                                 (jint)broadcast_id, metadata_obj.get());
  }

  void OnBroadcastAudioSessionCreated(bool success) override {
    log::info("");

    std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterCallbacksMutex);
    CallbackEnv sCallbackEnv(__func__);

    if (!sCallbackEnv.valid() || sBroadcasterCallbacksObj == nullptr) {
      return;
    }
    sCallbackEnv->CallVoidMethod(sBroadcasterCallbacksObj, method_onBroadcastAudioSessionCreated,
                                 success ? JNI_TRUE : JNI_FALSE);
  }
};

static LeAudioBroadcasterCallbacksImpl sLeAudioBroadcasterCallbacks;
@@ -1449,6 +1463,7 @@ static int register_com_android_bluetooth_le_audio_broadcaster(JNIEnv* env) {
          {"onBroadcastStateChanged", "(II)V", &method_onBroadcastStateChanged},
          {"onBroadcastMetadataChanged", "(ILandroid/bluetooth/BluetoothLeBroadcastMetadata;)V",
           &method_onBroadcastMetadataChanged},
          {"onBroadcastAudioSessionCreated", "(Z)V", &method_onBroadcastAudioSessionCreated},
  };
  GET_JAVA_METHODS(env, "com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface",
                   javaMethods);
+10 −0
Original line number Diff line number Diff line
@@ -131,6 +131,16 @@ public class LeAudioBroadcasterNativeInterface {
        sendMessageToService(event);
    }

    @VisibleForTesting
    public void onBroadcastAudioSessionCreated(boolean success) {
        Log.d(TAG, "onBroadcastAudioSessionCreated: success=" + success);
        LeAudioStackEvent event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED);

        event.valueBool1 = success;
        sendMessageToService(event);
    }

    /**
     * Initializes the native interface.
     *
+67 −28
Original line number Diff line number Diff line
@@ -193,6 +193,7 @@ public class LeAudioService extends ProfileService {
    private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue =
            new LinkedList<>();
    boolean mIsSourceStreamMonitorModeEnabled = false;
    boolean mIsSinkStreamMonitorModeEnabled = false;
    boolean mIsBroadcastPausedFromOutside = false;

    @VisibleForTesting TbsService mTbsService;
@@ -528,6 +529,7 @@ public class LeAudioService extends ProfileService {
        mCreateBroadcastQueue.clear();
        mAwaitingBroadcastCreateResponse = false;
        mIsSourceStreamMonitorModeEnabled = false;
        mIsSinkStreamMonitorModeEnabled = false;
        mIsBroadcastPausedFromOutside = false;

        clearBroadcastTimeoutCallback();
@@ -1053,8 +1055,9 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (!leaudioBigDependsOnAudioState()) {
            if (!areAllGroupsInNotActiveState()) {
            /* Broadcast would be created once unicast group became inactive */
                /* Broadcast will be created once unicast group became inactive */
                Log.i(
                        TAG,
                        "Unicast group is active, queueing Broadcast creation, while the Unicast"
@@ -1067,6 +1070,7 @@ public class LeAudioService extends ProfileService {

                return;
            }
        }

        byte[] broadcastCode = broadcastSettings.getBroadcastCode();
        boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0);
@@ -1090,6 +1094,9 @@ public class LeAudioService extends ProfileService {
        Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));

        mAwaitingBroadcastCreateResponse = true;
        if (leaudioBigDependsOnAudioState()) {
            mCreateBroadcastQueue.add(broadcastSettings);
        }
        mLeAudioBroadcasterNativeInterface.createBroadcast(
                broadcastSettings.isPublicBroadcast(),
                broadcastSettings.getBroadcastName(),
@@ -1293,6 +1300,7 @@ public class LeAudioService extends ProfileService {

        Log.d(TAG, "destroyBroadcast");
        if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
            mIsSinkStreamMonitorModeEnabled = false;
            mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, false);
        }
        mLeAudioBroadcasterNativeInterface.destroyBroadcast(broadcastId);
@@ -2062,16 +2070,6 @@ public class LeAudioService extends ProfileService {
                newDevice, previousDevice, getBroadcastProfile(suppressNoisyIntent));
    }

    /*
     * Listen mode is set when broadcast is queued, waiting for create response notification or
     * descriptor was created - idicate that create notification was received.
     */
    private boolean wasSetSinkListeningMode() {
        return !mCreateBroadcastQueue.isEmpty()
                || mAwaitingBroadcastCreateResponse
                || !mBroadcastDescriptors.isEmpty();
    }

    /**
     * Report the active devices change to the active device manager and the media framework.
     *
@@ -2107,7 +2105,9 @@ public class LeAudioService extends ProfileService {
            if (notifyAndUpdateInactiveOutDeviceOnly
                    && ((newSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0)) {
                newInDevice = getLeadDeviceForTheGroup(groupId);
            } else if (Flags.leaudioBroadcastAudioHandoverPolicies() && wasSetSinkListeningMode()) {
            } else if (Flags.leaudioBroadcastAudioHandoverPolicies()
                    && mIsSinkStreamMonitorModeEnabled) {
                mIsSinkStreamMonitorModeEnabled = false;
                mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, false);
            }
        }
@@ -2716,6 +2716,7 @@ public class LeAudioService extends ProfileService {
        if (bassClientService == null) {
            Log.e(TAG, "handleSourceStreamStatusChange: BASS Client service is not available");

            mIsSourceStreamMonitorModeEnabled = false;
            mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, false);
        }

@@ -3502,6 +3503,11 @@ public class LeAudioService extends ProfileService {
                                mBroadcastIdDeactivatedForUnicastTransition = Optional.empty();
                            }

                            if (leaudioBigDependsOnAudioState()) {
                                if (mAwaitingBroadcastCreateResponse) {
                                    updateFallbackUnicastGroupIdForBroadcast(groupId);
                                }
                            } else {
                                if (!mCreateBroadcastQueue.isEmpty()) {
                                    updateFallbackUnicastGroupIdForBroadcast(groupId);
                                    BluetoothLeBroadcastSettings settings =
@@ -3509,6 +3515,7 @@ public class LeAudioService extends ProfileService {
                                    createBroadcast(settings);
                                }
                            }
                        }
                        break;
                    }
                case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL:
@@ -3528,6 +3535,11 @@ public class LeAudioService extends ProfileService {
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) {
            int broadcastId = stackEvent.valueInt1;
            boolean success = stackEvent.valueBool1;

            if (leaudioBigDependsOnAudioState()) {
                mCreateBroadcastQueue.remove();
            }

            if (success) {
                Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " created.");
                mBroadcastDescriptors.put(broadcastId, new LeAudioBroadcastDescriptor());
@@ -3718,6 +3730,33 @@ public class LeAudioService extends ProfileService {
                                notifyBroadcastMetadataChanged(
                                        broadcastId, stackEvent.broadcastMetadata));
            }
        } else if (stackEvent.type
                == LeAudioStackEvent.EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED) {
            boolean success = stackEvent.valueBool1;

            if (!success) {
                Log.e(TAG, "EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED: failed to create");

                if (mAwaitingBroadcastCreateResponse) {
                    mAwaitingBroadcastCreateResponse = false;
                    mCreateBroadcastQueue.clear();
                }

                return;
            }

            /* Broadcast creation procedure were initiated and some unicast group are still
             * active.
             */
            if (mAwaitingBroadcastCreateResponse && !areAllGroupsInNotActiveState()) {
                /* Broadcast would be created once unicast group became inactive */
                Log.i(TAG, "Unicast group is active, deactivate due to pending broadcast");
                if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
                    mIsSinkStreamMonitorModeEnabled = true;
                    mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, true);
                }
                removeActiveDevice(true);
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) {
            mLeAudioNativeIsInitialized = true;
            for (Map.Entry<ParcelUuid, Pair<Integer, Integer>> entry :
+5 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ public class LeAudioStackEvent {
    public static final int EVENT_TYPE_BROADCAST_DESTROYED = EVENT_TYPE_UNICAST_MAX + 2;
    public static final int EVENT_TYPE_BROADCAST_STATE = EVENT_TYPE_UNICAST_MAX + 3;
    public static final int EVENT_TYPE_BROADCAST_METADATA_CHANGED = EVENT_TYPE_UNICAST_MAX + 4;
    public static final int EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED = EVENT_TYPE_UNICAST_MAX + 5;

    // Do not modify without updating the HAL bt_le_audio.h files.
    // Match up with GroupStatus enum of bt_le_audio.h
@@ -178,6 +179,8 @@ public class LeAudioStackEvent {
                return "EVENT_TYPE_BROADCAST_STATE";
            case EVENT_TYPE_BROADCAST_METADATA_CHANGED:
                return "EVENT_TYPE_BROADCAST_METADATA_CHANGED";
            case EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED:
                return "EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED";
            case EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED:
                return "EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED";
            case EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED:
@@ -364,6 +367,8 @@ public class LeAudioStackEvent {

    private static String eventTypeValueBool1ToString(int type, boolean value) {
        switch (type) {
            case EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED:
                // same as EVENT_TYPE_BROADCAST_CREATED
            case EVENT_TYPE_BROADCAST_CREATED:
                return "{success:" + value + "}";
            default:
+29 −8
Original line number Diff line number Diff line
@@ -795,17 +795,19 @@ public class LeAudioBroadcastServiceTest {
    @Test
    public void testCreatePendingBroadcast() {
        int groupId = 1;
        int broadcastId = 243;
        byte[] code = {0x00, 0x01, 0x00, 0x02};

        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE);

        prepareConnectedUnicastDevice(groupId);

        LeAudioStackEvent create_event =
        LeAudioStackEvent stackEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
        create_event.valueInt1 = groupId;
        create_event.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
        mService.messageFromNative(create_event);
        stackEvent.valueInt1 = groupId;
        stackEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
        mService.messageFromNative(stackEvent);

        /* Prepare create broadcast */
        BluetoothLeAudioContentMetadata.Builder meta_builder =
@@ -817,15 +819,27 @@ public class LeAudioBroadcastServiceTest {
        BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1);
        mService.createBroadcast(settings);

        /* Successfully created audio session notification */
        stackEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED);
        stackEvent.valueBool1 = true;
        mService.messageFromNative(stackEvent);

        /* Check if broadcast is started automatically when created */
        stackEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
        stackEvent.valueInt1 = broadcastId;
        stackEvent.valueBool1 = true;
        mService.messageFromNative(stackEvent);

        /* Active group should become inactive */
        int activeGroup = mService.getActiveGroupId();
        Assert.assertEquals(activeGroup, LE_AUDIO_GROUP_ID_INVALID);

        /* Imitate group inactivity to cause create broadcast */
        create_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
        create_event.valueInt1 = groupId;
        create_event.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
        mService.messageFromNative(create_event);
        stackEvent = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
        stackEvent.valueInt1 = groupId;
        stackEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
        mService.messageFromNative(stackEvent);

        List<BluetoothLeBroadcastSubgroupSettings> settingsList = settings.getSubgroupSettings();

@@ -886,6 +900,7 @@ public class LeAudioBroadcastServiceTest {
    private void prepareHandoverStreamingBroadcast(int groupId, int broadcastId, byte[] code) {
        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE);

        synchronized (mService.mBroadcastCallbacks) {
            mService.mBroadcastCallbacks.register(mCallbacks);
@@ -919,6 +934,12 @@ public class LeAudioBroadcastServiceTest {
        BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1);
        mService.createBroadcast(settings);

        /* Successfully created audio session notification */
        create_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_AUDIO_SESSION_CREATED);
        create_event.valueBool1 = true;
        mService.messageFromNative(create_event);

        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        eq(mBroadcastDevice), eq(null), any(BluetoothProfileConnectionInfo.class));
Loading