Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +43 −5 Original line number Diff line number Diff line Loading @@ -186,13 +186,14 @@ public class LeAudioService extends ProfileService { mAvailableContexts = 0; mInputSelectableConfig = new ArrayList<>(); mOutputSelectableConfig = new ArrayList<>(); mInactivatedDueToContextType = false; } public Boolean mIsConnected; public Boolean mIsActive; public Boolean mHasFallbackDeviceWhenGettingInactive; public Integer mDirection; public BluetoothLeAudioCodecStatus mCodecStatus; Boolean mIsConnected; Boolean mIsActive; Boolean mHasFallbackDeviceWhenGettingInactive; Integer mDirection; BluetoothLeAudioCodecStatus mCodecStatus; /* This can be non empty only for the streaming time */ BluetoothDevice mLostLeadDeviceWhileStreaming; BluetoothDevice mCurrentLeadDevice; Loading @@ -200,6 +201,7 @@ public class LeAudioService extends ProfileService { Integer mAvailableContexts; List<BluetoothLeAudioCodecConfig> mInputSelectableConfig; List<BluetoothLeAudioCodecConfig> mOutputSelectableConfig; Boolean mInactivatedDueToContextType; } private static class LeAudioDeviceDescriptor { Loading Loading @@ -1522,6 +1524,21 @@ public class LeAudioService extends ProfileService { return mActiveAudioOutDevice != null; } private void clearInactiveDueToContextTypeFlags() { synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue(); if (groupDescriptor.mInactivatedDueToContextType) { if (DBG) { Log.d(TAG, "clearInactiveDueToContextTypeFlags " + groupEntry.getKey()); } groupDescriptor.mInactivatedDueToContextType = false; } } } } /** * Set the active device group. * Loading @@ -1539,6 +1556,7 @@ public class LeAudioService extends ProfileService { } groupId = descriptor.mGroupId; clearInactiveDueToContextTypeFlags(); } int currentlyActiveGroupId = getActiveGroupId(); Loading Loading @@ -1824,6 +1842,13 @@ public class LeAudioService extends ProfileService { .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD, 1); break; case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); if (groupDescriptor != null && groupDescriptor.mIsActive) { Log.i(TAG, "Group " + groupId + " is inactivated due to blocked media context"); groupDescriptor.mInactivatedDueToContextType = true; setActiveGroupWithDevice(null, true); } default: break; } Loading Loading @@ -2238,6 +2263,15 @@ public class LeAudioService extends ProfileService { descriptor.mDirection = direction; descriptor.mAvailableContexts = available_contexts; updateInbandRingtoneForTheGroup(groupId); boolean mediaIsAvailable = ((descriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_MEDIA) != 0); if (descriptor.mInactivatedDueToContextType && mediaIsAvailable) { Log.i(TAG, " Media context type again available for " + groupId); setActiveGroupWithDevice(getLeadDeviceForTheGroup(groupId), true); } } else { Log.e(TAG, "messageFromNative: no descriptors for group: " + groupId); } Loading Loading @@ -4148,6 +4182,10 @@ public class LeAudioService extends ProfileService { + groupDescriptor.mLostLeadDeviceWhileStreaming); ProfileService.println(sb, " mInbandRingtoneEnabled: " + groupDescriptor.mInbandRingtoneEnabled); ProfileService.println( sb, "mInactivatedDueToContextType: " + groupDescriptor.mInactivatedDueToContextType); for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry : mDeviceDescriptors.entrySet()) { Loading android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +3 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class LeAudioStackEvent { static final int HEALTH_RECOMMENDATION_ACTION_NONE = 0; static final int HEALTH_RECOMMENDATION_ACTION_DISABLE = 1; static final int HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING = 2; static final int HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP = 3; static final int GROUP_STATUS_INACTIVE = 0; static final int GROUP_STATUS_ACTIVE = 1; Loading Loading @@ -217,6 +218,8 @@ public class LeAudioStackEvent { return "ACTION_DISABLE"; case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: return "ACTION_CONSIDER_DISABLING"; case HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: return "ACTION_INACTIVATE_GROUP"; default: return "UNKNOWN"; } Loading android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -1536,6 +1536,40 @@ public class LeAudioServiceTest { assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); } @Test public void testMediaContextUnavailableForAWhile() { doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED; Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA; injectAudioConfChanged(testGroupId, contexts, 1); // Set group and device as active. injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class)); LeAudioStackEvent healthBasedGroupAction = new LeAudioStackEvent( LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION); healthBasedGroupAction.valueInt1 = testGroupId; healthBasedGroupAction.valueInt2 = LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP; mService.messageFromNative(healthBasedGroupAction); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(), any(BluetoothProfileConnectionInfo.class)); injectAudioConfChanged(testGroupId, contexts, 1); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class)); } private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) { onGroupStatusCallbackCalled = false; Loading system/bta/le_audio/client.cc +4 −0 Original line number Diff line number Diff line Loading @@ -3815,6 +3815,10 @@ class LeAudioClientImpl : public LeAudioClient { if (!remote_contexts.sink.any() && !remote_contexts.source.any()) { LOG_WARN("Requested context type not available on the remote side"); if (leAudioHealthStatus_) { leAudioHealthStatus_->AddStatisticForGroup( group, LeAudioHealthGroupStatType::STREAM_CONTEXT_NOT_AVAILABLE); } return false; } Loading system/bta/le_audio/le_audio_health_status.cc +13 −1 Original line number Diff line number Diff line Loading @@ -144,6 +144,9 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { group->stream_signaling_failures_cnt_++; group->stream_failures_cnt_++; break; case LeAudioHealthGroupStatType::STREAM_CONTEXT_NOT_AVAILABLE: group->stream_context_not_avail_cnt_++; break; } LeAudioHealthBasedAction action = LeAudioHealthBasedAction::NONE; Loading @@ -152,12 +155,20 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { if ((group->stream_failures_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS)) { action = LeAudioHealthBasedAction::DISABLE; } else if (group->stream_context_not_avail_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS) { action = LeAudioHealthBasedAction::INACTIVATE_GROUP; group->stream_context_not_avail_cnt_ = 0; } } else { /* Had some success before */ if ((100 * group->stream_failures_cnt_ / group->stream_success_cnt_) >= THRESHOLD_FOR_DISABLE_CONSIDERATION) { action = LeAudioHealthBasedAction::CONSIDER_DISABLING; } else if (group->stream_context_not_avail_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS) { action = LeAudioHealthBasedAction::INACTIVATE_GROUP; group->stream_context_not_avail_cnt_ = 0; } } Loading Loading @@ -195,7 +206,8 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { << ", success: " << group.stream_success_cnt_ << ", fail total: " << group.stream_failures_cnt_ << ", fail cis: " << group.stream_cis_failures_cnt_ << ", fail signaling: " << group.stream_signaling_failures_cnt_; << ", fail signaling: " << group.stream_signaling_failures_cnt_ << ", context not avail: " << group.stream_context_not_avail_cnt_; dprintf(fd, "%s", stream.str().c_str()); } Loading Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +43 −5 Original line number Diff line number Diff line Loading @@ -186,13 +186,14 @@ public class LeAudioService extends ProfileService { mAvailableContexts = 0; mInputSelectableConfig = new ArrayList<>(); mOutputSelectableConfig = new ArrayList<>(); mInactivatedDueToContextType = false; } public Boolean mIsConnected; public Boolean mIsActive; public Boolean mHasFallbackDeviceWhenGettingInactive; public Integer mDirection; public BluetoothLeAudioCodecStatus mCodecStatus; Boolean mIsConnected; Boolean mIsActive; Boolean mHasFallbackDeviceWhenGettingInactive; Integer mDirection; BluetoothLeAudioCodecStatus mCodecStatus; /* This can be non empty only for the streaming time */ BluetoothDevice mLostLeadDeviceWhileStreaming; BluetoothDevice mCurrentLeadDevice; Loading @@ -200,6 +201,7 @@ public class LeAudioService extends ProfileService { Integer mAvailableContexts; List<BluetoothLeAudioCodecConfig> mInputSelectableConfig; List<BluetoothLeAudioCodecConfig> mOutputSelectableConfig; Boolean mInactivatedDueToContextType; } private static class LeAudioDeviceDescriptor { Loading Loading @@ -1522,6 +1524,21 @@ public class LeAudioService extends ProfileService { return mActiveAudioOutDevice != null; } private void clearInactiveDueToContextTypeFlags() { synchronized (mGroupLock) { for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue(); if (groupDescriptor.mInactivatedDueToContextType) { if (DBG) { Log.d(TAG, "clearInactiveDueToContextTypeFlags " + groupEntry.getKey()); } groupDescriptor.mInactivatedDueToContextType = false; } } } } /** * Set the active device group. * Loading @@ -1539,6 +1556,7 @@ public class LeAudioService extends ProfileService { } groupId = descriptor.mGroupId; clearInactiveDueToContextTypeFlags(); } int currentlyActiveGroupId = getActiveGroupId(); Loading Loading @@ -1824,6 +1842,13 @@ public class LeAudioService extends ProfileService { .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD, 1); break; case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); if (groupDescriptor != null && groupDescriptor.mIsActive) { Log.i(TAG, "Group " + groupId + " is inactivated due to blocked media context"); groupDescriptor.mInactivatedDueToContextType = true; setActiveGroupWithDevice(null, true); } default: break; } Loading Loading @@ -2238,6 +2263,15 @@ public class LeAudioService extends ProfileService { descriptor.mDirection = direction; descriptor.mAvailableContexts = available_contexts; updateInbandRingtoneForTheGroup(groupId); boolean mediaIsAvailable = ((descriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_MEDIA) != 0); if (descriptor.mInactivatedDueToContextType && mediaIsAvailable) { Log.i(TAG, " Media context type again available for " + groupId); setActiveGroupWithDevice(getLeadDeviceForTheGroup(groupId), true); } } else { Log.e(TAG, "messageFromNative: no descriptors for group: " + groupId); } Loading Loading @@ -4148,6 +4182,10 @@ public class LeAudioService extends ProfileService { + groupDescriptor.mLostLeadDeviceWhileStreaming); ProfileService.println(sb, " mInbandRingtoneEnabled: " + groupDescriptor.mInbandRingtoneEnabled); ProfileService.println( sb, "mInactivatedDueToContextType: " + groupDescriptor.mInactivatedDueToContextType); for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry : mDeviceDescriptors.entrySet()) { Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +3 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class LeAudioStackEvent { static final int HEALTH_RECOMMENDATION_ACTION_NONE = 0; static final int HEALTH_RECOMMENDATION_ACTION_DISABLE = 1; static final int HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING = 2; static final int HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP = 3; static final int GROUP_STATUS_INACTIVE = 0; static final int GROUP_STATUS_ACTIVE = 1; Loading Loading @@ -217,6 +218,8 @@ public class LeAudioStackEvent { return "ACTION_DISABLE"; case HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: return "ACTION_CONSIDER_DISABLING"; case HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: return "ACTION_INACTIVATE_GROUP"; default: return "UNKNOWN"; } Loading
android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -1536,6 +1536,40 @@ public class LeAudioServiceTest { assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); } @Test public void testMediaContextUnavailableForAWhile() { doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); connectTestDevice(mSingleDevice, testGroupId); String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED; Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA; injectAudioConfChanged(testGroupId, contexts, 1); // Set group and device as active. injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class)); LeAudioStackEvent healthBasedGroupAction = new LeAudioStackEvent( LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION); healthBasedGroupAction.valueInt1 = testGroupId; healthBasedGroupAction.valueInt2 = LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP; mService.messageFromNative(healthBasedGroupAction); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(null), any(), any(BluetoothProfileConnectionInfo.class)); injectAudioConfChanged(testGroupId, contexts, 1); verify(mAudioManager, times(1)) .handleBluetoothActiveDeviceChanged( eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class)); } private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) { onGroupStatusCallbackCalled = false; Loading
system/bta/le_audio/client.cc +4 −0 Original line number Diff line number Diff line Loading @@ -3815,6 +3815,10 @@ class LeAudioClientImpl : public LeAudioClient { if (!remote_contexts.sink.any() && !remote_contexts.source.any()) { LOG_WARN("Requested context type not available on the remote side"); if (leAudioHealthStatus_) { leAudioHealthStatus_->AddStatisticForGroup( group, LeAudioHealthGroupStatType::STREAM_CONTEXT_NOT_AVAILABLE); } return false; } Loading
system/bta/le_audio/le_audio_health_status.cc +13 −1 Original line number Diff line number Diff line Loading @@ -144,6 +144,9 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { group->stream_signaling_failures_cnt_++; group->stream_failures_cnt_++; break; case LeAudioHealthGroupStatType::STREAM_CONTEXT_NOT_AVAILABLE: group->stream_context_not_avail_cnt_++; break; } LeAudioHealthBasedAction action = LeAudioHealthBasedAction::NONE; Loading @@ -152,12 +155,20 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { if ((group->stream_failures_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS)) { action = LeAudioHealthBasedAction::DISABLE; } else if (group->stream_context_not_avail_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS) { action = LeAudioHealthBasedAction::INACTIVATE_GROUP; group->stream_context_not_avail_cnt_ = 0; } } else { /* Had some success before */ if ((100 * group->stream_failures_cnt_ / group->stream_success_cnt_) >= THRESHOLD_FOR_DISABLE_CONSIDERATION) { action = LeAudioHealthBasedAction::CONSIDER_DISABLING; } else if (group->stream_context_not_avail_cnt_ >= MAX_ALLOWED_FAILURES_IN_A_ROW_WITHOUT_SUCCESS) { action = LeAudioHealthBasedAction::INACTIVATE_GROUP; group->stream_context_not_avail_cnt_ = 0; } } Loading Loading @@ -195,7 +206,8 @@ class LeAudioHealthStatusImpl : public LeAudioHealthStatus { << ", success: " << group.stream_success_cnt_ << ", fail total: " << group.stream_failures_cnt_ << ", fail cis: " << group.stream_cis_failures_cnt_ << ", fail signaling: " << group.stream_signaling_failures_cnt_; << ", fail signaling: " << group.stream_signaling_failures_cnt_ << ", context not avail: " << group.stream_context_not_avail_cnt_; dprintf(fd, "%s", stream.str().c_str()); } Loading