1 /* 2 * Copyright 2019 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.bluetooth.btservice.storage; 18 19 import android.bluetooth.BluetoothA2dp; 20 import android.bluetooth.BluetoothA2dp.OptionalCodecsPreferenceStatus; 21 import android.bluetooth.BluetoothA2dp.OptionalCodecsSupportStatus; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothProtoEnums; 26 import android.bluetooth.BluetoothSinkAudioPolicy; 27 import android.content.BroadcastReceiver; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.os.Binder; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.provider.Settings; 38 import android.util.Log; 39 40 import com.android.bluetooth.BluetoothStatsLog; 41 import com.android.bluetooth.Utils; 42 import com.android.bluetooth.btservice.AdapterService; 43 import com.android.internal.annotations.VisibleForTesting; 44 45 import com.google.common.collect.EvictingQueue; 46 47 import java.io.PrintWriter; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Locale; 53 import java.util.Map; 54 import java.util.Objects; 55 import java.util.concurrent.Semaphore; 56 import java.util.concurrent.TimeUnit; 57 58 /** 59 * The active device manager is responsible to handle a Room database 60 * for Bluetooth persistent data. 61 */ 62 public class DatabaseManager { 63 private static final String TAG = "BluetoothDatabase"; 64 65 private AdapterService mAdapterService = null; 66 private HandlerThread mHandlerThread = null; 67 private Handler mHandler = null; 68 private MetadataDatabase mDatabase = null; 69 private boolean mMigratedFromSettingsGlobal = false; 70 71 @VisibleForTesting 72 final Map<String, Metadata> mMetadataCache = new HashMap<>(); 73 private final Semaphore mSemaphore = new Semaphore(1); 74 private static final int METADATA_CHANGED_LOG_MAX_SIZE = 20; 75 private final EvictingQueue<String> mMetadataChangedLog; 76 77 private static final int LOAD_DATABASE_TIMEOUT = 500; // milliseconds 78 private static final int MSG_LOAD_DATABASE = 0; 79 private static final int MSG_UPDATE_DATABASE = 1; 80 private static final int MSG_DELETE_DATABASE = 2; 81 private static final int MSG_CLEAR_DATABASE = 100; 82 private static final String LOCAL_STORAGE = "LocalStorage"; 83 84 private static final String 85 LEGACY_BTSNOOP_DEFAULT_MODE = "bluetooth_btsnoop_default_mode"; 86 private static final String 87 LEGACY_HEADSET_PRIORITY_PREFIX = "bluetooth_headset_priority_"; 88 private static final String 89 LEGACY_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_"; 90 private static final String 91 LEGACY_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_"; 92 private static final String LEGACY_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX = 93 "bluetooth_a2dp_supports_optional_codecs_"; 94 private static final String LEGACY_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX = 95 "bluetooth_a2dp_optional_codecs_enabled_"; 96 private static final String 97 LEGACY_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_"; 98 private static final String 99 LEGACY_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_"; 100 private static final String 101 LEGACY_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_"; 102 private static final String 103 LEGACY_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_"; 104 private static final String 105 LEGACY_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_"; 106 private static final String 107 LEGACY_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_"; 108 private static final String 109 LEGACY_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_"; 110 111 /** 112 * Constructor of the DatabaseManager 113 */ DatabaseManager(AdapterService service)114 public DatabaseManager(AdapterService service) { 115 mAdapterService = service; 116 mMetadataChangedLog = EvictingQueue.create(METADATA_CHANGED_LOG_MAX_SIZE); 117 } 118 119 class DatabaseHandler extends Handler { DatabaseHandler(Looper looper)120 DatabaseHandler(Looper looper) { 121 super(looper); 122 } 123 124 @Override handleMessage(Message msg)125 public void handleMessage(Message msg) { 126 switch (msg.what) { 127 case MSG_LOAD_DATABASE: { 128 synchronized (mDatabase) { 129 List<Metadata> list; 130 try { 131 list = mDatabase.load(); 132 } catch (IllegalStateException e) { 133 Log.e(TAG, "Unable to open database: " + e); 134 mDatabase = MetadataDatabase 135 .createDatabaseWithoutMigration(mAdapterService); 136 list = mDatabase.load(); 137 } 138 compactLastConnectionTime(list); 139 cacheMetadata(list); 140 } 141 break; 142 } 143 case MSG_UPDATE_DATABASE: { 144 Metadata data = (Metadata) msg.obj; 145 synchronized (mDatabase) { 146 mDatabase.insert(data); 147 } 148 break; 149 } 150 case MSG_DELETE_DATABASE: { 151 String address = (String) msg.obj; 152 synchronized (mDatabase) { 153 mDatabase.delete(address); 154 } 155 break; 156 } 157 case MSG_CLEAR_DATABASE: { 158 synchronized (mDatabase) { 159 mDatabase.deleteAll(); 160 } 161 break; 162 } 163 } 164 } 165 } 166 167 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 168 @Override 169 public void onReceive(Context context, Intent intent) { 170 String action = intent.getAction(); 171 if (action == null) { 172 Log.e(TAG, "Received intent with null action"); 173 return; 174 } 175 switch (action) { 176 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { 177 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 178 BluetoothDevice.ERROR); 179 BluetoothDevice device = 180 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 181 Objects.requireNonNull(device, 182 "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 183 bondStateChanged(device, state); 184 break; 185 } 186 case BluetoothAdapter.ACTION_STATE_CHANGED: { 187 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 188 BluetoothAdapter.STATE_OFF); 189 if (!mMigratedFromSettingsGlobal 190 && state == BluetoothAdapter.STATE_TURNING_ON) { 191 migrateSettingsGlobal(); 192 } 193 break; 194 } 195 } 196 } 197 }; 198 bondStateChanged(BluetoothDevice device, int state)199 void bondStateChanged(BluetoothDevice device, int state) { 200 synchronized (mMetadataCache) { 201 String address = device.getAddress(); 202 if (state != BluetoothDevice.BOND_NONE) { 203 if (mMetadataCache.containsKey(address)) { 204 return; 205 } 206 createMetadata(address, false); 207 } else { 208 Metadata metadata = mMetadataCache.get(address); 209 if (metadata != null) { 210 mMetadataCache.remove(address); 211 deleteDatabase(metadata); 212 } 213 } 214 } 215 } 216 isValidMetaKey(int key)217 boolean isValidMetaKey(int key) { 218 if (key >= 0 && key <= BluetoothDevice.getMaxMetadataKey()) { 219 return true; 220 } 221 Log.w(TAG, "Invalid metadata key " + key); 222 return false; 223 } 224 225 /** 226 * Set customized metadata to database with requested key 227 */ 228 @VisibleForTesting setCustomMeta(BluetoothDevice device, int key, byte[] newValue)229 public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) { 230 synchronized (mMetadataCache) { 231 if (device == null) { 232 Log.e(TAG, "setCustomMeta: device is null"); 233 return false; 234 } 235 if (!isValidMetaKey(key)) { 236 Log.e(TAG, "setCustomMeta: meta key invalid " + key); 237 return false; 238 } 239 240 String address = device.getAddress(); 241 if (!mMetadataCache.containsKey(address)) { 242 createMetadata(address, false); 243 } 244 Metadata data = mMetadataCache.get(address); 245 byte[] oldValue = data.getCustomizedMeta(key); 246 if (oldValue != null && Arrays.equals(oldValue, newValue)) { 247 Log.v(TAG, "setCustomMeta: metadata not changed."); 248 return true; 249 } 250 logManufacturerInfo(device, key, newValue); 251 logMetadataChange(address, "setCustomMeta key=" + key); 252 data.setCustomizedMeta(key, newValue); 253 254 updateDatabase(data); 255 mAdapterService.metadataChanged(address, key, newValue); 256 return true; 257 } 258 } 259 260 /** 261 * Get customized metadata from database with requested key 262 */ 263 @VisibleForTesting getCustomMeta(BluetoothDevice device, int key)264 public byte[] getCustomMeta(BluetoothDevice device, int key) { 265 synchronized (mMetadataCache) { 266 if (device == null) { 267 Log.e(TAG, "getCustomMeta: device is null"); 268 return null; 269 } 270 if (!isValidMetaKey(key)) { 271 Log.e(TAG, "getCustomMeta: meta key invalid " + key); 272 return null; 273 } 274 275 String address = device.getAddress(); 276 277 if (!mMetadataCache.containsKey(address)) { 278 Log.d(TAG, "getCustomMeta: device " + address + " is not in cache"); 279 return null; 280 } 281 282 Metadata data = mMetadataCache.get(address); 283 return data.getCustomizedMeta(key); 284 } 285 } 286 287 /** 288 * Set audio policy metadata to database with requested key 289 */ 290 @VisibleForTesting setAudioPolicyMetadata(BluetoothDevice device, BluetoothSinkAudioPolicy policies)291 public boolean setAudioPolicyMetadata(BluetoothDevice device, 292 BluetoothSinkAudioPolicy policies) { 293 synchronized (mMetadataCache) { 294 if (device == null) { 295 Log.e(TAG, "setAudioPolicyMetadata: device is null"); 296 return false; 297 } 298 299 String address = device.getAddress(); 300 if (!mMetadataCache.containsKey(address)) { 301 createMetadata(address, false); 302 } 303 Metadata data = mMetadataCache.get(address); 304 AudioPolicyEntity entity = data.audioPolicyMetadata; 305 entity.callEstablishAudioPolicy = policies.getCallEstablishPolicy(); 306 entity.connectingTimeAudioPolicy = policies.getActiveDevicePolicyAfterConnection(); 307 entity.inBandRingtoneAudioPolicy = policies.getInBandRingtonePolicy(); 308 309 updateDatabase(data); 310 return true; 311 } 312 } 313 314 /** 315 * Get audio policy metadata from database with requested key 316 */ 317 @VisibleForTesting getAudioPolicyMetadata(BluetoothDevice device)318 public BluetoothSinkAudioPolicy getAudioPolicyMetadata(BluetoothDevice device) { 319 synchronized (mMetadataCache) { 320 if (device == null) { 321 Log.e(TAG, "getAudioPolicyMetadata: device is null"); 322 return null; 323 } 324 325 String address = device.getAddress(); 326 327 if (!mMetadataCache.containsKey(address)) { 328 Log.d(TAG, "getAudioPolicyMetadata: device " + address + " is not in cache"); 329 return null; 330 } 331 332 AudioPolicyEntity entity = mMetadataCache.get(address).audioPolicyMetadata; 333 return new BluetoothSinkAudioPolicy.Builder() 334 .setCallEstablishPolicy(entity.callEstablishAudioPolicy) 335 .setActiveDevicePolicyAfterConnection(entity.connectingTimeAudioPolicy) 336 .setInBandRingtonePolicy(entity.inBandRingtoneAudioPolicy) 337 .build(); 338 } 339 } 340 341 /** 342 * Set the device profile connection policy 343 * 344 * @param device {@link BluetoothDevice} wish to set 345 * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET}, 346 * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP}, 347 * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST}, 348 * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP}, 349 * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP}, 350 * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP}, 351 * {@link BluetoothProfile#HEARING_AID}, {@link BluetoothProfile#LE_AUDIO}, 352 * {@link BluetoothProfile#VOLUME_CONTROL}, {@link BluetoothProfile#CSIP_SET_COORDINATOR}, 353 * {@link BluetoothProfile#LE_AUDIO_BROADCAST_ASSISTANT}, 354 * @param newConnectionPolicy the connectionPolicy to set; one of 355 * {@link BluetoothProfile.CONNECTION_POLICY_UNKNOWN}, 356 * {@link BluetoothProfile.CONNECTION_POLICY_FORBIDDEN}, 357 * {@link BluetoothProfile.CONNECTION_POLICY_ALLOWED} 358 */ 359 @VisibleForTesting setProfileConnectionPolicy(BluetoothDevice device, int profile, int newConnectionPolicy)360 public boolean setProfileConnectionPolicy(BluetoothDevice device, int profile, 361 int newConnectionPolicy) { 362 synchronized (mMetadataCache) { 363 if (device == null) { 364 Log.e(TAG, "setProfileConnectionPolicy: device is null"); 365 return false; 366 } 367 368 if (newConnectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 369 && newConnectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN 370 && newConnectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 371 Log.e(TAG, "setProfileConnectionPolicy: invalid connection policy " 372 + newConnectionPolicy); 373 return false; 374 } 375 376 String address = device.getAddress(); 377 if (!mMetadataCache.containsKey(address)) { 378 if (newConnectionPolicy == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) { 379 return true; 380 } 381 createMetadata(address, false); 382 } 383 Metadata data = mMetadataCache.get(address); 384 int oldConnectionPolicy = data.getProfileConnectionPolicy(profile); 385 if (oldConnectionPolicy == newConnectionPolicy) { 386 Log.v(TAG, "setProfileConnectionPolicy connection policy not changed."); 387 return true; 388 } 389 String profileStr = BluetoothProfile.getProfileName(profile); 390 logMetadataChange(address, profileStr + " connection policy changed: " 391 + ": " + oldConnectionPolicy + " -> " + newConnectionPolicy); 392 393 data.setProfileConnectionPolicy(profile, newConnectionPolicy); 394 updateDatabase(data); 395 return true; 396 } 397 } 398 399 /** 400 * Get the device profile connection policy 401 * 402 * @param device {@link BluetoothDevice} wish to get 403 * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET}, 404 * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP}, 405 * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST}, 406 * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP}, 407 * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP}, 408 * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP}, 409 * {@link BluetoothProfile#HEARING_AID}, {@link BluetoothProfile#LE_AUDIO}, 410 * {@link BluetoothProfile#VOLUME_CONTROL}, {@link BluetoothProfile#CSIP_SET_COORDINATOR}, 411 * {@link BluetoothProfile#LE_AUDIO_BROADCAST_ASSISTANT}, 412 * @return the profile connection policy of the device; one of 413 * {@link BluetoothProfile.CONNECTION_POLICY_UNKNOWN}, 414 * {@link BluetoothProfile.CONNECTION_POLICY_FORBIDDEN}, 415 * {@link BluetoothProfile.CONNECTION_POLICY_ALLOWED} 416 */ 417 @VisibleForTesting getProfileConnectionPolicy(BluetoothDevice device, int profile)418 public int getProfileConnectionPolicy(BluetoothDevice device, int profile) { 419 synchronized (mMetadataCache) { 420 if (device == null) { 421 Log.e(TAG, "getProfileConnectionPolicy: device is null"); 422 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 423 } 424 425 String address = device.getAddress(); 426 427 if (!mMetadataCache.containsKey(address)) { 428 Log.d(TAG, "getProfileConnectionPolicy: device " + device.getAnonymizedAddress() 429 + " is not in cache"); 430 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 431 } 432 433 Metadata data = mMetadataCache.get(address); 434 int connectionPolicy = data.getProfileConnectionPolicy(profile); 435 436 Log.v(TAG, "getProfileConnectionPolicy: device " + device.getAnonymizedAddress() 437 + " profile=" + BluetoothProfile.getProfileName(profile) 438 + ", connectionPolicy=" + connectionPolicy); 439 return connectionPolicy; 440 } 441 } 442 443 /** 444 * Set the A2DP optional coedc support value 445 * 446 * @param device {@link BluetoothDevice} wish to set 447 * @param newValue the new A2DP optional coedc support value, one of 448 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN}, 449 * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED}, 450 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED} 451 */ 452 @VisibleForTesting setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue)453 public void setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue) { 454 synchronized (mMetadataCache) { 455 if (device == null) { 456 Log.e(TAG, "setA2dpOptionalCodec: device is null"); 457 return; 458 } 459 if (newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN 460 && newValue != BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED 461 && newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) { 462 Log.e(TAG, "setA2dpSupportsOptionalCodecs: invalid value " + newValue); 463 return; 464 } 465 466 String address = device.getAddress(); 467 468 if (!mMetadataCache.containsKey(address)) { 469 return; 470 } 471 Metadata data = mMetadataCache.get(address); 472 int oldValue = data.a2dpSupportsOptionalCodecs; 473 if (oldValue == newValue) { 474 return; 475 } 476 logMetadataChange(address, "Supports optional codec changed: " 477 + oldValue + " -> " + newValue); 478 479 data.a2dpSupportsOptionalCodecs = newValue; 480 updateDatabase(data); 481 } 482 } 483 484 /** 485 * Get the A2DP optional coedc support value 486 * 487 * @param device {@link BluetoothDevice} wish to get 488 * @return the A2DP optional coedc support value, one of 489 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN}, 490 * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED}, 491 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED}, 492 */ 493 @VisibleForTesting 494 @OptionalCodecsSupportStatus getA2dpSupportsOptionalCodecs(BluetoothDevice device)495 public int getA2dpSupportsOptionalCodecs(BluetoothDevice device) { 496 synchronized (mMetadataCache) { 497 if (device == null) { 498 Log.e(TAG, "setA2dpOptionalCodec: device is null"); 499 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 500 } 501 502 String address = device.getAddress(); 503 504 if (!mMetadataCache.containsKey(address)) { 505 Log.d(TAG, "getA2dpOptionalCodec: device " + address + " is not in cache"); 506 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 507 } 508 509 Metadata data = mMetadataCache.get(address); 510 return data.a2dpSupportsOptionalCodecs; 511 } 512 } 513 514 /** 515 * Set the A2DP optional coedc enabled value 516 * 517 * @param device {@link BluetoothDevice} wish to set 518 * @param newValue the new A2DP optional coedc enabled value, one of 519 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN}, 520 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED}, 521 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED} 522 */ 523 @VisibleForTesting setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue)524 public void setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue) { 525 synchronized (mMetadataCache) { 526 if (device == null) { 527 Log.e(TAG, "setA2dpOptionalCodecEnabled: device is null"); 528 return; 529 } 530 if (newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN 531 && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED 532 && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 533 Log.e(TAG, "setA2dpOptionalCodecsEnabled: invalid value " + newValue); 534 return; 535 } 536 537 String address = device.getAddress(); 538 539 if (!mMetadataCache.containsKey(address)) { 540 return; 541 } 542 Metadata data = mMetadataCache.get(address); 543 int oldValue = data.a2dpOptionalCodecsEnabled; 544 if (oldValue == newValue) { 545 return; 546 } 547 logMetadataChange(address, "Enable optional codec changed: " 548 + oldValue + " -> " + newValue); 549 550 data.a2dpOptionalCodecsEnabled = newValue; 551 updateDatabase(data); 552 } 553 } 554 555 /** 556 * Get the A2DP optional coedc enabled value 557 * 558 * @param device {@link BluetoothDevice} wish to get 559 * @return the A2DP optional coedc enabled value, one of 560 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN}, 561 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED}, 562 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED} 563 */ 564 @VisibleForTesting 565 @OptionalCodecsPreferenceStatus getA2dpOptionalCodecsEnabled(BluetoothDevice device)566 public int getA2dpOptionalCodecsEnabled(BluetoothDevice device) { 567 synchronized (mMetadataCache) { 568 if (device == null) { 569 Log.e(TAG, "getA2dpOptionalCodecEnabled: device is null"); 570 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 571 } 572 573 String address = device.getAddress(); 574 575 if (!mMetadataCache.containsKey(address)) { 576 Log.d(TAG, "getA2dpOptionalCodecEnabled: device " + address + " is not in cache"); 577 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 578 } 579 580 Metadata data = mMetadataCache.get(address); 581 return data.a2dpOptionalCodecsEnabled; 582 } 583 } 584 585 /** 586 * Updates the time this device was last connected 587 * 588 * @param device is the remote bluetooth device for which we are setting the connection time 589 */ setConnection(BluetoothDevice device, boolean isA2dpDevice)590 public void setConnection(BluetoothDevice device, boolean isA2dpDevice) { 591 synchronized (mMetadataCache) { 592 Log.d(TAG, "setConnection: device " + device.getAnonymizedAddress() 593 + " and isA2dpDevice=" + isA2dpDevice); 594 if (device == null) { 595 Log.e(TAG, "setConnection: device is null"); 596 return; 597 } 598 599 if (isA2dpDevice) { 600 resetActiveA2dpDevice(); 601 } 602 603 String address = device.getAddress(); 604 605 if (!mMetadataCache.containsKey(address)) { 606 Log.d(TAG, "setConnection: Creating new metadata entry for device: " + device); 607 createMetadata(address, isA2dpDevice); 608 return; 609 } 610 // Updates last_active_time to the current counter value and increments the counter 611 Metadata metadata = mMetadataCache.get(address); 612 metadata.last_active_time = MetadataDatabase.sCurrentConnectionNumber++; 613 614 // Only update is_active_a2dp_device if an a2dp device is connected 615 if (isA2dpDevice) { 616 metadata.is_active_a2dp_device = true; 617 } 618 619 Log.d(TAG, "Updating last connected time for device: " + device.getAnonymizedAddress() 620 + " to " + metadata.last_active_time); 621 updateDatabase(metadata); 622 } 623 } 624 625 /** 626 * Sets is_active_device to false if currently true for device 627 * 628 * @param device is the remote bluetooth device with which we have disconnected a2dp 629 */ setDisconnection(BluetoothDevice device)630 public void setDisconnection(BluetoothDevice device) { 631 synchronized (mMetadataCache) { 632 if (device == null) { 633 Log.e(TAG, "setDisconnection: device is null"); 634 return; 635 } 636 637 String address = device.getAddress(); 638 639 if (!mMetadataCache.containsKey(address)) { 640 return; 641 } 642 // Updates last connected time to either current time if connected or -1 if disconnected 643 Metadata metadata = mMetadataCache.get(address); 644 if (metadata.is_active_a2dp_device) { 645 metadata.is_active_a2dp_device = false; 646 Log.d(TAG, "setDisconnection: Updating is_active_device to false for device: " 647 + device); 648 updateDatabase(metadata); 649 } 650 } 651 } 652 653 /** 654 * Remove a2dpActiveDevice from the current active device in the connection order table 655 */ resetActiveA2dpDevice()656 private void resetActiveA2dpDevice() { 657 synchronized (mMetadataCache) { 658 Log.d(TAG, "resetActiveA2dpDevice()"); 659 for (Map.Entry<String, Metadata> entry : mMetadataCache.entrySet()) { 660 Metadata metadata = entry.getValue(); 661 if (metadata.is_active_a2dp_device) { 662 Log.d(TAG, "resetActiveA2dpDevice"); 663 metadata.is_active_a2dp_device = false; 664 updateDatabase(metadata); 665 } 666 } 667 } 668 } 669 670 /** 671 * Gets the most recently connected bluetooth devices in order with most recently connected 672 * first and least recently connected last 673 * 674 * @return a {@link List} of {@link BluetoothDevice} representing connected bluetooth devices 675 * in order of most recently connected 676 */ getMostRecentlyConnectedDevices()677 public List<BluetoothDevice> getMostRecentlyConnectedDevices() { 678 List<BluetoothDevice> mostRecentlyConnectedDevices = new ArrayList<>(); 679 synchronized (mMetadataCache) { 680 List<Metadata> sortedMetadata = new ArrayList<>(mMetadataCache.values()); 681 sortedMetadata.sort((o1, o2) -> Long.compare(o2.last_active_time, o1.last_active_time)); 682 for (Metadata metadata : sortedMetadata) { 683 try { 684 mostRecentlyConnectedDevices.add(BluetoothAdapter.getDefaultAdapter() 685 .getRemoteDevice(metadata.getAddress())); 686 } catch (IllegalArgumentException ex) { 687 Log.d(TAG, "getBondedDevicesOrdered: Invalid address for " 688 + "device " + metadata.getAddress()); 689 } 690 } 691 } 692 return mostRecentlyConnectedDevices; 693 } 694 695 /** 696 * Gets the most recently connected bluetooth device in a given list. 697 * 698 * @param devicesList the list of {@link BluetoothDevice} to search in 699 * @return the most recently connected {@link BluetoothDevice} in the given 700 * {@code devicesList}, or null if an error occurred 701 * 702 * @hide 703 */ getMostRecentlyConnectedDevicesInList( List<BluetoothDevice> devicesList)704 public BluetoothDevice getMostRecentlyConnectedDevicesInList( 705 List<BluetoothDevice> devicesList) { 706 if (devicesList == null) { 707 return null; 708 } 709 710 BluetoothDevice mostRecentDevice = null; 711 long mostRecentLastActiveTime = -1; 712 synchronized (mMetadataCache) { 713 for (BluetoothDevice device : devicesList) { 714 String address = device.getAddress(); 715 Metadata metadata = mMetadataCache.get(address); 716 if (metadata != null && (mostRecentLastActiveTime == -1 717 || mostRecentLastActiveTime < metadata.last_active_time)) { 718 mostRecentLastActiveTime = metadata.last_active_time; 719 mostRecentDevice = device; 720 } 721 722 } 723 } 724 return mostRecentDevice; 725 } 726 727 /** 728 * Gets the last active a2dp device 729 * 730 * @return the most recently active a2dp device or null if the last a2dp device was null 731 */ getMostRecentlyConnectedA2dpDevice()732 public BluetoothDevice getMostRecentlyConnectedA2dpDevice() { 733 synchronized (mMetadataCache) { 734 for (Map.Entry<String, Metadata> entry : mMetadataCache.entrySet()) { 735 Metadata metadata = entry.getValue(); 736 if (metadata.is_active_a2dp_device) { 737 try { 738 return BluetoothAdapter.getDefaultAdapter().getRemoteDevice( 739 metadata.getAddress()); 740 } catch (IllegalArgumentException ex) { 741 Log.d(TAG, "getMostRecentlyConnectedA2dpDevice: Invalid address for " 742 + "device " + metadata.getAddress()); 743 } 744 } 745 } 746 } 747 return null; 748 } 749 750 /** 751 * 752 * @param metadataList is the list of metadata 753 */ compactLastConnectionTime(List<Metadata> metadataList)754 private void compactLastConnectionTime(List<Metadata> metadataList) { 755 Log.d(TAG, "compactLastConnectionTime: Compacting metadata after load"); 756 MetadataDatabase.sCurrentConnectionNumber = 0; 757 // Have to go in reverse order as list is ordered by descending last_active_time 758 for (int index = metadataList.size() - 1; index >= 0; index--) { 759 Metadata metadata = metadataList.get(index); 760 if (metadata.last_active_time != MetadataDatabase.sCurrentConnectionNumber) { 761 Log.d(TAG, "compactLastConnectionTime: Setting last_active_item for device: " 762 + metadata.getAnonymizedAddress() + " from " + metadata.last_active_time 763 + " to " + MetadataDatabase.sCurrentConnectionNumber); 764 metadata.last_active_time = MetadataDatabase.sCurrentConnectionNumber; 765 updateDatabase(metadata); 766 MetadataDatabase.sCurrentConnectionNumber++; 767 } 768 } 769 } 770 771 /** 772 * Get the {@link Looper} for the handler thread. This is used in testing and helper 773 * objects 774 * 775 * @return {@link Looper} for the handler thread 776 */ 777 @VisibleForTesting getHandlerLooper()778 public Looper getHandlerLooper() { 779 if (mHandlerThread == null) { 780 return null; 781 } 782 return mHandlerThread.getLooper(); 783 } 784 785 /** 786 * Start and initialize the DatabaseManager 787 * 788 * @param database the Bluetooth storage {@link MetadataDatabase} 789 */ start(MetadataDatabase database)790 public void start(MetadataDatabase database) { 791 Log.d(TAG, "start()"); 792 if (mAdapterService == null) { 793 Log.e(TAG, "stat failed, mAdapterService is null."); 794 return; 795 } 796 797 if (database == null) { 798 Log.e(TAG, "stat failed, database is null."); 799 return; 800 } 801 802 mDatabase = database; 803 804 mHandlerThread = new HandlerThread("BluetoothDatabaseManager"); 805 mHandlerThread.start(); 806 mHandler = new DatabaseHandler(mHandlerThread.getLooper()); 807 808 IntentFilter filter = new IntentFilter(); 809 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 810 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 811 mAdapterService.registerReceiver(mReceiver, filter); 812 813 loadDatabase(); 814 } 815 getDatabaseAbsolutePath()816 String getDatabaseAbsolutePath() { 817 //TODO backup database when Bluetooth turn off and FOTA? 818 return mAdapterService.getDatabasePath(MetadataDatabase.DATABASE_NAME) 819 .getAbsolutePath(); 820 } 821 822 /** 823 * Clear all persistence data in database 824 */ factoryReset()825 public void factoryReset() { 826 Log.w(TAG, "factoryReset"); 827 Message message = mHandler.obtainMessage(MSG_CLEAR_DATABASE); 828 mHandler.sendMessage(message); 829 } 830 831 /** 832 * Close and de-init the DatabaseManager 833 */ cleanup()834 public void cleanup() { 835 removeUnusedMetadata(); 836 mAdapterService.unregisterReceiver(mReceiver); 837 if (mHandlerThread != null) { 838 mHandlerThread.quit(); 839 mHandlerThread = null; 840 } 841 mMetadataCache.clear(); 842 } 843 createMetadata(String address, boolean isActiveA2dpDevice)844 void createMetadata(String address, boolean isActiveA2dpDevice) { 845 Metadata data = new Metadata(address); 846 data.is_active_a2dp_device = isActiveA2dpDevice; 847 mMetadataCache.put(address, data); 848 updateDatabase(data); 849 logMetadataChange(address, "Metadata created"); 850 } 851 852 @VisibleForTesting removeUnusedMetadata()853 void removeUnusedMetadata() { 854 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 855 synchronized (mMetadataCache) { 856 mMetadataCache.forEach((address, metadata) -> { 857 if (!address.equals(LOCAL_STORAGE) 858 && !Arrays.asList(bondedDevices).stream().anyMatch(device -> 859 address.equals(device.getAddress()))) { 860 List<Integer> list = metadata.getChangedCustomizedMeta(); 861 for (int key : list) { 862 mAdapterService.metadataChanged(address, key, null); 863 } 864 Log.i(TAG, "remove unpaired device from database " + address); 865 deleteDatabase(mMetadataCache.get(address)); 866 } 867 }); 868 } 869 } 870 cacheMetadata(List<Metadata> list)871 void cacheMetadata(List<Metadata> list) { 872 synchronized (mMetadataCache) { 873 Log.i(TAG, "cacheMetadata"); 874 // Unlock the main thread. 875 mSemaphore.release(); 876 877 if (!isMigrated(list)) { 878 // Wait for data migrate from Settings Global 879 mMigratedFromSettingsGlobal = false; 880 return; 881 } 882 mMigratedFromSettingsGlobal = true; 883 for (Metadata data : list) { 884 String address = data.getAddress(); 885 Log.v(TAG, "cacheMetadata: found device " + data.getAnonymizedAddress()); 886 mMetadataCache.put(address, data); 887 } 888 Log.i(TAG, "cacheMetadata: Database is ready"); 889 } 890 } 891 isMigrated(List<Metadata> list)892 boolean isMigrated(List<Metadata> list) { 893 for (Metadata data : list) { 894 String address = data.getAddress(); 895 if (address.equals(LOCAL_STORAGE) && data.migrated) { 896 return true; 897 } 898 } 899 return false; 900 } 901 migrateSettingsGlobal()902 void migrateSettingsGlobal() { 903 Log.i(TAG, "migrateSettingGlobal"); 904 905 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 906 ContentResolver contentResolver = mAdapterService.getContentResolver(); 907 908 for (BluetoothDevice device : bondedDevices) { 909 int a2dpConnectionPolicy = Settings.Global.getInt(contentResolver, 910 getLegacyA2dpSinkPriorityKey(device.getAddress()), 911 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 912 int a2dpSinkConnectionPolicy = Settings.Global.getInt(contentResolver, 913 getLegacyA2dpSrcPriorityKey(device.getAddress()), 914 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 915 int hearingaidConnectionPolicy = Settings.Global.getInt(contentResolver, 916 getLegacyHearingAidPriorityKey(device.getAddress()), 917 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 918 int headsetConnectionPolicy = Settings.Global.getInt(contentResolver, 919 getLegacyHeadsetPriorityKey(device.getAddress()), 920 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 921 int headsetClientConnectionPolicy = Settings.Global.getInt(contentResolver, 922 getLegacyHeadsetPriorityKey(device.getAddress()), 923 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 924 int hidHostConnectionPolicy = Settings.Global.getInt(contentResolver, 925 getLegacyHidHostPriorityKey(device.getAddress()), 926 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 927 int mapConnectionPolicy = Settings.Global.getInt(contentResolver, 928 getLegacyMapPriorityKey(device.getAddress()), 929 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 930 int mapClientConnectionPolicy = Settings.Global.getInt(contentResolver, 931 getLegacyMapClientPriorityKey(device.getAddress()), 932 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 933 int panConnectionPolicy = Settings.Global.getInt(contentResolver, 934 getLegacyPanPriorityKey(device.getAddress()), 935 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 936 int pbapConnectionPolicy = Settings.Global.getInt(contentResolver, 937 getLegacyPbapClientPriorityKey(device.getAddress()), 938 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 939 int pbapClientConnectionPolicy = Settings.Global.getInt(contentResolver, 940 getLegacyPbapClientPriorityKey(device.getAddress()), 941 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 942 int sapConnectionPolicy = Settings.Global.getInt(contentResolver, 943 getLegacySapPriorityKey(device.getAddress()), 944 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 945 int a2dpSupportsOptionalCodec = Settings.Global.getInt(contentResolver, 946 getLegacyA2dpSupportsOptionalCodecsKey(device.getAddress()), 947 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN); 948 int a2dpOptionalCodecEnabled = Settings.Global.getInt(contentResolver, 949 getLegacyA2dpOptionalCodecsEnabledKey(device.getAddress()), 950 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN); 951 952 String address = device.getAddress(); 953 Metadata data = new Metadata(address); 954 data.setProfileConnectionPolicy(BluetoothProfile.A2DP, a2dpConnectionPolicy); 955 data.setProfileConnectionPolicy(BluetoothProfile.A2DP_SINK, a2dpSinkConnectionPolicy); 956 data.setProfileConnectionPolicy(BluetoothProfile.HEADSET, headsetConnectionPolicy); 957 data.setProfileConnectionPolicy(BluetoothProfile.HEADSET_CLIENT, 958 headsetClientConnectionPolicy); 959 data.setProfileConnectionPolicy(BluetoothProfile.HID_HOST, hidHostConnectionPolicy); 960 data.setProfileConnectionPolicy(BluetoothProfile.PAN, panConnectionPolicy); 961 data.setProfileConnectionPolicy(BluetoothProfile.PBAP, pbapConnectionPolicy); 962 data.setProfileConnectionPolicy(BluetoothProfile.PBAP_CLIENT, 963 pbapClientConnectionPolicy); 964 data.setProfileConnectionPolicy(BluetoothProfile.MAP, mapConnectionPolicy); 965 data.setProfileConnectionPolicy(BluetoothProfile.MAP_CLIENT, mapClientConnectionPolicy); 966 data.setProfileConnectionPolicy(BluetoothProfile.SAP, sapConnectionPolicy); 967 data.setProfileConnectionPolicy(BluetoothProfile.HEARING_AID, 968 hearingaidConnectionPolicy); 969 data.setProfileConnectionPolicy(BluetoothProfile.LE_AUDIO, 970 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 971 data.a2dpSupportsOptionalCodecs = a2dpSupportsOptionalCodec; 972 data.a2dpOptionalCodecsEnabled = a2dpOptionalCodecEnabled; 973 mMetadataCache.put(address, data); 974 updateDatabase(data); 975 } 976 977 // Mark database migrated from Settings Global 978 Metadata localData = new Metadata(LOCAL_STORAGE); 979 localData.migrated = true; 980 mMetadataCache.put(LOCAL_STORAGE, localData); 981 updateDatabase(localData); 982 983 // Reload database after migration is completed 984 loadDatabase(); 985 986 } 987 988 /** 989 * Get the key that retrieves a bluetooth headset's priority. 990 */ getLegacyHeadsetPriorityKey(String address)991 private static String getLegacyHeadsetPriorityKey(String address) { 992 return LEGACY_HEADSET_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 993 } 994 995 /** 996 * Get the key that retrieves a bluetooth a2dp sink's priority. 997 */ getLegacyA2dpSinkPriorityKey(String address)998 private static String getLegacyA2dpSinkPriorityKey(String address) { 999 return LEGACY_A2DP_SINK_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1000 } 1001 1002 /** 1003 * Get the key that retrieves a bluetooth a2dp src's priority. 1004 */ getLegacyA2dpSrcPriorityKey(String address)1005 private static String getLegacyA2dpSrcPriorityKey(String address) { 1006 return LEGACY_A2DP_SRC_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1007 } 1008 1009 /** 1010 * Get the key that retrieves a bluetooth a2dp device's ability to support optional codecs. 1011 */ getLegacyA2dpSupportsOptionalCodecsKey(String address)1012 private static String getLegacyA2dpSupportsOptionalCodecsKey(String address) { 1013 return LEGACY_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX 1014 + address.toUpperCase(Locale.ROOT); 1015 } 1016 1017 /** 1018 * Get the key that retrieves whether a bluetooth a2dp device should have optional codecs 1019 * enabled. 1020 */ getLegacyA2dpOptionalCodecsEnabledKey(String address)1021 private static String getLegacyA2dpOptionalCodecsEnabledKey(String address) { 1022 return LEGACY_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX 1023 + address.toUpperCase(Locale.ROOT); 1024 } 1025 1026 /** 1027 * Get the key that retrieves a bluetooth Input Device's priority. 1028 */ getLegacyHidHostPriorityKey(String address)1029 private static String getLegacyHidHostPriorityKey(String address) { 1030 return LEGACY_INPUT_DEVICE_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1031 } 1032 1033 /** 1034 * Get the key that retrieves a bluetooth pan client priority. 1035 */ getLegacyPanPriorityKey(String address)1036 private static String getLegacyPanPriorityKey(String address) { 1037 return LEGACY_PAN_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1038 } 1039 1040 /** 1041 * Get the key that retrieves a bluetooth hearing aid priority. 1042 */ getLegacyHearingAidPriorityKey(String address)1043 private static String getLegacyHearingAidPriorityKey(String address) { 1044 return LEGACY_HEARING_AID_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1045 } 1046 1047 /** 1048 * Get the key that retrieves a bluetooth map priority. 1049 */ getLegacyMapPriorityKey(String address)1050 private static String getLegacyMapPriorityKey(String address) { 1051 return LEGACY_MAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1052 } 1053 1054 /** 1055 * Get the key that retrieves a bluetooth map client priority. 1056 */ getLegacyMapClientPriorityKey(String address)1057 private static String getLegacyMapClientPriorityKey(String address) { 1058 return LEGACY_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1059 } 1060 1061 /** 1062 * Get the key that retrieves a bluetooth pbap client priority. 1063 */ getLegacyPbapClientPriorityKey(String address)1064 private static String getLegacyPbapClientPriorityKey(String address) { 1065 return LEGACY_PBAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1066 } 1067 1068 /** 1069 * Get the key that retrieves a bluetooth sap priority. 1070 */ getLegacySapPriorityKey(String address)1071 private static String getLegacySapPriorityKey(String address) { 1072 return LEGACY_SAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); 1073 } 1074 loadDatabase()1075 private void loadDatabase() { 1076 Log.d(TAG, "Load Database"); 1077 Message message = mHandler.obtainMessage(MSG_LOAD_DATABASE); 1078 mHandler.sendMessage(message); 1079 try { 1080 // Lock the thread until handler thread finish loading database. 1081 mSemaphore.tryAcquire(LOAD_DATABASE_TIMEOUT, TimeUnit.MILLISECONDS); 1082 } catch (InterruptedException e) { 1083 Log.e(TAG, "loadDatabase: semaphore acquire failed"); 1084 } 1085 } 1086 updateDatabase(Metadata data)1087 private void updateDatabase(Metadata data) { 1088 if (data.getAddress() == null) { 1089 Log.e(TAG, "updateDatabase: address is null"); 1090 return; 1091 } 1092 Log.d(TAG, "updateDatabase " + data.getAnonymizedAddress()); 1093 Message message = mHandler.obtainMessage(MSG_UPDATE_DATABASE); 1094 message.obj = data; 1095 mHandler.sendMessage(message); 1096 } 1097 1098 @VisibleForTesting deleteDatabase(Metadata data)1099 void deleteDatabase(Metadata data) { 1100 String address = data.getAddress(); 1101 if (address == null) { 1102 Log.e(TAG, "deleteDatabase: address is null"); 1103 return; 1104 } 1105 logMetadataChange(address, "Metadata deleted"); 1106 Message message = mHandler.obtainMessage(MSG_DELETE_DATABASE); 1107 message.obj = data.getAddress(); 1108 mHandler.sendMessage(message); 1109 } 1110 logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue)1111 private void logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue) { 1112 String callingApp = mAdapterService.getPackageManager().getNameForUid( 1113 Binder.getCallingUid()); 1114 String manufacturerName = ""; 1115 String modelName = ""; 1116 String hardwareVersion = ""; 1117 String softwareVersion = ""; 1118 String value = Utils.byteArrayToUtf8String(bytesValue); 1119 switch (key) { 1120 case BluetoothDevice.METADATA_MANUFACTURER_NAME: 1121 manufacturerName = value; 1122 break; 1123 case BluetoothDevice.METADATA_MODEL_NAME: 1124 modelName = value; 1125 break; 1126 case BluetoothDevice.METADATA_HARDWARE_VERSION: 1127 hardwareVersion = value; 1128 break; 1129 case BluetoothDevice.METADATA_SOFTWARE_VERSION: 1130 softwareVersion = value; 1131 break; 1132 default: 1133 // Do not log anything if metadata doesn't fall into above categories 1134 return; 1135 } 1136 String[] macAddress = device.getAddress().split(":"); 1137 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, 1138 mAdapterService.obfuscateAddress(device), 1139 BluetoothProtoEnums.DEVICE_INFO_EXTERNAL, callingApp, manufacturerName, modelName, 1140 hardwareVersion, softwareVersion, mAdapterService.getMetricId(device), 1141 device.getAddressType(), 1142 Integer.parseInt(macAddress[0], 16), 1143 Integer.parseInt(macAddress[1], 16), 1144 Integer.parseInt(macAddress[2], 16)); 1145 } 1146 logMetadataChange(String address, String log)1147 private void logMetadataChange(String address, String log) { 1148 String time = Utils.getLocalTimeString(); 1149 String uidPid = Utils.getUidPidString(); 1150 mMetadataChangedLog.add(time + " (" + uidPid + ") " + address + " " + log); 1151 } 1152 1153 /** 1154 * Dump database info to a PrintWriter 1155 * 1156 * @param writer the PrintWriter to write log 1157 */ dump(PrintWriter writer)1158 public void dump(PrintWriter writer) { 1159 writer.println("\nBluetoothDatabase:"); 1160 writer.println(" Metadata Changes:"); 1161 for (String log : mMetadataChangedLog) { 1162 writer.println(" " + log); 1163 } 1164 writer.println("\nMetadata:"); 1165 for (HashMap.Entry<String, Metadata> entry : mMetadataCache.entrySet()) { 1166 if (entry.getKey().equals(LOCAL_STORAGE)) { 1167 // No need to dump local storage 1168 continue; 1169 } 1170 writer.println(" " + entry.getValue()); 1171 } 1172 } 1173 } 1174