diff options
| author | 2023-12-13 07:45:46 +0000 | |
|---|---|---|
| committer | 2023-12-13 07:45:46 +0000 | |
| commit | 40c7673783fa45351e1b832a40c6d17aa3d9bee2 (patch) | |
| tree | de7c62cde50dbef8c8eee859ca3041d5a9b56530 | |
| parent | 06b8920ba75d5ea959376017f45ad77a5a46b036 (diff) | |
| parent | ba28740c059533f1e576868a79a07c5eadaf9109 (diff) | |
Merge "[Audiosharing] Register cached callbacks once service is connected." into main
2 files changed, 387 insertions, 232 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 69b61c74625e..2cb44ec39a23 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -31,7 +31,6 @@ 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; @@ -42,7 +41,6 @@ import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import androidx.annotation.RequiresApi; @@ -53,15 +51,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; /** - * LocalBluetoothLeBroadcast provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcast}. - * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback. + * LocalBluetoothLeBroadcast provides an interface between the Settings app and the functionality of + * the local {@link BluetoothLeBroadcast}. Use the {@link BluetoothLeBroadcast.Callback} to get the + * result callback. */ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcast"; @@ -74,11 +74,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 1; private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; - private static final Uri[] SETTINGS_URIS = new Uri[]{ - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), - }; + private static final Uri[] SETTINGS_URIS = + new Uri[] { + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), + }; private BluetoothLeBroadcast mServiceBroadcast; private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant; @@ -95,62 +96,82 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private Executor mExecutor; private ContentResolver mContentResolver; private ContentObserver mSettingsObserver; + // Cached broadcast callbacks being register before service is connected. + private Map<BluetoothLeBroadcast.Callback, Executor> mCachedBroadcastCallbackExecutorMap = + new ConcurrentHashMap<>(); - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected: " + profile); - } - 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)); + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected: " + profile); + } + 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(); + if (DEBUG) { + Log.d( + TAG, + "onServiceConnected: register " + + "mCachedBroadcastCallbackExecutorMap = " + + mCachedBroadcastCallbackExecutorMap); + } + mCachedBroadcastCallbackExecutorMap.forEach( + (callback, executor) -> + registerServiceCallBack(executor, callback)); + } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && !mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = true; + mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; + registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); + } } - registerContentObserver(); - } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && !mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = true; - mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; - registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); - } - } - @Override - public void onServiceDisconnected(int profile) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) { - mIsBroadcastProfileReady = false; - unregisterServiceCallBack(mBroadcastCallback); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = false; - unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); - } + @Override + public void onServiceDisconnected(int profile) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected: " + profile); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) + && mIsBroadcastProfileReady) { + mIsBroadcastProfileReady = false; + unregisterServiceCallBack(mBroadcastCallback); + mCachedBroadcastCallbackExecutorMap.clear(); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = false; + unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); + } - if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { - unregisterContentObserver(); - } - } - }; + if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { + unregisterContentObserver(); + } + } + }; private final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStarted(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override @@ -161,8 +182,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } @Override - public void onBroadcastMetadataChanged(int broadcastId, - @NonNull BluetoothLeBroadcastMetadata metadata) { + public void onBroadcastMetadataChanged( + int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) { if (DEBUG) { Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId); } @@ -172,8 +193,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastStopped(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStopped(), reason = " + + reason + + ", broadcastId = " + broadcastId); } @@ -191,37 +215,42 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastUpdated(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdated(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override public void onBroadcastUpdateFailed(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdateFailed(), reason = " + + reason + + ", broadcastId = " + broadcastId); } } @Override - public void onPlaybackStarted(int reason, int broadcastId) { - } + public void onPlaybackStarted(int reason, int broadcastId) {} @Override - public void onPlaybackStopped(int reason, int broadcastId) { - } + public void onPlaybackStopped(int reason, int broadcastId) {} }; private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override - public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceAdded( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @Override public void onSearchStarted(int reason) {} @@ -238,38 +267,65 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {} @Override - public void onSourceAddFailed(@NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastMetadata source, int reason) {} + public void onSourceAddFailed( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, + int reason) {} @Override - public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModified( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModifyFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoved( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoved(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoveFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoveFailed(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) {} + public void onReceiveStateChanged( + @NonNull BluetoothDevice sink, + int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) { + if (DEBUG) { + Log.d( + TAG, + "onReceiveStateChanged(), sink = " + + sink + + ", sourceId = " + + sourceId + + ", state = " + + state); + } + } }; private class BroadcastSettingsObserver extends ContentObserver { @@ -296,8 +352,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { BluetoothAdapter.getDefaultAdapter() .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); BluetoothAdapter.getDefaultAdapter() - .getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); } /** @@ -312,11 +368,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, - "startBroadcast: language = " + language + " ,programInfo = " + programInfo); + Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, + mServiceBroadcast.startBroadcast( + mBluetoothLeAudioContentMetadata, (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } @@ -325,7 +381,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public void setProgramInfo(String programInfo) { - setProgramInfo(programInfo, /*updateContentResolver=*/ true); + setProgramInfo(programInfo, /* updateContentResolver= */ true); } private void setProgramInfo(String programInfo, boolean updateContentResolver) { @@ -344,8 +400,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, + programInfo); } } @@ -354,7 +412,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public void setBroadcastCode(byte[] broadcastCode) { - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ true); } private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) { @@ -372,7 +430,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, new String(broadcastCode, StandardCharsets.UTF_8)); } } @@ -401,8 +461,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, + mAppSourceName); } } @@ -427,10 +489,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (mBluetoothLeBroadcastMetadata == null) { final List<BluetoothLeBroadcastMetadata> metadataList = mServiceBroadcast.getAllBroadcastMetadata(); - mBluetoothLeBroadcastMetadata = metadataList.stream() - .filter(i -> i.getBroadcastId() == mBroadcastId) - .findFirst() - .orElse(null); + mBluetoothLeBroadcastMetadata = + metadataList.stream() + .filter(i -> i.getBroadcastId() == mBroadcastId) + .findFirst() + .orElse(null); } return mBluetoothLeBroadcastMetadata; } @@ -440,22 +503,27 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null"); return; } - String programInfo = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); + String programInfo = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); if (programInfo == null) { programInfo = getDefaultValueOfProgramInfo(); } - setProgramInfo(programInfo, /*updateContentResolver=*/ false); + setProgramInfo(programInfo, /* updateContentResolver= */ false); - String prefBroadcastCode = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); - byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode() - : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false); + String prefBroadcastCode = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); + byte[] broadcastCode = + (prefBroadcastCode == null) + ? getDefaultValueOfBroadcastCode() + : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ false); - String appSourceName = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); - setAppSourceName(appSourceName, /*updateContentResolver=*/ false); + String appSourceName = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); + setAppSourceName(appSourceName, /* updateContentResolver= */ false); } private void updateBroadcastInfoFromBroadcastMetadata( @@ -474,12 +542,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata(); setProgramInfo(contentMetadata.getProgramInfo()); - setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true); + setAppSourceName(getAppSourceName(), /* updateContentResolver= */ true); } /** - * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system - * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}. + * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system calls + * the corresponding callback {@link BluetoothLeBroadcast.Callback}. */ public void stopLatestBroadcast() { stopBroadcast(mBroadcastId); @@ -511,7 +579,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, + Log.d( + TAG, "updateBroadcast: language = " + language + " ,programInfo = " + programInfo); } mNewAppSourceName = appSourceName; @@ -519,50 +588,79 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback) { if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "registerServiceCallBack failed, the BluetoothLeBroadcast is null."); + mCachedBroadcastCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mServiceBroadcast.registerCallback(executor, callback); + try { + mServiceBroadcast.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } /** - * Register Broadcast Assistant Callbacks to track it's state and receivers + * Register Broadcast Assistant Callbacks to track its state and receivers * * @param executor Executor object for callback * @param callback Callback object to be registered */ - public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor, + private void registerBroadcastAssistantCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "registerBroadcastAssistantCallback failed, " + + "the BluetoothLeBroadcastAssistant is null."); return; } mServiceBroadcastAssistant.registerCallback(executor, callback); } + /** + * Unregister previously registered Broadcast Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) { + mCachedBroadcastCallbackExecutorMap.remove(callback); if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "unregisterServiceCallBack failed, the BluetoothLeBroadcast is null."); return; } - mServiceBroadcast.unregisterCallback(callback); + try { + mServiceBroadcast.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } /** - * Unregister previousely registered Broadcast Assistant Callbacks + * Unregister previously registered Broadcast Assistant Callbacks * * @param callback Callback object to be unregistered */ - public void unregisterBroadcastAssistantCallback( + private void unregisterBroadcastAssistantCallback( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "unregisterBroadcastAssistantCallback, " + + "the BluetoothLeBroadcastAssistant is null."); return; } @@ -570,8 +668,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private void buildContentMetadata(String language, String programInfo) { - mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo( - programInfo).build(); + mBluetoothLeAudioContentMetadata = + mBuilder.setLanguage(language).setProgramInfo(programInfo).build(); } public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() { @@ -600,9 +698,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return true; } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public int getConnectionStatus(BluetoothDevice device) { if (mServiceBroadcast == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -611,9 +707,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectionState(device); } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public List<BluetoothDevice> getConnectedDevices() { if (mServiceBroadcast == null) { return new ArrayList<BluetoothDevice>(0); @@ -622,8 +716,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectedDevices(); } - public @NonNull - List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { + /** Get all broadcast metadata. */ + public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return Collections.emptyList(); @@ -640,16 +734,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty(); } - /** - * Service does not provide method to get/set policy. - */ + /** Service does not provide method to get/set policy. */ public int getConnectionPolicy(BluetoothDevice device) { return CONNECTION_POLICY_FORBIDDEN; } /** - * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, - * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)} + * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, {@link + * #stopBroadcast()} or {@link #updateBroadcast(String, String)} */ public boolean setEnabled(BluetoothDevice device, boolean enabled) { return false; @@ -683,9 +775,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } if (mServiceBroadcast != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST, - mServiceBroadcast); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST, mServiceBroadcast); mServiceBroadcast = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); @@ -694,13 +785,13 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private String getDefaultValueOfProgramInfo() { - //set the default value; + // set the default value; int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX); return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix; } private byte[] getDefaultValueOfBroadcastCode() { - //set the default value; + // set the default value; return generateRandomPassword().getBytes(StandardCharsets.UTF_8); } @@ -708,14 +799,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "resetCacheInfo:"); } - setAppSourceName("", /*updateContentResolver=*/ true); + setAppSourceName("", /* updateContentResolver= */ true); mBluetoothLeBroadcastMetadata = null; mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; } private String generateRandomPassword() { String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); } @@ -752,5 +843,4 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } } } - } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java index bb103b8896fd..34008ac56042 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java @@ -39,13 +39,14 @@ import com.android.settingslib.R; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; - /** - * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcastAssistant}. - * Use the {@link BluetoothLeBroadcastAssistant.Callback} to get the result callback. + * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app and the + * functionality of the local {@link BluetoothLeBroadcastAssistant}. Use the {@link + * BluetoothLeBroadcastAssistant.Callback} to get the result callback. */ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcastAssistant"; @@ -62,58 +63,76 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; private BluetoothLeBroadcastMetadata.Builder mBuilder; private boolean mIsProfileReady; + // Cached assistant callbacks being register before service is connected. + private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCachedCallbackExecutorMap = + new ConcurrentHashMap<>(); + + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected"); + } + mService = (BluetoothLeBroadcastAssistant) proxy; + // We just bound to the service, so refresh the UI for any connected LeAudio + // devices. + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + while (!deviceList.isEmpty()) { + BluetoothDevice nextDevice = deviceList.remove(0); + CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); + // we may add a new device here, but generally this should not happen + if (device == null) { + if (DEBUG) { + Log.d( + TAG, + "LocalBluetoothLeBroadcastAssistant found new device: " + + nextDevice); + } + device = mDeviceManager.addDevice(nextDevice); + } + device.onProfileStateChanged( + LocalBluetoothLeBroadcastAssistant.this, + BluetoothProfile.STATE_CONNECTED); + device.refresh(); + } - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected"); - } - mService = (BluetoothLeBroadcastAssistant) proxy; - // We just bound to the service, so refresh the UI for any connected LeAudio devices. - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - while (!deviceList.isEmpty()) { - BluetoothDevice nextDevice = deviceList.remove(0); - CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); - // we may add a new device here, but generally this should not happen - if (device == null) { + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; if (DEBUG) { - Log.d(TAG, "LocalBluetoothLeBroadcastAssistant found new device: " - + nextDevice); + Log.d( + TAG, + "onServiceConnected, register mCachedCallbackExecutorMap = " + + mCachedCallbackExecutorMap); } - device = mDeviceManager.addDevice(nextDevice); + mCachedCallbackExecutorMap.forEach( + (callback, executor) -> registerServiceCallBack(executor, callback)); } - device.onProfileStateChanged(LocalBluetoothLeBroadcastAssistant.this, - BluetoothProfile.STATE_CONNECTED); - device.refresh(); - } - mProfileManager.callServiceConnectedListeners(); - mIsProfileReady = true; - } - - @Override - public void onServiceDisconnected(int profile) { - if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { - Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); - return; - } - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - mProfileManager.callServiceDisconnectedListeners(); - mIsProfileReady = false; - } - }; + @Override + public void onServiceDisconnected(int profile) { + if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { + Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); + return; + } + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected"); + } + mProfileManager.callServiceDisconnectedListeners(); + mIsProfileReady = false; + mCachedCallbackExecutorMap.clear(); + } + }; - public LocalBluetoothLeBroadcastAssistant(Context context, + public LocalBluetoothLeBroadcastAssistant( + Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mProfileManager = profileManager; mDeviceManager = deviceManager; - BluetoothAdapter.getDefaultAdapter(). - getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); mBuilder = new BluetoothLeBroadcastMetadata.Builder(); } @@ -123,11 +142,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * @param sink Broadcast Sink to which the Broadcast Source should be added * @param metadata Broadcast Source metadata to be added to the Broadcast Sink * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, - boolean isGroupOp) { + public void addSource( + BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) { if (mService == null) { Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); return; @@ -140,36 +159,55 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * the qr code string. * * @param sink Broadcast Sink to which the Broadcast Source should be added - * @param sourceAddressType hardware MAC Address of the device. See - * {@link BluetoothDevice.AddressType}. + * @param sourceAddressType hardware MAC Address of the device. See {@link + * BluetoothDevice.AddressType}. * @param presentationDelayMicros presentation delay of this Broadcast Source in microseconds. * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source. * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source. - * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, - * {@link BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if - * unknown. + * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link + * BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if unknown. * @param isEncrypted whether the Broadcast Source is encrypted. * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not required. * @param sourceDevice source advertiser address. * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType, - int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId, - int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, - BluetoothDevice sourceDevice, boolean isGroupOp) { + public void addSource( + @NonNull BluetoothDevice sink, + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice, + boolean isGroupOp) { if (DEBUG) { Log.d(TAG, "addSource()"); } - buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId, - paSyncInterval, isEncrypted, broadcastCode, sourceDevice); + buildMetadata( + sourceAddressType, + presentationDelayMicros, + sourceAdvertisingSid, + broadcastId, + paSyncInterval, + isEncrypted, + broadcastCode, + sourceDevice); addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp); } - private void buildMetadata(int sourceAddressType, int presentationDelayMicros, - int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, - byte[] broadcastCode, BluetoothDevice sourceDevice) { + private void buildMetadata( + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice) { mBluetoothLeBroadcastMetadata = mBuilder.setSourceDevice(sourceDevice, sourceAddressType) .setSourceAdvertisingSid(sourceAdvertisingSid) @@ -223,10 +261,10 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile /** * Stops an ongoing search for nearby Broadcast Sources. * - * On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be - * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. - * On failure, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be - * called with reason code + * <p>On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be + * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. On failure, + * {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be called with + * reason code * * @throws IllegalStateException if callback was not registered */ @@ -245,8 +283,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * Get information about all Broadcast Sources that a Broadcast Sink knows about. * * @param sink Broadcast Sink from which to get all Broadcast Sources - * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} - * stored in the Broadcast Sink + * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} stored + * in the Broadcast Sink * @throws NullPointerException when <var>sink</var> is null */ public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources( @@ -261,24 +299,50 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return mService.getAllSources(sink); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Assistant Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "registerServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); + mCachedCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mService.registerCallback(executor, callback); + try { + mService.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } + /** + * Unregister previously registered Broadcast Assistant Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { + mCachedCallbackExecutorMap.remove(callback); if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "unregisterServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); return; } - mService.unregisterCallback(callback); + try { + mService.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } public boolean isProfileReady() { @@ -310,9 +374,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return new ArrayList<BluetoothDevice>(0); } return mService.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}); + new int[] { + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING + }); } public boolean isEnabled(BluetoothDevice device) { @@ -373,9 +439,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile } if (mService != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, - mService); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, mService); mService = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); |