• 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.BluetoothLeBroadcastMetadata;
29 import android.bluetooth.BluetoothLeBroadcastSubgroup;
30 import android.bluetooth.BluetoothProfile;
31 import android.bluetooth.BluetoothProfile.ServiceListener;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.database.ContentObserver;
35 import android.net.Uri;
36 import android.os.Build;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.provider.Settings;
40 import android.text.TextUtils;
41 import android.util.Log;
42 
43 import androidx.annotation.RequiresApi;
44 
45 import com.android.settingslib.R;
46 
47 import java.nio.charset.StandardCharsets;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Collections;
51 import java.util.List;
52 import java.util.UUID;
53 import java.util.concurrent.Executor;
54 import java.util.concurrent.Executors;
55 import java.util.concurrent.ThreadLocalRandom;
56 
57 /**
58  * LocalBluetoothLeBroadcast provides an interface between the Settings app
59  * and the functionality of the local {@link BluetoothLeBroadcast}.
60  * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback.
61  */
62 public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
63     private static final String TAG = "LocalBluetoothLeBroadcast";
64     private static final boolean DEBUG = BluetoothUtils.D;
65 
66     static final String NAME = "LE_AUDIO_BROADCAST";
67     private static final String UNDERLINE = "_";
68     private static final int DEFAULT_CODE_MAX = 9999;
69     private static final int DEFAULT_CODE_MIN = 1000;
70     // Order of this profile in device profiles list
71     private static final int ORDINAL = 1;
72     private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
73     private static final Uri[] SETTINGS_URIS = new Uri[]{
74             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO),
75             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
76             Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME),
77     };
78 
79     private BluetoothLeBroadcast mService;
80     private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
81     private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata;
82     private BluetoothLeAudioContentMetadata.Builder mBuilder;
83     private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
84     private String mAppSourceName = "";
85     private String mNewAppSourceName = "";
86     private boolean mIsProfileReady;
87     private String mProgramInfo;
88     private byte[] mBroadcastCode;
89     private Executor mExecutor;
90     private ContentResolver mContentResolver;
91     private ContentObserver mSettingsObserver;
92 
93     private final ServiceListener mServiceListener = new ServiceListener() {
94         @Override
95         public void onServiceConnected(int profile, BluetoothProfile proxy) {
96             if (DEBUG) {
97                 Log.d(TAG, "Bluetooth service connected");
98             }
99             if(!mIsProfileReady) {
100                 mService = (BluetoothLeBroadcast) proxy;
101                 mIsProfileReady = true;
102                 registerServiceCallBack(mExecutor, mBroadcastCallback);
103                 List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata();
104                 if (!metadata.isEmpty()) {
105                     updateBroadcastInfoFromBroadcastMetadata(metadata.get(0));
106                 }
107                 registerContentObserver();
108             }
109         }
110 
111         @Override
112         public void onServiceDisconnected(int profile) {
113             if (DEBUG) {
114                 Log.d(TAG, "Bluetooth service disconnected");
115             }
116             if(mIsProfileReady) {
117                 mIsProfileReady = false;
118                 unregisterServiceCallBack(mBroadcastCallback);
119                 unregisterContentObserver();
120             }
121         }
122     };
123 
124     private final BluetoothLeBroadcast.Callback mBroadcastCallback =
125             new BluetoothLeBroadcast.Callback() {
126                 @Override
127                 public void onBroadcastStarted(int reason, int broadcastId) {
128                     if (DEBUG) {
129                         Log.d(TAG,
130                                 "onBroadcastStarted(), reason = " + reason + ", broadcastId = "
131                                         + broadcastId);
132                     }
133                     setLatestBroadcastId(broadcastId);
134                     setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
135                 }
136 
137                 @Override
138                 public void onBroadcastStartFailed(int reason) {
139                     if (DEBUG) {
140                         Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
141                     }
142                 }
143 
144                 @Override
145                 public void onBroadcastMetadataChanged(int broadcastId,
146                         @NonNull BluetoothLeBroadcastMetadata metadata) {
147                     if (DEBUG) {
148                         Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId);
149                     }
150                     setLatestBluetoothLeBroadcastMetadata(metadata);
151                 }
152 
153                 @Override
154                 public void onBroadcastStopped(int reason, int broadcastId) {
155                     if (DEBUG) {
156                         Log.d(TAG,
157                                 "onBroadcastStopped(), reason = " + reason + ", broadcastId = "
158                                         + broadcastId);
159                     }
160                     resetCacheInfo();
161                 }
162 
163                 @Override
164                 public void onBroadcastStopFailed(int reason) {
165                     if (DEBUG) {
166                         Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
167                     }
168                 }
169 
170                 @Override
171                 public void onBroadcastUpdated(int reason, int broadcastId) {
172                     if (DEBUG) {
173                         Log.d(TAG,
174                                 "onBroadcastUpdated(), reason = " + reason + ", broadcastId = "
175                                         + broadcastId);
176                     }
177                     setLatestBroadcastId(broadcastId);
178                     setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true);
179                 }
180 
181                 @Override
182                 public void onBroadcastUpdateFailed(int reason, int broadcastId) {
183                     if (DEBUG) {
184                         Log.d(TAG,
185                                 "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = "
186                                         + broadcastId);
187                     }
188                 }
189 
190                 @Override
191                 public void onPlaybackStarted(int reason, int broadcastId) {
192                 }
193 
194                 @Override
195                 public void onPlaybackStopped(int reason, int broadcastId) {
196                 }
197             };
198 
199     private class BroadcastSettingsObserver extends ContentObserver {
BroadcastSettingsObserver(Handler h)200         BroadcastSettingsObserver(Handler h) {
201             super(h);
202         }
203 
204         @Override
onChange(boolean selfChange)205         public void onChange(boolean selfChange) {
206             Log.d(TAG, "BroadcastSettingsObserver: onChange");
207             updateBroadcastInfoFromContentProvider();
208         }
209     }
210 
LocalBluetoothLeBroadcast(Context context)211     LocalBluetoothLeBroadcast(Context context) {
212         mExecutor = Executors.newSingleThreadExecutor();
213         mBuilder = new BluetoothLeAudioContentMetadata.Builder();
214         mContentResolver = context.getContentResolver();
215         Handler handler = new Handler(Looper.getMainLooper());
216         mSettingsObserver = new BroadcastSettingsObserver(handler);
217         updateBroadcastInfoFromContentProvider();
218 
219         // Before registering callback, the constructor should finish creating the all of variables.
220         BluetoothAdapter.getDefaultAdapter()
221                 .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
222     }
223 
224     /**
225      * Start the LE Broadcast. If the system started the LE Broadcast, then the system calls the
226      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
227      */
startBroadcast(String appSourceName, String language)228     public void startBroadcast(String appSourceName, String language) {
229         mNewAppSourceName = appSourceName;
230         if (mService == null) {
231             Log.d(TAG, "The BluetoothLeBroadcast is null when starting the broadcast.");
232             return;
233         }
234         String programInfo = getProgramInfo();
235         if (DEBUG) {
236             Log.d(TAG,
237                     "startBroadcast: language = " + language + " ,programInfo = " + programInfo);
238         }
239         buildContentMetadata(language, programInfo);
240         mService.startBroadcast(mBluetoothLeAudioContentMetadata, mBroadcastCode);
241     }
242 
getProgramInfo()243     public String getProgramInfo() {
244         return mProgramInfo;
245     }
246 
setProgramInfo(String programInfo)247     public void setProgramInfo(String programInfo) {
248         setProgramInfo(programInfo, /*updateContentResolver=*/ true);
249     }
250 
setProgramInfo(String programInfo, boolean updateContentResolver)251     private void setProgramInfo(String programInfo, boolean updateContentResolver) {
252         if (TextUtils.isEmpty(programInfo)) {
253             Log.d(TAG, "setProgramInfo: programInfo is null or empty");
254             return;
255         }
256         if (mProgramInfo != null && TextUtils.equals(mProgramInfo, programInfo)) {
257             Log.d(TAG, "setProgramInfo: programInfo is not changed");
258             return;
259         }
260         Log.d(TAG, "setProgramInfo: " + programInfo);
261         mProgramInfo = programInfo;
262         if (updateContentResolver) {
263             if (mContentResolver == null) {
264                 Log.d(TAG, "mContentResolver is null");
265                 return;
266             }
267             Settings.Secure.putString(mContentResolver,
268                     Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo);
269         }
270     }
271 
getBroadcastCode()272     public byte[] getBroadcastCode() {
273         return mBroadcastCode;
274     }
275 
setBroadcastCode(byte[] broadcastCode)276     public void setBroadcastCode(byte[] broadcastCode) {
277         setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true);
278     }
279 
setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver)280     private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) {
281         if (broadcastCode == null) {
282             Log.d(TAG, "setBroadcastCode: broadcastCode is null");
283             return;
284         }
285         if (mBroadcastCode != null && Arrays.equals(broadcastCode, mBroadcastCode)) {
286             Log.d(TAG, "setBroadcastCode: broadcastCode is not changed");
287             return;
288         }
289         mBroadcastCode = broadcastCode;
290         if (updateContentResolver) {
291             if (mContentResolver == null) {
292                 Log.d(TAG, "mContentResolver is null");
293                 return;
294             }
295             Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
296                     new String(broadcastCode, StandardCharsets.UTF_8));
297         }
298     }
299 
setLatestBroadcastId(int broadcastId)300     private void setLatestBroadcastId(int broadcastId) {
301         Log.d(TAG, "setLatestBroadcastId: mBroadcastId is " + broadcastId);
302         mBroadcastId = broadcastId;
303     }
304 
getLatestBroadcastId()305     public int getLatestBroadcastId() {
306         return mBroadcastId;
307     }
308 
setAppSourceName(String appSourceName, boolean updateContentResolver)309     private void setAppSourceName(String appSourceName, boolean updateContentResolver) {
310         if (TextUtils.isEmpty(appSourceName)) {
311             appSourceName = "";
312         }
313         if (mAppSourceName != null && TextUtils.equals(mAppSourceName, appSourceName)) {
314             Log.d(TAG, "setAppSourceName: appSourceName is not changed");
315             return;
316         }
317         mAppSourceName = appSourceName;
318         mNewAppSourceName = "";
319         if (updateContentResolver) {
320             if (mContentResolver == null) {
321                 Log.d(TAG, "mContentResolver is null");
322                 return;
323             }
324             Settings.Secure.putString(mContentResolver,
325                     Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName);
326         }
327     }
328 
getAppSourceName()329     public String getAppSourceName() {
330         return mAppSourceName;
331     }
332 
setLatestBluetoothLeBroadcastMetadata( BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata)333     private void setLatestBluetoothLeBroadcastMetadata(
334             BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata) {
335         if (bluetoothLeBroadcastMetadata != null
336                 && bluetoothLeBroadcastMetadata.getBroadcastId() == mBroadcastId) {
337             mBluetoothLeBroadcastMetadata = bluetoothLeBroadcastMetadata;
338             updateBroadcastInfoFromBroadcastMetadata(bluetoothLeBroadcastMetadata);
339         }
340     }
341 
getLatestBluetoothLeBroadcastMetadata()342     public BluetoothLeBroadcastMetadata getLatestBluetoothLeBroadcastMetadata() {
343         if (mService == null) {
344             Log.d(TAG, "The BluetoothLeBroadcast is null");
345             return null;
346         }
347         if (mBluetoothLeBroadcastMetadata == null) {
348             final List<BluetoothLeBroadcastMetadata> metadataList =
349                     mService.getAllBroadcastMetadata();
350             mBluetoothLeBroadcastMetadata = metadataList.stream()
351                     .filter(i -> i.getBroadcastId() == mBroadcastId)
352                     .findFirst()
353                     .orElse(null);
354         }
355         return mBluetoothLeBroadcastMetadata;
356     }
357 
updateBroadcastInfoFromContentProvider()358     private void updateBroadcastInfoFromContentProvider() {
359         if (mContentResolver == null) {
360             Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null");
361             return;
362         }
363         String programInfo = Settings.Secure.getString(mContentResolver,
364                 Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO);
365         if (programInfo == null) {
366             programInfo = getDefaultValueOfProgramInfo();
367         }
368         setProgramInfo(programInfo, /*updateContentResolver=*/ false);
369 
370         String prefBroadcastCode = Settings.Secure.getString(mContentResolver,
371                 Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE);
372         byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode()
373                 : prefBroadcastCode.getBytes(StandardCharsets.UTF_8);
374         setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false);
375 
376         String appSourceName = Settings.Secure.getString(mContentResolver,
377                 Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME);
378         setAppSourceName(appSourceName, /*updateContentResolver=*/ false);
379     }
380 
updateBroadcastInfoFromBroadcastMetadata( BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata)381     private void updateBroadcastInfoFromBroadcastMetadata(
382             BluetoothLeBroadcastMetadata bluetoothLeBroadcastMetadata) {
383         if (bluetoothLeBroadcastMetadata == null) {
384             Log.d(TAG, "The bluetoothLeBroadcastMetadata is null");
385             return;
386         }
387         setBroadcastCode(bluetoothLeBroadcastMetadata.getBroadcastCode());
388         setLatestBroadcastId(bluetoothLeBroadcastMetadata.getBroadcastId());
389 
390         List<BluetoothLeBroadcastSubgroup> subgroup = bluetoothLeBroadcastMetadata.getSubgroups();
391         if (subgroup == null || subgroup.size() < 1) {
392             Log.d(TAG, "The subgroup is not valid value");
393             return;
394         }
395         BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata();
396         setProgramInfo(contentMetadata.getProgramInfo());
397         setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true);
398     }
399 
400     /**
401      * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system
402      * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}.
403      */
stopLatestBroadcast()404     public void stopLatestBroadcast() {
405         stopBroadcast(mBroadcastId);
406     }
407 
408     /**
409      * Stop the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
410      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
411      */
stopBroadcast(int broadcastId)412     public void stopBroadcast(int broadcastId) {
413         if (mService == null) {
414             Log.d(TAG, "The BluetoothLeBroadcast is null when stopping the broadcast.");
415             return;
416         }
417         if (DEBUG) {
418             Log.d(TAG, "stopBroadcast()");
419         }
420         mService.stopBroadcast(broadcastId);
421     }
422 
423     /**
424      * Update the LE Broadcast. If the system stopped the LE Broadcast, then the system calls the
425      * corresponding callback {@link BluetoothLeBroadcast.Callback}.
426      */
updateBroadcast(String appSourceName, String language)427     public void updateBroadcast(String appSourceName, String language) {
428         if (mService == null) {
429             Log.d(TAG, "The BluetoothLeBroadcast is null when updating the broadcast.");
430             return;
431         }
432         String programInfo = getProgramInfo();
433         if (DEBUG) {
434             Log.d(TAG,
435                     "updateBroadcast: language = " + language + " ,programInfo = " + programInfo);
436         }
437         mNewAppSourceName = appSourceName;
438         mBluetoothLeAudioContentMetadata = mBuilder.setProgramInfo(programInfo).build();
439         mService.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata);
440     }
441 
registerServiceCallBack(@onNull @allbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback)442     public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor,
443             @NonNull BluetoothLeBroadcast.Callback callback) {
444         if (mService == null) {
445             Log.d(TAG, "The BluetoothLeBroadcast is null.");
446             return;
447         }
448 
449         mService.registerCallback(executor, callback);
450     }
451 
unregisterServiceCallBack(@onNull BluetoothLeBroadcast.Callback callback)452     public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
453         if (mService == null) {
454             Log.d(TAG, "The BluetoothLeBroadcast is null.");
455             return;
456         }
457 
458         mService.unregisterCallback(callback);
459     }
460 
buildContentMetadata(String language, String programInfo)461     private void buildContentMetadata(String language, String programInfo) {
462         mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo(
463                 programInfo).build();
464     }
465 
getLocalBluetoothLeBroadcastMetaData()466     public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() {
467         final BluetoothLeBroadcastMetadata metadata = getLatestBluetoothLeBroadcastMetadata();
468         if (metadata == null) {
469             Log.d(TAG, "The BluetoothLeBroadcastMetadata is null.");
470             return null;
471         }
472         return new LocalBluetoothLeBroadcastMetadata(metadata);
473     }
474 
isProfileReady()475     public boolean isProfileReady() {
476         return mIsProfileReady;
477     }
478 
479     @Override
getProfileId()480     public int getProfileId() {
481         return BluetoothProfile.LE_AUDIO_BROADCAST;
482     }
483 
accessProfileEnabled()484     public boolean accessProfileEnabled() {
485         return false;
486     }
487 
isAutoConnectable()488     public boolean isAutoConnectable() {
489         return true;
490     }
491 
492     /**
493      * Not supported since LE Audio Broadcasts do not establish a connection.
494      */
getConnectionStatus(BluetoothDevice device)495     public int getConnectionStatus(BluetoothDevice device) {
496         if (mService == null) {
497             return BluetoothProfile.STATE_DISCONNECTED;
498         }
499         // LE Audio Broadcasts are not connection-oriented.
500         return mService.getConnectionState(device);
501     }
502 
503     /**
504      * Not supported since LE Audio Broadcasts do not establish a connection.
505      */
getConnectedDevices()506     public List<BluetoothDevice> getConnectedDevices() {
507         if (mService == null) {
508             return new ArrayList<BluetoothDevice>(0);
509         }
510         // LE Audio Broadcasts are not connection-oriented.
511         return mService.getConnectedDevices();
512     }
513 
514     public @NonNull
getAllBroadcastMetadata()515     List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
516         if (mService == null) {
517             Log.d(TAG, "The BluetoothLeBroadcast is null.");
518             return Collections.emptyList();
519         }
520 
521         return mService.getAllBroadcastMetadata();
522     }
523 
isEnabled(BluetoothDevice device)524     public boolean isEnabled(BluetoothDevice device) {
525         if (mService == null) {
526             return false;
527         }
528 
529         return !mService.getAllBroadcastMetadata().isEmpty();
530     }
531 
532     /**
533      * Service does not provide method to get/set policy.
534      */
getConnectionPolicy(BluetoothDevice device)535     public int getConnectionPolicy(BluetoothDevice device) {
536         return CONNECTION_POLICY_FORBIDDEN;
537     }
538 
539     /**
540      * Service does not provide "setEnabled" method. Please use {@link #startBroadcast},
541      * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)}
542      */
setEnabled(BluetoothDevice device, boolean enabled)543     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
544         return false;
545     }
546 
toString()547     public String toString() {
548         return NAME;
549     }
550 
getOrdinal()551     public int getOrdinal() {
552         return ORDINAL;
553     }
554 
getNameResource(BluetoothDevice device)555     public int getNameResource(BluetoothDevice device) {
556         return R.string.summary_empty;
557     }
558 
getSummaryResourceForDevice(BluetoothDevice device)559     public int getSummaryResourceForDevice(BluetoothDevice device) {
560         int state = getConnectionStatus(device);
561         return BluetoothUtils.getConnectionStateSummary(state);
562     }
563 
getDrawableResource(BluetoothClass btClass)564     public int getDrawableResource(BluetoothClass btClass) {
565         return 0;
566     }
567 
568     @RequiresApi(Build.VERSION_CODES.S)
finalize()569     protected void finalize() {
570         if (DEBUG) {
571             Log.d(TAG, "finalize()");
572         }
573         if (mService != null) {
574             try {
575                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
576                         BluetoothProfile.LE_AUDIO_BROADCAST,
577                         mService);
578                 mService = null;
579             } catch (Throwable t) {
580                 Log.w(TAG, "Error cleaning up LeAudio proxy", t);
581             }
582         }
583     }
584 
getDefaultValueOfProgramInfo()585     private String getDefaultValueOfProgramInfo() {
586         //set the default value;
587         int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX);
588         return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix;
589     }
590 
getDefaultValueOfBroadcastCode()591     private byte[] getDefaultValueOfBroadcastCode() {
592         //set the default value;
593         return generateRandomPassword().getBytes(StandardCharsets.UTF_8);
594     }
595 
resetCacheInfo()596     private void resetCacheInfo() {
597         if (DEBUG) {
598             Log.d(TAG, "resetCacheInfo:");
599         }
600         setAppSourceName("", /*updateContentResolver=*/ true);
601         mBluetoothLeBroadcastMetadata = null;
602         mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
603     }
604 
generateRandomPassword()605     private String generateRandomPassword() {
606         String randomUUID = UUID.randomUUID().toString();
607         //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
608         return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
609     }
610 
registerContentObserver()611     private void registerContentObserver() {
612         if (mContentResolver == null) {
613             Log.d(TAG, "mContentResolver is null");
614             return;
615         }
616         for (Uri uri : SETTINGS_URIS) {
617             mContentResolver.registerContentObserver(uri, false, mSettingsObserver);
618         }
619     }
620 
unregisterContentObserver()621     private void unregisterContentObserver() {
622         if (mContentResolver == null) {
623             Log.d(TAG, "mContentResolver is null");
624             return;
625         }
626         mContentResolver.unregisterContentObserver(mSettingsObserver);
627     }
628 }
629