Loading android/app/src/com/android/bluetooth/hfp/HeadsetService.java +3 −2 Original line number Diff line number Diff line Loading @@ -1399,8 +1399,9 @@ public class HeadsetService extends ProfileService { LeAudioService leAudioService = mFactory.getLeAudioService(); if (leAudioService != null) { Log.i(TAG, "Make sure there is no le audio device active."); leAudioService.setActiveDevice(null); leAudioService.setInactiveForHfpHandover(mActiveDevice); } broadcastActiveDevice(mActiveDevice); int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Loading Loading @@ -1432,7 +1433,7 @@ public class HeadsetService extends ProfileService { } } int connectAudio() { public int connectAudio() { synchronized (mStateMachines) { BluetoothDevice device = mActiveDevice; if (device == null) { Loading android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +72 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.mcp.McpService; import com.android.bluetooth.tbs.TbsGatt; import com.android.bluetooth.vc.VolumeControlService; Loading Loading @@ -117,6 +118,7 @@ public class LeAudioService extends ProfileService { LeAudioNativeInterface mLeAudioNativeInterface; boolean mLeAudioNativeIsInitialized = false; BluetoothDevice mHfpHandoverDevice = null; LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; @VisibleForTesting AudioManager mAudioManager; Loading Loading @@ -331,6 +333,7 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.cleanup(); mLeAudioNativeInterface = null; mLeAudioNativeIsInitialized = false; mHfpHandoverDevice = null; // Set the service and BLE devices as inactive setLeAudioService(null); Loading Loading @@ -1173,6 +1176,36 @@ public class LeAudioService extends ProfileService { } } private void handleGroupIdleDuringCall() { if (mHfpHandoverDevice == null) { if (DBG) { Log.d(TAG, "There is no HFP handover"); } return; } HeadsetService headsetService = mServiceFactory.getHeadsetService(); if (headsetService == null) { if (DBG) { Log.d(TAG, "There is no HFP service available"); } return; } BluetoothDevice activeHfpDevice = headsetService.getActiveDevice(); if (activeHfpDevice == null) { if (DBG) { Log.d(TAG, "Make " + mHfpHandoverDevice + " active again "); } headsetService.setActiveDevice(mHfpHandoverDevice); } else { if (DBG) { Log.d(TAG, "Connect audio to " + activeHfpDevice); } headsetService.connectAudio(); } mHfpHandoverDevice = null; } // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { Loading Loading @@ -1338,6 +1371,10 @@ public class LeAudioService extends ProfileService { handleGroupTransitToInactive(groupId); break; } case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: { handleGroupIdleDuringCall(); break; } default: break; } Loading Loading @@ -1746,6 +1783,20 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.setInCall(inCall); } /** * Set Inactive by HFP during handover */ public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice) { if (!mLeAudioNativeIsInitialized) { Log.e(TAG, "Le Audio not initialized properly."); return; } if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) { mHfpHandoverDevice = hfpHandoverDevice; setActiveDevice(null); } } /** * Set connection policy of the profile and connects it if connectionPolicy is * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is Loading Loading @@ -2467,6 +2518,26 @@ public class LeAudioService extends ProfileService { } } @Override public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice, AttributionSource source, SynchronousResultReceiver receiver) { try { Objects.requireNonNull(source, "source cannot be null"); Objects.requireNonNull(receiver, "receiver cannot be null"); LeAudioService service = getService(source); if (service == null) { throw new IllegalStateException("service is null"); } enforceBluetoothPrivilegedPermission(service); service.setInactiveForHfpHandover(hfpHandoverDevice); receiver.send(null); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupRemoveNode(int groupId, BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { Loading Loading @@ -2712,6 +2783,7 @@ public class LeAudioService extends ProfileService { ProfileService.println(sb, " currentlyActiveGroupId: " + getActiveGroupId()); ProfileService.println(sb, " mActiveAudioOutDevice: " + mActiveAudioOutDevice); ProfileService.println(sb, " mActiveAudioInDevice: " + mActiveAudioInDevice); ProfileService.println(sb, " mHfpHandoverDevice:" + mHfpHandoverDevice); for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); Loading android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +3 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class LeAudioStackEvent { static final int GROUP_STATUS_INACTIVE = 0; static final int GROUP_STATUS_ACTIVE = 1; static final int GROUP_STATUS_TURNED_IDLE_DURING_CALL = 2; static final int GROUP_NODE_ADDED = 1; static final int GROUP_NODE_REMOVED = 2; Loading Loading @@ -192,6 +193,8 @@ public class LeAudioStackEvent { return "GROUP_STATUS_ACTIVE"; case GROUP_STATUS_INACTIVE: return "GROUP_STATUS_INACTIVE"; case GROUP_STATUS_TURNED_IDLE_DURING_CALL: return "GROUP_STATUS_TURNED_IDLE_DURING_CALL"; default: break; } Loading system/binder/android/bluetooth/IBluetoothLeAudio.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -67,12 +67,15 @@ oneway interface IBluetoothLeAudio { void setCcidInformation(in ParcelUuid userUuid, in int ccid, in int contextType, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setInCall(in boolean inCall, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setInactiveForHfpHandover(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); /* Same value as bluetooth::groups::kGroupUnknown */ const int LE_AUDIO_GROUP_ID_INVALID = -1; const int GROUP_STATUS_INACTIVE = 0; const int GROUP_STATUS_ACTIVE = 1; const int GROUP_STATUS_TURNED_IDLE_DURING_CALL = 2; const int GROUP_NODE_ADDED = 1; const int GROUP_NODE_REMOVED = 2; Loading system/bta/le_audio/client.cc +22 −4 Original line number Diff line number Diff line Loading @@ -833,15 +833,16 @@ class LeAudioClientImpl : public LeAudioClient { return; } auto group_id_to_close = active_group_id_; active_group_id_ = bluetooth::groups::kGroupUnknown; if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_); StopAudio(); ClientAudioIntefraceRelease(); GroupStop(active_group_id_); callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE); active_group_id_ = group_id; GroupStop(group_id_to_close); callbacks_->OnGroupStatus(group_id_to_close, GroupStatus::INACTIVE); return; } Loading Loading @@ -3675,6 +3676,20 @@ class LeAudioClientImpl : public LeAudioClient { } } void NotifyUpperLayerGroupTurnedIdleDuringCall(int group_id) { if (!osi_property_get_bool(kNotifyUpperLayerAboutGroupBeingInIdleDuringCall, false)) { return; } /* If group is inactive, phone is in call and Group is not having CIS * connected, notify upper layer about it, so it can decide to create SCO if * it is in the handover case */ if (in_call_ && active_group_id_ == bluetooth::groups::kGroupUnknown) { callbacks_->OnGroupStatus(group_id, GroupStatus::TURNED_IDLE_DURING_CALL); } } void StatusReportCb(int group_id, GroupStreamStatus status) { LOG_INFO("status: %d , audio_sender_state %s, audio_receiver_state %s", static_cast<int>(status), Loading Loading @@ -3745,6 +3760,7 @@ class LeAudioClientImpl : public LeAudioClient { } CancelStreamingRequest(); if (group) { NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_); HandlePendingAvailableContexts(group); HandlePendingDeviceDisconnection(group); } Loading Loading @@ -3782,6 +3798,8 @@ class LeAudioClientImpl : public LeAudioClient { AudioState audio_sender_state_; /* Keep in call state. */ bool in_call_; static constexpr char kNotifyUpperLayerAboutGroupBeingInIdleDuringCall[] = "persist.bluetooth.leaudio.notify.idle.during.call"; /* Current stream configuration */ LeAudioCodecConfiguration current_source_codec_config; Loading Loading
android/app/src/com/android/bluetooth/hfp/HeadsetService.java +3 −2 Original line number Diff line number Diff line Loading @@ -1399,8 +1399,9 @@ public class HeadsetService extends ProfileService { LeAudioService leAudioService = mFactory.getLeAudioService(); if (leAudioService != null) { Log.i(TAG, "Make sure there is no le audio device active."); leAudioService.setActiveDevice(null); leAudioService.setInactiveForHfpHandover(mActiveDevice); } broadcastActiveDevice(mActiveDevice); int connectStatus = connectAudio(mActiveDevice); if (connectStatus != BluetoothStatusCodes.SUCCESS) { Loading Loading @@ -1432,7 +1433,7 @@ public class HeadsetService extends ProfileService { } } int connectAudio() { public int connectAudio() { synchronized (mStateMachines) { BluetoothDevice device = mActiveDevice; if (device == null) { Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +72 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.mcp.McpService; import com.android.bluetooth.tbs.TbsGatt; import com.android.bluetooth.vc.VolumeControlService; Loading Loading @@ -117,6 +118,7 @@ public class LeAudioService extends ProfileService { LeAudioNativeInterface mLeAudioNativeInterface; boolean mLeAudioNativeIsInitialized = false; BluetoothDevice mHfpHandoverDevice = null; LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; @VisibleForTesting AudioManager mAudioManager; Loading Loading @@ -331,6 +333,7 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.cleanup(); mLeAudioNativeInterface = null; mLeAudioNativeIsInitialized = false; mHfpHandoverDevice = null; // Set the service and BLE devices as inactive setLeAudioService(null); Loading Loading @@ -1173,6 +1176,36 @@ public class LeAudioService extends ProfileService { } } private void handleGroupIdleDuringCall() { if (mHfpHandoverDevice == null) { if (DBG) { Log.d(TAG, "There is no HFP handover"); } return; } HeadsetService headsetService = mServiceFactory.getHeadsetService(); if (headsetService == null) { if (DBG) { Log.d(TAG, "There is no HFP service available"); } return; } BluetoothDevice activeHfpDevice = headsetService.getActiveDevice(); if (activeHfpDevice == null) { if (DBG) { Log.d(TAG, "Make " + mHfpHandoverDevice + " active again "); } headsetService.setActiveDevice(mHfpHandoverDevice); } else { if (DBG) { Log.d(TAG, "Connect audio to " + activeHfpDevice); } headsetService.connectAudio(); } mHfpHandoverDevice = null; } // Suppressed since this is part of a local process @SuppressLint("AndroidFrameworkRequiresPermission") void messageFromNative(LeAudioStackEvent stackEvent) { Loading Loading @@ -1338,6 +1371,10 @@ public class LeAudioService extends ProfileService { handleGroupTransitToInactive(groupId); break; } case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: { handleGroupIdleDuringCall(); break; } default: break; } Loading Loading @@ -1746,6 +1783,20 @@ public class LeAudioService extends ProfileService { mLeAudioNativeInterface.setInCall(inCall); } /** * Set Inactive by HFP during handover */ public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice) { if (!mLeAudioNativeIsInitialized) { Log.e(TAG, "Le Audio not initialized properly."); return; } if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) { mHfpHandoverDevice = hfpHandoverDevice; setActiveDevice(null); } } /** * Set connection policy of the profile and connects it if connectionPolicy is * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is Loading Loading @@ -2467,6 +2518,26 @@ public class LeAudioService extends ProfileService { } } @Override public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice, AttributionSource source, SynchronousResultReceiver receiver) { try { Objects.requireNonNull(source, "source cannot be null"); Objects.requireNonNull(receiver, "receiver cannot be null"); LeAudioService service = getService(source); if (service == null) { throw new IllegalStateException("service is null"); } enforceBluetoothPrivilegedPermission(service); service.setInactiveForHfpHandover(hfpHandoverDevice); receiver.send(null); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupRemoveNode(int groupId, BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { Loading Loading @@ -2712,6 +2783,7 @@ public class LeAudioService extends ProfileService { ProfileService.println(sb, " currentlyActiveGroupId: " + getActiveGroupId()); ProfileService.println(sb, " mActiveAudioOutDevice: " + mActiveAudioOutDevice); ProfileService.println(sb, " mActiveAudioInDevice: " + mActiveAudioInDevice); ProfileService.println(sb, " mHfpHandoverDevice:" + mHfpHandoverDevice); for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { LeAudioGroupDescriptor descriptor = entry.getValue(); Loading
android/app/src/com/android/bluetooth/le_audio/LeAudioStackEvent.java +3 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class LeAudioStackEvent { static final int GROUP_STATUS_INACTIVE = 0; static final int GROUP_STATUS_ACTIVE = 1; static final int GROUP_STATUS_TURNED_IDLE_DURING_CALL = 2; static final int GROUP_NODE_ADDED = 1; static final int GROUP_NODE_REMOVED = 2; Loading Loading @@ -192,6 +193,8 @@ public class LeAudioStackEvent { return "GROUP_STATUS_ACTIVE"; case GROUP_STATUS_INACTIVE: return "GROUP_STATUS_INACTIVE"; case GROUP_STATUS_TURNED_IDLE_DURING_CALL: return "GROUP_STATUS_TURNED_IDLE_DURING_CALL"; default: break; } Loading
system/binder/android/bluetooth/IBluetoothLeAudio.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -67,12 +67,15 @@ oneway interface IBluetoothLeAudio { void setCcidInformation(in ParcelUuid userUuid, in int ccid, in int contextType, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setInCall(in boolean inCall, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") void setInactiveForHfpHandover(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver); /* Same value as bluetooth::groups::kGroupUnknown */ const int LE_AUDIO_GROUP_ID_INVALID = -1; const int GROUP_STATUS_INACTIVE = 0; const int GROUP_STATUS_ACTIVE = 1; const int GROUP_STATUS_TURNED_IDLE_DURING_CALL = 2; const int GROUP_NODE_ADDED = 1; const int GROUP_NODE_REMOVED = 2; Loading
system/bta/le_audio/client.cc +22 −4 Original line number Diff line number Diff line Loading @@ -833,15 +833,16 @@ class LeAudioClientImpl : public LeAudioClient { return; } auto group_id_to_close = active_group_id_; active_group_id_ = bluetooth::groups::kGroupUnknown; if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_); StopAudio(); ClientAudioIntefraceRelease(); GroupStop(active_group_id_); callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE); active_group_id_ = group_id; GroupStop(group_id_to_close); callbacks_->OnGroupStatus(group_id_to_close, GroupStatus::INACTIVE); return; } Loading Loading @@ -3675,6 +3676,20 @@ class LeAudioClientImpl : public LeAudioClient { } } void NotifyUpperLayerGroupTurnedIdleDuringCall(int group_id) { if (!osi_property_get_bool(kNotifyUpperLayerAboutGroupBeingInIdleDuringCall, false)) { return; } /* If group is inactive, phone is in call and Group is not having CIS * connected, notify upper layer about it, so it can decide to create SCO if * it is in the handover case */ if (in_call_ && active_group_id_ == bluetooth::groups::kGroupUnknown) { callbacks_->OnGroupStatus(group_id, GroupStatus::TURNED_IDLE_DURING_CALL); } } void StatusReportCb(int group_id, GroupStreamStatus status) { LOG_INFO("status: %d , audio_sender_state %s, audio_receiver_state %s", static_cast<int>(status), Loading Loading @@ -3745,6 +3760,7 @@ class LeAudioClientImpl : public LeAudioClient { } CancelStreamingRequest(); if (group) { NotifyUpperLayerGroupTurnedIdleDuringCall(group->group_id_); HandlePendingAvailableContexts(group); HandlePendingDeviceDisconnection(group); } Loading Loading @@ -3782,6 +3798,8 @@ class LeAudioClientImpl : public LeAudioClient { AudioState audio_sender_state_; /* Keep in call state. */ bool in_call_; static constexpr char kNotifyUpperLayerAboutGroupBeingInIdleDuringCall[] = "persist.bluetooth.leaudio.notify.idle.during.call"; /* Current stream configuration */ LeAudioCodecConfiguration current_source_codec_config; Loading