• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settingslib.bluetooth;
18 
19 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.NonNull;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothClass;
25 import android.bluetooth.BluetoothDevice;
26 import android.bluetooth.BluetoothLeAudioContentMetadata;
27 import android.bluetooth.BluetoothLeBroadcast;
28 import android.bluetooth.BluetoothLeBroadcastAssistant;
29 import android.bluetooth.BluetoothLeBroadcastMetadata;
30 import android.bluetooth.BluetoothLeBroadcastReceiveState;
31 import android.bluetooth.BluetoothLeBroadcastSubgroup;
32 import android.bluetooth.BluetoothProfile;
33 import android.bluetooth.BluetoothProfile.ServiceListener;
34 import android.bluetooth.BluetoothStatusCodes;
35 import android.content.ContentResolver;
36 import android.content.Context;
37 import android.database.ContentObserver;
38 import android.net.Uri;
39 import android.os.Build;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.provider.Settings;
43 import android.text.TextUtils;
44 import android.util.Log;
45 import android.util.Pair;
46 
47 import androidx.annotation.RequiresApi;
48 
49 import com.android.settingslib.R;
50 
51 import java.nio.charset.StandardCharsets;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.List;
56 import java.util.UUID;
57 import java.util.concurrent.Executor;
58 import java.util.concurrent.Executors;
59 import java.util.concurrent.ThreadLocalRandom;
60 
61 /**
62  * LocalBluetoothLeBroadcast provides an interface between the Settings app
63  * and the functionality of the local {@link BluetoothLeBroadcast}.
64  * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback.
65  */
66 public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
67     private static final String TAG = "LocalBluetoothLeBroadcast";
68     private static final boolean DEBUG = BluetoothUtils.D;
69 
70     static final String NAME = "LE_AUDIO_BROADCAST";
71     private static final String UNDERLINE = "_";
72     private static final int DEFAULT_CODE_MAX = 9999;
73     private static final int DEFAULT_CODE_MIN = 1000;
74     // Order of this profile in device profiles list
75     private static final int ORDINAL = 1;
76     private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
77     private static final Uri[] SETTINGS_URIS = new Uri[]{
78             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO),
79             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
80             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME),
81     };
82 
83     private BluetoothLeBroadcast mServiceBroadcast;
84     private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
85     private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
86     private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
87     private BluetoothLeAudioContentMetadata.Builder mBuilder;
88     private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
89     private String mAppSourceName = "";
90     private String mNewAppSourceName = "";
91     private boolean mIsBroadcastProfileReady = false;
92     private boolean mIsBroadcastAssistantProfileReady = false;
93     private String mProgramInfo;
94     private byte[] mBroadcastCode;
95     private Executor mExecutor;
96     private ContentResolver mContentResolver;
97     private ContentObserver mSettingsObserver;
98 
99     private final ServiceListener mServiceListener = new ServiceListener() {
100         @Override
101         public void onServiceConnected(int profile, BluetoothProfile proxy) {
102             if (DEBUG) {
103                 Log.d(TAG, "Bluetooth service connected: " + profile);
104             }
105             if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) {
106                 mServiceBroadcast = (BluetoothLeBroadcast) proxy;
107                 mIsBroadcastProfileReady = true;
108                 registerServiceCallBack(mExecutor, mBroadcastCallback);
109                 List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata();
110                 if (!metadata.isEmpty()) {
111                     updateBroadcastInfoFromBroadcastMetadata(metadata.get(0));
112                 }
113                 registerContentObserver();
114             } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
115                     && !mIsBroadcastAssistantProfileReady) {
116                 mIsBroadcastAssistantProfileReady = true;
117                 mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
118                 registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback);
119             }
120         }
121 
122         @Override
123         public void onServiceDisconnected(int profile) {
124             if (DEBUG) {
125                 Log.d(TAG, "Bluetooth service disconnected");
126             }
127             if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) {
128                 mIsBroadcastProfileReady = false;
129                 unregisterServiceCallBack(mBroadcastCallback);
130             }
131             if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
132                     && mIsBroadcastAssistantProfileReady) {
133                 mIsBroadcastAssistantProfileReady = false;
134                 unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback);
135             }
136 
137             if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) {
138                 unregisterContentObserver();
139             }
140         }
141     };
142 
143     private final BluetoothLeBroadcast.Callback mBroadcastCallback =
144             new BluetoothLeBroadcast.Callback() {
145                 @Override
146                 public void onBroadcastStarted(int reason, int broadcastId) {
147                     if (DEBUG) {
148                         Log.d(TAG,
149                                 "onBroadcastStarted(), reason = " + reason + ", broadcastId = "
150                                         + broadcastId);
151                     }
152                     setLatestBroadcastId(broadcastId);
153                     setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
154                 }
155 
156                 @Override
157                 public void onBroadcastStartFailed(int reason) {
158                     if (DEBUG) {
159                         Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
160                     }
161                 }
162 
163                 @Override
164                 public void onBroadcastMetadataChanged(int broadcastId,
165                         @NonNull BluetoothLeBroadcastMetadata metadata) {
166                     if (DEBUG) {
167                         Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
168                     }
169                     setLatestBluetoothLeBroadcastMetadata(metadata);
170                 }
171 
172                 @Override
173                 public void onBroadcastStopped(int reason, int broadcastId) {
174                     if (DEBUG) {
175                         Log.d(TAG,
176                                 "onBroadcastStopped(), reason = " + reason + ", broadcastId = "
177                                         + broadcastId);
178                     }
179 
180                     stopLocalSourceReceivers();
181                     resetCacheInfo();
182                 }
183 
184                 @Override
185                 public void onBroadcastStopFailed(int reason) {
186                     if (DEBUG) {
187                         Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
188                     }
189                 }
190 
191                 @Override
192                 public void onBroadcastUpdated(int reason, int broadcastId) {
193                     if (DEBUG) {
194                         Log.d(TAG,
195                                 "onBroadcastUpdated(), reason = " + reason + ", broadcastId = "
196                                         + broadcastId);
197                     }
198                     setLatestBroadcastId(broadcastId);
199                     setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
200                 }
201 
202                 @Override
203                 public void onBroadcastUpdateFailed(int reason, int broadcastId) {
204                     if (DEBUG) {
205                         Log.d(TAG,
206                                 "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = "
207                                         + broadcastId);
208                     }
209                 }
210 
211                 @Override
212                 public void onPlaybackStarted(int reason, int broadcastId) {
213                 }
214 
215                 @Override
216                 public void onPlaybackStopped(int reason, int broadcastId) {
217                 }
218             };
219 
220     private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
221             new BluetoothLeBroadcastAssistant.Callback() {
222                 @Override
223                 public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId,
224                         int reason) {}
225                 @Override
226                 public void onSearchStarted(int reason) {}
227 
228                 @Override
229                 public void onSearchStartFailed(int reason) {}
230 
231                 @Override
232                 public void onSearchStopped(int reason) {}
233 
234                 @Override
235                 public void onSearchStopFailed(int reason) {}
236 
237                 @Override
238                 public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
239 
240                 @Override
241                 public void onSourceAddFailed(@NonNull BluetoothDevice sink,
242                         @NonNull BluetoothLeBroadcastMetadata source, int reason) {}
243 
244                 @Override
245                 public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId,
246                         int reason) {}
247 
248                 @Override
249                 public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId,
250                         int reason) {}
251 
252                 @Override
253                 public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId,
254                         int reason) {
255                     if (DEBUG) {
256                         Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = "
257                                 + reason + ", sourceId = " + sourceId);
258                     }
259                 }
260 
261                 @Override
262                 public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId,
263                         int reason) {
264                     if (DEBUG) {
265                         Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = "
266                                 + reason + ", sourceId = " + sourceId);
267                     }
268                 }
269 
270                 @Override
271                 public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId,
272                         @NonNull BluetoothLeBroadcastReceiveState state) {}
273             };
274 
275     private class BroadcastSettingsObserver extends ContentObserver {
BroadcastSettingsObserver(Handler h)276         BroadcastSettingsObserver(Handler h) {
277             super(h);
278         }
279 
280         @Override
onChange(boolean selfChange)281         public void onChange(boolean selfChange) {
282             Log.d(TAG, "BroadcastSettingsObserver: onChange");
283             updateBroadcastInfoFromContentProvider();
284         }
285     }
286 
LocalBluetoothLeBroadcast(Context context)287     LocalBluetoothLeBroadcast(Context context) {
288         mExecutor = Executors.newSingleThreadExecutor();
289         mBuilder = new BluetoothLeAudioContentMetadata.Builder();
290         mContentResolver = context.getContentResolver();
291         Handler handler = new Handler(Looper.getMainLooper());
292         mSettingsObserver = new BroadcastSettingsObserver(handler);
293         updateBroadcastInfoFromContentProvider();
294 
295         // Before registering callback, the constructor should finish creating the all of variables.
296         BluetoothAdapter.getDefaultAdapter()
297                 .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
298         BluetoothAdapter.getDefaultAdapter()
299                 .getProfileProxy(context, mServiceListener,
300                 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
301     }
302 
303     /**
304      * Start the LE Broadcast. If the system started the LE Broadcast, then the system calls the
305      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
306      */
startBroadcast(String appSourceName, String language)307     public void startBroadcast(String appSourceName, String language) {
308         mNewAppSourceName = appSourceName;
309         if (mServiceBroadcast == null) {
310             Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast.");
311             return;
312         }
313         String programInfo = getProgramInfo();
314         if (DEBUG) {
315             Log.d(TAG,
316                     "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
317         }
318         buildContentMetadata(language, programInfo);
319         mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata,
320                 (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null);
321     }
322 
getProgramInfo()323     public String getProgramInfo() {
324         return mProgramInfo;
325     }
326 
setProgramInfo(String programInfo)327     public void setProgramInfo(String programInfo) {
328         setProgramInfo(programInfo, /*updateContentResolver=*/ true);
329     }
330 
setProgramInfo(String programInfo, boolean updateContentResolver)331     private void setProgramInfo(String programInfo, boolean updateContentResolver) {
332         if (TextUtils.isEmpty(programInfo)) {
333             Log.d(TAG, "setProgramInfo: programInfo is null or empty");
334             return;
335         }
336         if (mProgramInfo != null && TextUtils.equals(mProgramInfo, programInfo)) {
337             Log.d(TAG, "setProgramInfo: programInfo is not changed");
338             return;
339         }
340         Log.d(TAG, "setProgramInfo: " + programInfo);
341         mProgramInfo = programInfo;
342         if (updateContentResolver) {
343             if (mContentResolver == null) {
344                 Log.d(TAG, "mContentResolver is null");
345                 return;
346             }
347             Settings.Secure.putString(mContentResolver,
348                     Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo);
349         }
350     }
351 
getBroadcastCode()352     public byte[] getBroadcastCode() {
353         return mBroadcastCode;
354     }
355 
setBroadcastCode(byte[] broadcastCode)356     public void setBroadcastCode(byte[] broadcastCode) {
357         setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true);
358     }
359 
setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver)360     private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) {
361         if (broadcastCode == null) {
362             Log.d(TAG, "setBroadcastCode: broadcastCode is null");
363             return;
364         }
365         if (mBroadcastCode != null && Arrays.equals(broadcastCode, mBroadcastCode)) {
366             Log.d(TAG, "setBroadcastCode: broadcastCode is not changed");
367             return;
368         }
369         mBroadcastCode = broadcastCode;
370         if (updateContentResolver) {
371             if (mContentResolver == null) {
372                 Log.d(TAG, "mContentResolver is null");
373                 return;
374             }
375             Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
376                     new String(broadcastCode, StandardCharsets.UTF_8));
377         }
378     }
379 
setLatestBroadcastId(int broadcastId)380     private void setLatestBroadcastId(int broadcastId) {
381         Log.d(TAG, "setLatestBroadcastId: mBroadcastId is " + broadcastId);
382         mBroadcastId = broadcastId;
383     }
384 
getLatestBroadcastId()385     public int getLatestBroadcastId() {
386         return mBroadcastId;
387     }
388 
setAppSourceName(String appSourceName, boolean updateContentResolver)389     private void setAppSourceName(String appSourceName, boolean updateContentResolver) {
390         if (TextUtils.isEmpty(appSourceName)) {
391             appSourceName = "";
392         }
393         if (mAppSourceName != null && TextUtils.equals(mAppSourceName, appSourceName)) {
394             Log.d(TAG, "setAppSourceName: appSourceName is not changed");
395             return;
396         }
397         mAppSourceName = appSourceName;
398         mNewAppSourceName = "";
399         if (updateContentResolver) {
400             if (mContentResolver == null) {
401                 Log.d(TAG, "mContentResolver is null");
402                 return;
403             }
404             Settings.Secure.putString(mContentResolver,
405                     Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName);
406         }
407     }
408 
getAppSourceName()409     public String getAppSourceName() {
410         return mAppSourceName;
411     }
412 
setLatestBluetoothLeBroadcastMetadata( BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata)413     private void setLatestBluetoothLeBroadcastMetadata(
414             BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata) {
415         if (bluetoothLeBroadcastMetadata != null
416                 && bluetoothLeBroadcastMetadata.getBroadcastId() == mBroadcastId) {
417             mBluetoothLeBroadcastMetadata = bluetoothLeBroadcastMetadata;
418             updateBroadcastInfoFromBroadcastMetadata(bluetoothLeBroadcastMetadata);
419         }
420     }
421 
getLatestBluetoothLeBroadcastMetadata()422     public BluetoothLeBroadcastMetadata getLatestBluetoothLeBroadcastMetadata() {
423         if (mServiceBroadcast == null) {
424             Log.d(TAG, "The BluetoothLeBroadcast is null");
425             return null;
426         }
427         if (mBluetoothLeBroadcastMetadata == null) {
428             final List<BluetoothLeBroadcastMetadata> metadataList =
429                     mServiceBroadcast.getAllBroadcastMetadata();
430             mBluetoothLeBroadcastMetadata = metadataList.stream()
431                     .filter(i -> i.getBroadcastId() == mBroadcastId)
432                     .findFirst()
433                     .orElse(null);
434         }
435         return mBluetoothLeBroadcastMetadata;
436     }
437 
updateBroadcastInfoFromContentProvider()438     private void updateBroadcastInfoFromContentProvider() {
439         if (mContentResolver == null) {
440             Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null");
441             return;
442         }
443         String programInfo = Settings.Secure.getString(mContentResolver,
444                 Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO);
445         if (programInfo == null) {
446             programInfo = getDefaultValueOfProgramInfo();
447         }
448         setProgramInfo(programInfo, /*updateContentResolver=*/ false);
449 
450         String prefBroadcastCode = Settings.Secure.getString(mContentResolver,
451                 Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE);
452         byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode()
453                 : prefBroadcastCode.getBytes(StandardCharsets.UTF_8);
454         setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false);
455 
456         String appSourceName = Settings.Secure.getString(mContentResolver,
457                 Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME);
458         setAppSourceName(appSourceName, /*updateContentResolver=*/ false);
459     }
460 
updateBroadcastInfoFromBroadcastMetadata( BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata)461     private void updateBroadcastInfoFromBroadcastMetadata(
462             BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata) {
463         if (bluetoothLeBroadcastMetadata == null) {
464             Log.d(TAG, "The bluetoothLeBroadcastMetadata is null");
465             return;
466         }
467         setBroadcastCode(bluetoothLeBroadcastMetadata.getBroadcastCode());
468         setLatestBroadcastId(bluetoothLeBroadcastMetadata.getBroadcastId());
469 
470         List<BluetoothLeBroadcastSubgroup> subgroup = bluetoothLeBroadcastMetadata.getSubgroups();
471         if (subgroup == null || subgroup.size() < 1) {
472             Log.d(TAG, "The subgroup is not valid value");
473             return;
474         }
475         BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata();
476         setProgramInfo(contentMetadata.getProgramInfo());
477         setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true);
478     }
479 
480     /**
481      * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system
482      * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}.
483      */
stopLatestBroadcast()484     public void stopLatestBroadcast() {
485         stopBroadcast(mBroadcastId);
486     }
487 
488     /**
489      * Stop the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
490      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
491      */
stopBroadcast(int broadcastId)492     public void stopBroadcast(int broadcastId) {
493         if (mServiceBroadcast == null) {
494             Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
495             return;
496         }
497         if (DEBUG) {
498             Log.d(TAG, "stopBroadcast()");
499         }
500         mServiceBroadcast.stopBroadcast(broadcastId);
501     }
502 
503     /**
504      * Update the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
505      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
506      */
updateBroadcast(String appSourceName, String language)507     public void updateBroadcast(String appSourceName, String language) {
508         if (mServiceBroadcast == null) {
509             Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast.");
510             return;
511         }
512         String programInfo = getProgramInfo();
513         if (DEBUG) {
514             Log.d(TAG,
515                     "updateBroadcast: language = " + language + " ,programInfo = " + programInfo);
516         }
517         mNewAppSourceName = appSourceName;
518         mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
519         mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
520     }
521 
registerServiceCallBack(@onNull @allbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback)522     public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
523             @NonNull BluetoothLeBroadcast.Callback callback) {
524         if (mServiceBroadcast == null) {
525             Log.d(TAG, "The BluetoothLeBroadcast is null.");
526             return;
527         }
528 
529         mServiceBroadcast.registerCallback(executor, callback);
530     }
531 
532     /**
533      * Register Broadcast Assistant Callbacks to track it's state and receivers
534      *
535      * @param executor Executor object for callback
536      * @param callback Callback object to be registered
537      */
registerBroadcastAssistantCallback(@onNull @allbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback)538     public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor,
539             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
540         if (mServiceBroadcastAssistant == null) {
541             Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
542             return;
543         }
544 
545         mServiceBroadcastAssistant.registerCallback(executor, callback);
546     }
547 
unregisterServiceCallBack(@onNull BluetoothLeBroadcast.Callback callback)548     public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
549         if (mServiceBroadcast == null) {
550             Log.d(TAG, "The BluetoothLeBroadcast is null.");
551             return;
552         }
553 
554         mServiceBroadcast.unregisterCallback(callback);
555     }
556 
557     /**
558      * Unregister previousely registered Broadcast Assistant Callbacks
559      *
560      * @param callback Callback object to be unregistered
561      */
unregisterBroadcastAssistantCallback( @onNull BluetoothLeBroadcastAssistant.Callback callback)562     public void unregisterBroadcastAssistantCallback(
563             @NonNull BluetoothLeBroadcastAssistant.Callback callback) {
564         if (mServiceBroadcastAssistant == null) {
565             Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null.");
566             return;
567         }
568 
569         mServiceBroadcastAssistant.unregisterCallback(callback);
570     }
571 
buildContentMetadata(String language, String programInfo)572     private void buildContentMetadata(String language, String programInfo) {
573         mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo(
574                 programInfo).build();
575     }
576 
getLocalBluetoothLeBroadcastMetaData()577     public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() {
578         final BluetoothLeBroadcastMetadata metadata = getLatestBluetoothLeBroadcastMetadata();
579         if (metadata == null) {
580             Log.d(TAG, "The BluetoothLeBroadcastMetadata is null.");
581             return null;
582         }
583         return new LocalBluetoothLeBroadcastMetadata(metadata);
584     }
585 
isProfileReady()586     public boolean isProfileReady() {
587         return mIsBroadcastProfileReady;
588     }
589 
590     @Override
getProfileId()591     public int getProfileId() {
592         return BluetoothProfile.LE_AUDIO_BROADCAST;
593     }
594 
accessProfileEnabled()595     public boolean accessProfileEnabled() {
596         return false;
597     }
598 
isAutoConnectable()599     public boolean isAutoConnectable() {
600         return true;
601     }
602 
603     /**
604      * Not supported since LE Audio Broadcasts do not establish a connection.
605      */
getConnectionStatus(BluetoothDevice device)606     public int getConnectionStatus(BluetoothDevice device) {
607         if (mServiceBroadcast == null) {
608             return BluetoothProfile.STATE_DISCONNECTED;
609         }
610         // LE Audio Broadcasts are not connection-oriented.
611         return mServiceBroadcast.getConnectionState(device);
612     }
613 
614     /**
615      * Not supported since LE Audio Broadcasts do not establish a connection.
616      */
getConnectedDevices()617     public List<BluetoothDevice> getConnectedDevices() {
618         if (mServiceBroadcast == null) {
619             return new ArrayList<BluetoothDevice>(0);
620         }
621         // LE Audio Broadcasts are not connection-oriented.
622         return mServiceBroadcast.getConnectedDevices();
623     }
624 
625     public @NonNull
getAllBroadcastMetadata()626     List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
627         if (mServiceBroadcast == null) {
628             Log.d(TAG, "The BluetoothLeBroadcast is null.");
629             return Collections.emptyList();
630         }
631 
632         return mServiceBroadcast.getAllBroadcastMetadata();
633     }
634 
isEnabled(BluetoothDevice device)635     public boolean isEnabled(BluetoothDevice device) {
636         if (mServiceBroadcast == null) {
637             return false;
638         }
639 
640         return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty();
641     }
642 
643     /**
644      * Service does not provide method to get/set policy.
645      */
getConnectionPolicy(BluetoothDevice device)646     public int getConnectionPolicy(BluetoothDevice device) {
647         return CONNECTION_POLICY_FORBIDDEN;
648     }
649 
650     /**
651      * Service does not provide "setEnabled" method. Please use {@link #startBroadcast},
652      * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)}
653      */
setEnabled(BluetoothDevice device, boolean enabled)654     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
655         return false;
656     }
657 
toString()658     public String toString() {
659         return NAME;
660     }
661 
getOrdinal()662     public int getOrdinal() {
663         return ORDINAL;
664     }
665 
getNameResource(BluetoothDevice device)666     public int getNameResource(BluetoothDevice device) {
667         return R.string.summary_empty;
668     }
669 
getSummaryResourceForDevice(BluetoothDevice device)670     public int getSummaryResourceForDevice(BluetoothDevice device) {
671         int state = getConnectionStatus(device);
672         return BluetoothUtils.getConnectionStateSummary(state);
673     }
674 
getDrawableResource(BluetoothClass btClass)675     public int getDrawableResource(BluetoothClass btClass) {
676         return 0;
677     }
678 
679     @RequiresApi(Build.VERSION_CODES.S)
finalize()680     protected void finalize() {
681         if (DEBUG) {
682             Log.d(TAG, "finalize()");
683         }
684         if (mServiceBroadcast != null) {
685             try {
686                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
687                         BluetoothProfile.LE_AUDIO_BROADCAST,
688                         mServiceBroadcast);
689                 mServiceBroadcast = null;
690             } catch (Throwable t) {
691                 Log.w(TAG, "Error cleaning up LeAudio proxy", t);
692             }
693         }
694     }
695 
getDefaultValueOfProgramInfo()696     private String getDefaultValueOfProgramInfo() {
697         //set the default value;
698         int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
699         return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix;
700     }
701 
getDefaultValueOfBroadcastCode()702     private byte[] getDefaultValueOfBroadcastCode() {
703         //set the default value;
704         return generateRandomPassword().getBytes(StandardCharsets.UTF_8);
705     }
706 
resetCacheInfo()707     private void resetCacheInfo() {
708         if (DEBUG) {
709             Log.d(TAG, "resetCacheInfo:");
710         }
711         setAppSourceName("", /*updateContentResolver=*/ true);
712         mBluetoothLeBroadcastMetadata = null;
713         mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
714     }
715 
generateRandomPassword()716     private String generateRandomPassword() {
717         String randomUUID = UUID.randomUUID().toString();
718         //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
719         return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
720     }
721 
registerContentObserver()722     private void registerContentObserver() {
723         if (mContentResolver == null) {
724             Log.d(TAG, "mContentResolver is null");
725             return;
726         }
727         for (Uri uri : SETTINGS_URIS) {
728             mContentResolver.registerContentObserver(uri, false, mSettingsObserver);
729         }
730     }
731 
unregisterContentObserver()732     private void unregisterContentObserver() {
733         if (mContentResolver == null) {
734             Log.d(TAG, "mContentResolver is null");
735             return;
736         }
737         mContentResolver.unregisterContentObserver(mSettingsObserver);
738     }
739 
stopLocalSourceReceivers()740     private void stopLocalSourceReceivers() {
741         if (DEBUG) {
742             Log.d(TAG, "stopLocalSourceReceivers()");
743         }
744         for (BluetoothDevice device : mServiceBroadcastAssistant.getConnectedDevices()) {
745             for (BluetoothLeBroadcastReceiveState receiveState :
746                     mServiceBroadcastAssistant.getAllSources(device)) {
747                 /* Check if local/last broadcast is the synced one */
748                 int localBroadcastId = getLatestBroadcastId();
749                 if (receiveState.getBroadcastId() != localBroadcastId) continue;
750 
751                 mServiceBroadcastAssistant.removeSource(device, receiveState.getSourceId());
752             }
753         }
754     }
755 
756 }
757