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