Loading android/app/jni/com_android_bluetooth_le_audio.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java +10 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +67 −28 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -528,6 +529,7 @@ public class LeAudioService extends ProfileService { mCreateBroadcastQueue.clear(); mAwaitingBroadcastCreateResponse = false; mIsSourceStreamMonitorModeEnabled = false; mIsSinkStreamMonitorModeEnabled = false; mIsBroadcastPausedFromOutside = false; clearBroadcastTimeoutCallback(); Loading Loading @@ -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" Loading @@ -1067,6 +1070,7 @@ public class LeAudioService extends ProfileService { return; } } byte[] broadcastCode = broadcastSettings.getBroadcastCode(); boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0); Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -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. * Loading Loading @@ -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); } } Loading Loading @@ -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); } Loading Loading @@ -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 = Loading @@ -3509,6 +3515,7 @@ public class LeAudioService extends ProfileService { createBroadcast(settings); } } } break; } case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: Loading @@ -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()); Loading Loading @@ -3722,6 +3734,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 : Loading android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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: Loading Loading @@ -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: Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +29 −8 Original line number Diff line number Diff line Loading @@ -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 = Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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 Loading
android/app/jni/com_android_bluetooth_le_audio.cpp +15 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java +10 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +67 −28 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -528,6 +529,7 @@ public class LeAudioService extends ProfileService { mCreateBroadcastQueue.clear(); mAwaitingBroadcastCreateResponse = false; mIsSourceStreamMonitorModeEnabled = false; mIsSinkStreamMonitorModeEnabled = false; mIsBroadcastPausedFromOutside = false; clearBroadcastTimeoutCallback(); Loading Loading @@ -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" Loading @@ -1067,6 +1070,7 @@ public class LeAudioService extends ProfileService { return; } } byte[] broadcastCode = broadcastSettings.getBroadcastCode(); boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0); Loading @@ -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(), Loading Loading @@ -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); Loading Loading @@ -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. * Loading Loading @@ -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); } } Loading Loading @@ -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); } Loading Loading @@ -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 = Loading @@ -3509,6 +3515,7 @@ public class LeAudioService extends ProfileService { createBroadcast(settings); } } } break; } case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: Loading @@ -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()); Loading Loading @@ -3722,6 +3734,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 : Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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: Loading Loading @@ -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: Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +29 −8 Original line number Diff line number Diff line Loading @@ -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 = Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -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