diff options
| author | 2023-07-19 12:29:45 +0000 | |
|---|---|---|
| committer | 2023-08-02 13:22:11 +0000 | |
| commit | 9bd6f02e0930bc973d7f6d83def61fae0016f734 (patch) | |
| tree | 62676ee880d35e6eae0c8451db70e2c9af35b7b0 | |
| parent | d047a258c3746fcd4c9c54acadc19563b4affb00 (diff) | |
LocalBluetoothLeBroadcast: Remove sources on broadcast stop
This adds possibility to stop and remove local sources on remote
devices.
Tag: #feature
Bug: 290883060
Test: atest BluetoothLeBroadcastMetadataExtTest
Change-Id: Ic7f063e743303babbef189359e68df62fcceb91d
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java | 191 |
1 files changed, 159 insertions, 32 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index a05a6e9781da..69b61c74625e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -25,10 +25,13 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudioContentMetadata; import android.bluetooth.BluetoothLeBroadcast; +import android.bluetooth.BluetoothLeBroadcastAssistant; import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothLeBroadcastSubgroup; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; +import android.bluetooth.BluetoothStatusCodes; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -39,6 +42,7 @@ import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import androidx.annotation.RequiresApi; @@ -76,14 +80,16 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), }; - private BluetoothLeBroadcast mService; + private BluetoothLeBroadcast mServiceBroadcast; + private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant; private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata; private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; private BluetoothLeAudioContentMetadata.Builder mBuilder; private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; private String mAppSourceName = ""; private String mNewAppSourceName = ""; - private boolean mIsProfileReady; + private boolean mIsBroadcastProfileReady = false; + private boolean mIsBroadcastAssistantProfileReady = false; private String mProgramInfo; private byte[] mBroadcastCode; private Executor mExecutor; @@ -94,17 +100,22 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onServiceConnected(int profile, BluetoothProfile proxy) { if (DEBUG) { - Log.d(TAG, "Bluetooth service connected"); + Log.d(TAG, "Bluetooth service connected: " + profile); } - if(!mIsProfileReady) { - mService = (BluetoothLeBroadcast) proxy; - mIsProfileReady = true; + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) { + mServiceBroadcast = (BluetoothLeBroadcast) proxy; + mIsBroadcastProfileReady = true; registerServiceCallBack(mExecutor, mBroadcastCallback); List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata(); if (!metadata.isEmpty()) { updateBroadcastInfoFromBroadcastMetadata(metadata.get(0)); } registerContentObserver(); + } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && !mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = true; + mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; + registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); } } @@ -113,9 +124,17 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "Bluetooth service disconnected"); } - if(mIsProfileReady) { - mIsProfileReady = false; + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) { + mIsBroadcastProfileReady = false; unregisterServiceCallBack(mBroadcastCallback); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = false; + unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); + } + + if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { unregisterContentObserver(); } } @@ -157,6 +176,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + broadcastId); } + + stopLocalSourceReceivers(); resetCacheInfo(); } @@ -196,6 +217,61 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } }; + private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = + new BluetoothLeBroadcastAssistant.Callback() { + @Override + public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, + int reason) {} + @Override + public void onSearchStarted(int reason) {} + + @Override + public void onSearchStartFailed(int reason) {} + + @Override + public void onSearchStopped(int reason) {} + + @Override + public void onSearchStopFailed(int reason) {} + + @Override + public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {} + + @Override + public void onSourceAddFailed(@NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, int reason) {} + + @Override + public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, + int reason) {} + + @Override + public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, + int reason) {} + + @Override + public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, + int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = " + + reason + ", sourceId = " + sourceId); + } + } + + @Override + public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, + int reason) { + if (DEBUG) { + Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = " + + reason + ", sourceId = " + sourceId); + } + } + + @Override + public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) {} + }; + private class BroadcastSettingsObserver extends ContentObserver { BroadcastSettingsObserver(Handler h) { super(h); @@ -219,6 +295,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { // Before registering callback, the constructor should finish creating the all of variables. BluetoothAdapter.getDefaultAdapter() .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy(context, mServiceListener, + BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); } /** @@ -227,7 +306,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { */ public void startBroadcast(String appSourceName, String language) { mNewAppSourceName = appSourceName; - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast."); return; } @@ -237,7 +316,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mService.startBroadcast(mBluetoothLeAudioContentMetadata, + mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } @@ -341,13 +420,13 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public BluetoothLeBroadcastMetadata getLatestBluetoothLeBroadcastMetadata() { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null"); return null; } if (mBluetoothLeBroadcastMetadata == null) { final List<BluetoothLeBroadcastMetadata> metadataList = - mService.getAllBroadcastMetadata(); + mServiceBroadcast.getAllBroadcastMetadata(); mBluetoothLeBroadcastMetadata = metadataList.stream() .filter(i -> i.getBroadcastId() == mBroadcastId) .findFirst() @@ -411,14 +490,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { * corresponding callback {@link BluetoothLeBroadcast.Callback}. */ public void stopBroadcast(int broadcastId) { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast."); return; } if (DEBUG) { Log.d(TAG, "stopBroadcast()"); } - mService.stopBroadcast(broadcastId); + mServiceBroadcast.stopBroadcast(broadcastId); } /** @@ -426,7 +505,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { * corresponding callback {@link BluetoothLeBroadcast.Callback}. */ public void updateBroadcast(String appSourceName, String language) { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast."); return; } @@ -437,26 +516,57 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } mNewAppSourceName = appSourceName; mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build(); - mService.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); + mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); } public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback) { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return; } - mService.registerCallback(executor, callback); + mServiceBroadcast.registerCallback(executor, callback); + } + + /** + * Register Broadcast Assistant Callbacks to track it's state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull BluetoothLeBroadcastAssistant.Callback callback) { + if (mServiceBroadcastAssistant == null) { + Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + return; + } + + mServiceBroadcastAssistant.registerCallback(executor, callback); } public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return; } - mService.unregisterCallback(callback); + mServiceBroadcast.unregisterCallback(callback); + } + + /** + * Unregister previousely registered Broadcast Assistant Callbacks + * + * @param callback Callback object to be unregistered + */ + public void unregisterBroadcastAssistantCallback( + @NonNull BluetoothLeBroadcastAssistant.Callback callback) { + if (mServiceBroadcastAssistant == null) { + Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + return; + } + + mServiceBroadcastAssistant.unregisterCallback(callback); } private void buildContentMetadata(String language, String programInfo) { @@ -474,7 +584,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public boolean isProfileReady() { - return mIsProfileReady; + return mIsBroadcastProfileReady; } @Override @@ -494,40 +604,40 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { * Not supported since LE Audio Broadcasts do not establish a connection. */ public int getConnectionStatus(BluetoothDevice device) { - if (mService == null) { + if (mServiceBroadcast == null) { return BluetoothProfile.STATE_DISCONNECTED; } // LE Audio Broadcasts are not connection-oriented. - return mService.getConnectionState(device); + return mServiceBroadcast.getConnectionState(device); } /** * Not supported since LE Audio Broadcasts do not establish a connection. */ public List<BluetoothDevice> getConnectedDevices() { - if (mService == null) { + if (mServiceBroadcast == null) { return new ArrayList<BluetoothDevice>(0); } // LE Audio Broadcasts are not connection-oriented. - return mService.getConnectedDevices(); + return mServiceBroadcast.getConnectedDevices(); } public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { - if (mService == null) { + if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return Collections.emptyList(); } - return mService.getAllBroadcastMetadata(); + return mServiceBroadcast.getAllBroadcastMetadata(); } public boolean isEnabled(BluetoothDevice device) { - if (mService == null) { + if (mServiceBroadcast == null) { return false; } - return !mService.getAllBroadcastMetadata().isEmpty(); + return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty(); } /** @@ -571,12 +681,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "finalize()"); } - if (mService != null) { + if (mServiceBroadcast != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy( BluetoothProfile.LE_AUDIO_BROADCAST, - mService); - mService = null; + mServiceBroadcast); + mServiceBroadcast = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); } @@ -626,4 +736,21 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } mContentResolver.unregisterContentObserver(mSettingsObserver); } + + private void stopLocalSourceReceivers() { + if (DEBUG) { + Log.d(TAG, "stopLocalSourceReceivers()"); + } + for (BluetoothDevice device : mServiceBroadcastAssistant.getConnectedDevices()) { + for (BluetoothLeBroadcastReceiveState receiveState : + mServiceBroadcastAssistant.getAllSources(device)) { + /* Check if local/last broadcast is the synced one */ + int localBroadcastId = getLatestBroadcastId(); + if (receiveState.getBroadcastId() != localBroadcastId) continue; + + mServiceBroadcastAssistant.removeSource(device, receiveState.getSourceId()); + } + } + } + } |