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.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.BluetoothProtoEnums; 24 import android.content.BroadcastReceiver; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.os.Binder; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.provider.Settings; 35 import android.util.Log; 36 import android.util.StatsLog; 37 38 import com.android.bluetooth.Utils; 39 import com.android.bluetooth.btservice.AdapterService; 40 import com.android.internal.annotations.VisibleForTesting; 41 42 import java.util.Arrays; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Objects; 47 import java.util.concurrent.Semaphore; 48 import java.util.concurrent.TimeUnit; 49 50 /** 51 * The active device manager is responsible to handle a Room database 52 * for Bluetooth persistent data. 53 */ 54 public class DatabaseManager { 55 private static final boolean DBG = true; 56 private static final boolean VERBOSE = true; 57 private static final String TAG = "BluetoothDatabase"; 58 59 private AdapterService mAdapterService = null; 60 private HandlerThread mHandlerThread = null; 61 private Handler mHandler = null; 62 private MetadataDatabase mDatabase = null; 63 private boolean mMigratedFromSettingsGlobal = false; 64 65 @VisibleForTesting 66 final Map<String, Metadata> mMetadataCache = new HashMap<>(); 67 private final Semaphore mSemaphore = new Semaphore(1); 68 69 private static final int LOAD_DATABASE_TIMEOUT = 500; // milliseconds 70 private static final int MSG_LOAD_DATABASE = 0; 71 private static final int MSG_UPDATE_DATABASE = 1; 72 private static final int MSG_DELETE_DATABASE = 2; 73 private static final int MSG_CLEAR_DATABASE = 100; 74 private static final String LOCAL_STORAGE = "LocalStorage"; 75 76 /** 77 * Constructor of the DatabaseManager 78 */ DatabaseManager(AdapterService service)79 public DatabaseManager(AdapterService service) { 80 mAdapterService = service; 81 } 82 83 class DatabaseHandler extends Handler { DatabaseHandler(Looper looper)84 DatabaseHandler(Looper looper) { 85 super(looper); 86 } 87 88 @Override handleMessage(Message msg)89 public void handleMessage(Message msg) { 90 switch (msg.what) { 91 case MSG_LOAD_DATABASE: { 92 synchronized (mDatabase) { 93 List<Metadata> list; 94 try { 95 list = mDatabase.load(); 96 } catch (IllegalStateException e) { 97 Log.e(TAG, "Unable to open database: " + e); 98 mDatabase = MetadataDatabase 99 .createDatabaseWithoutMigration(mAdapterService); 100 list = mDatabase.load(); 101 } 102 cacheMetadata(list); 103 } 104 break; 105 } 106 case MSG_UPDATE_DATABASE: { 107 Metadata data = (Metadata) msg.obj; 108 synchronized (mDatabase) { 109 mDatabase.insert(data); 110 } 111 break; 112 } 113 case MSG_DELETE_DATABASE: { 114 String address = (String) msg.obj; 115 synchronized (mDatabase) { 116 mDatabase.delete(address); 117 } 118 break; 119 } 120 case MSG_CLEAR_DATABASE: { 121 synchronized (mDatabase) { 122 mDatabase.deleteAll(); 123 } 124 break; 125 } 126 } 127 } 128 } 129 130 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 131 @Override 132 public void onReceive(Context context, Intent intent) { 133 String action = intent.getAction(); 134 if (action == null) { 135 Log.e(TAG, "Received intent with null action"); 136 return; 137 } 138 switch (action) { 139 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { 140 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 141 BluetoothDevice.ERROR); 142 BluetoothDevice device = 143 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 144 Objects.requireNonNull(device, 145 "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 146 bondStateChanged(device, state); 147 break; 148 } 149 case BluetoothAdapter.ACTION_STATE_CHANGED: { 150 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 151 BluetoothAdapter.STATE_OFF); 152 if (!mMigratedFromSettingsGlobal 153 && state == BluetoothAdapter.STATE_TURNING_ON) { 154 migrateSettingsGlobal(); 155 } 156 break; 157 } 158 } 159 } 160 }; 161 bondStateChanged(BluetoothDevice device, int state)162 void bondStateChanged(BluetoothDevice device, int state) { 163 synchronized (mMetadataCache) { 164 String address = device.getAddress(); 165 if (state != BluetoothDevice.BOND_NONE) { 166 if (mMetadataCache.containsKey(address)) { 167 return; 168 } 169 createMetadata(address); 170 } else { 171 Metadata metadata = mMetadataCache.get(address); 172 if (metadata != null) { 173 mMetadataCache.remove(address); 174 deleteDatabase(metadata); 175 } 176 } 177 } 178 } 179 isValidMetaKey(int key)180 boolean isValidMetaKey(int key) { 181 switch (key) { 182 case BluetoothDevice.METADATA_MANUFACTURER_NAME: 183 case BluetoothDevice.METADATA_MODEL_NAME: 184 case BluetoothDevice.METADATA_SOFTWARE_VERSION: 185 case BluetoothDevice.METADATA_HARDWARE_VERSION: 186 case BluetoothDevice.METADATA_COMPANION_APP: 187 case BluetoothDevice.METADATA_MAIN_ICON: 188 case BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET: 189 case BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON: 190 case BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON: 191 case BluetoothDevice.METADATA_UNTETHERED_CASE_ICON: 192 case BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY: 193 case BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY: 194 case BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY: 195 case BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING: 196 case BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING: 197 case BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING: 198 case BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI: 199 return true; 200 } 201 Log.w(TAG, "Invalid metadata key " + key); 202 return false; 203 } 204 205 /** 206 * Set customized metadata to database with requested key 207 */ 208 @VisibleForTesting setCustomMeta(BluetoothDevice device, int key, byte[] newValue)209 public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) { 210 synchronized (mMetadataCache) { 211 if (device == null) { 212 Log.e(TAG, "setCustomMeta: device is null"); 213 return false; 214 } 215 if (!isValidMetaKey(key)) { 216 Log.e(TAG, "setCustomMeta: meta key invalid " + key); 217 return false; 218 } 219 220 String address = device.getAddress(); 221 if (VERBOSE) { 222 Log.d(TAG, "setCustomMeta: " + address + ", key=" + key); 223 } 224 if (!mMetadataCache.containsKey(address)) { 225 createMetadata(address); 226 } 227 Metadata data = mMetadataCache.get(address); 228 byte[] oldValue = data.getCustomizedMeta(key); 229 if (oldValue != null && Arrays.equals(oldValue, newValue)) { 230 if (VERBOSE) { 231 Log.d(TAG, "setCustomMeta: metadata not changed."); 232 } 233 return true; 234 } 235 logManufacturerInfo(device, key, newValue); 236 data.setCustomizedMeta(key, newValue); 237 238 updateDatabase(data); 239 mAdapterService.metadataChanged(address, key, newValue); 240 return true; 241 } 242 } 243 244 /** 245 * Get customized metadata from database with requested key 246 */ 247 @VisibleForTesting getCustomMeta(BluetoothDevice device, int key)248 public byte[] getCustomMeta(BluetoothDevice device, int key) { 249 synchronized (mMetadataCache) { 250 if (device == null) { 251 Log.e(TAG, "getCustomMeta: device is null"); 252 return null; 253 } 254 if (!isValidMetaKey(key)) { 255 Log.e(TAG, "getCustomMeta: meta key invalid " + key); 256 return null; 257 } 258 259 String address = device.getAddress(); 260 261 if (!mMetadataCache.containsKey(address)) { 262 Log.e(TAG, "getCustomMeta: device " + address + " is not in cache"); 263 return null; 264 } 265 266 Metadata data = mMetadataCache.get(address); 267 return data.getCustomizedMeta(key); 268 } 269 } 270 271 /** 272 * Set the device profile prioirty 273 * 274 * @param device {@link BluetoothDevice} wish to set 275 * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET}, 276 * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP}, 277 * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST}, 278 * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP}, 279 * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP}, 280 * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP}, 281 * {@link BluetoothProfile#HEARING_AID} 282 * @param newPriority the priority to set; one of 283 * {@link BluetoothProfile#PRIORITY_UNDEFINED}, 284 * {@link BluetoothProfile#PRIORITY_OFF}, 285 * {@link BluetoothProfile#PRIORITY_ON}, 286 * {@link BluetoothProfile#PRIORITY_AUTO_CONNECT} 287 */ 288 @VisibleForTesting setProfilePriority(BluetoothDevice device, int profile, int newPriority)289 public boolean setProfilePriority(BluetoothDevice device, int profile, int newPriority) { 290 synchronized (mMetadataCache) { 291 if (device == null) { 292 Log.e(TAG, "setProfilePriority: device is null"); 293 return false; 294 } 295 296 if (newPriority != BluetoothProfile.PRIORITY_UNDEFINED 297 && newPriority != BluetoothProfile.PRIORITY_OFF 298 && newPriority != BluetoothProfile.PRIORITY_ON 299 && newPriority != BluetoothProfile.PRIORITY_AUTO_CONNECT) { 300 Log.e(TAG, "setProfilePriority: invalid priority " + newPriority); 301 return false; 302 } 303 304 String address = device.getAddress(); 305 if (VERBOSE) { 306 Log.v(TAG, "setProfilePriority: " + address + ", profile=" + profile 307 + ", priority = " + newPriority); 308 } 309 if (!mMetadataCache.containsKey(address)) { 310 if (newPriority == BluetoothProfile.PRIORITY_UNDEFINED) { 311 return true; 312 } 313 createMetadata(address); 314 } 315 Metadata data = mMetadataCache.get(address); 316 int oldPriority = data.getProfilePriority(profile); 317 if (oldPriority == newPriority) { 318 if (VERBOSE) { 319 Log.v(TAG, "setProfilePriority priority not changed."); 320 } 321 return true; 322 } 323 324 data.setProfilePriority(profile, newPriority); 325 updateDatabase(data); 326 return true; 327 } 328 } 329 330 /** 331 * Get the device profile prioirty 332 * 333 * @param device {@link BluetoothDevice} wish to get 334 * @param profile The Bluetooth profile; one of {@link BluetoothProfile#HEADSET}, 335 * {@link BluetoothProfile#HEADSET_CLIENT}, {@link BluetoothProfile#A2DP}, 336 * {@link BluetoothProfile#A2DP_SINK}, {@link BluetoothProfile#HID_HOST}, 337 * {@link BluetoothProfile#PAN}, {@link BluetoothProfile#PBAP}, 338 * {@link BluetoothProfile#PBAP_CLIENT}, {@link BluetoothProfile#MAP}, 339 * {@link BluetoothProfile#MAP_CLIENT}, {@link BluetoothProfile#SAP}, 340 * {@link BluetoothProfile#HEARING_AID} 341 * @return the profile priority of the device; one of 342 * {@link BluetoothProfile#PRIORITY_UNDEFINED}, 343 * {@link BluetoothProfile#PRIORITY_OFF}, 344 * {@link BluetoothProfile#PRIORITY_ON}, 345 * {@link BluetoothProfile#PRIORITY_AUTO_CONNECT} 346 */ 347 @VisibleForTesting getProfilePriority(BluetoothDevice device, int profile)348 public int getProfilePriority(BluetoothDevice device, int profile) { 349 synchronized (mMetadataCache) { 350 if (device == null) { 351 Log.e(TAG, "getProfilePriority: device is null"); 352 return BluetoothProfile.PRIORITY_UNDEFINED; 353 } 354 355 String address = device.getAddress(); 356 357 if (!mMetadataCache.containsKey(address)) { 358 Log.e(TAG, "getProfilePriority: device " + address + " is not in cache"); 359 return BluetoothProfile.PRIORITY_UNDEFINED; 360 } 361 362 Metadata data = mMetadataCache.get(address); 363 int priority = data.getProfilePriority(profile); 364 if (VERBOSE) { 365 Log.v(TAG, "getProfilePriority: " + address + ", profile=" + profile 366 + ", priority = " + priority); 367 } 368 return priority; 369 } 370 } 371 372 /** 373 * Set the A2DP optional coedc support value 374 * 375 * @param device {@link BluetoothDevice} wish to set 376 * @param newValue the new A2DP optional coedc support value, one of 377 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN}, 378 * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED}, 379 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED} 380 */ 381 @VisibleForTesting setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue)382 public void setA2dpSupportsOptionalCodecs(BluetoothDevice device, int newValue) { 383 synchronized (mMetadataCache) { 384 if (device == null) { 385 Log.e(TAG, "setA2dpOptionalCodec: device is null"); 386 return; 387 } 388 if (newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN 389 && newValue != BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED 390 && newValue != BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) { 391 Log.e(TAG, "setA2dpSupportsOptionalCodecs: invalid value " + newValue); 392 return; 393 } 394 395 String address = device.getAddress(); 396 397 if (!mMetadataCache.containsKey(address)) { 398 return; 399 } 400 Metadata data = mMetadataCache.get(address); 401 int oldValue = data.a2dpSupportsOptionalCodecs; 402 if (oldValue == newValue) { 403 return; 404 } 405 406 data.a2dpSupportsOptionalCodecs = newValue; 407 updateDatabase(data); 408 } 409 } 410 411 /** 412 * Get the A2DP optional coedc support value 413 * 414 * @param device {@link BluetoothDevice} wish to get 415 * @return the A2DP optional coedc support value, one of 416 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORT_UNKNOWN}, 417 * {@link BluetoothA2dp#OPTIONAL_CODECS_NOT_SUPPORTED}, 418 * {@link BluetoothA2dp#OPTIONAL_CODECS_SUPPORTED}, 419 */ 420 @VisibleForTesting getA2dpSupportsOptionalCodecs(BluetoothDevice device)421 public int getA2dpSupportsOptionalCodecs(BluetoothDevice device) { 422 synchronized (mMetadataCache) { 423 if (device == null) { 424 Log.e(TAG, "setA2dpOptionalCodec: device is null"); 425 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 426 } 427 428 String address = device.getAddress(); 429 430 if (!mMetadataCache.containsKey(address)) { 431 Log.e(TAG, "getA2dpOptionalCodec: device " + address + " is not in cache"); 432 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 433 } 434 435 Metadata data = mMetadataCache.get(address); 436 return data.a2dpSupportsOptionalCodecs; 437 } 438 } 439 440 /** 441 * Set the A2DP optional coedc enabled value 442 * 443 * @param device {@link BluetoothDevice} wish to set 444 * @param newValue the new A2DP optional coedc enabled value, one of 445 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN}, 446 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED}, 447 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED} 448 */ 449 @VisibleForTesting setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue)450 public void setA2dpOptionalCodecsEnabled(BluetoothDevice device, int newValue) { 451 synchronized (mMetadataCache) { 452 if (device == null) { 453 Log.e(TAG, "setA2dpOptionalCodecEnabled: device is null"); 454 return; 455 } 456 if (newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN 457 && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED 458 && newValue != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 459 Log.e(TAG, "setA2dpOptionalCodecsEnabled: invalid value " + newValue); 460 return; 461 } 462 463 String address = device.getAddress(); 464 465 if (!mMetadataCache.containsKey(address)) { 466 return; 467 } 468 Metadata data = mMetadataCache.get(address); 469 int oldValue = data.a2dpOptionalCodecsEnabled; 470 if (oldValue == newValue) { 471 return; 472 } 473 474 data.a2dpOptionalCodecsEnabled = newValue; 475 updateDatabase(data); 476 } 477 } 478 479 /** 480 * Get the A2DP optional coedc enabled value 481 * 482 * @param device {@link BluetoothDevice} wish to get 483 * @return the A2DP optional coedc enabled value, one of 484 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_UNKNOWN}, 485 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_DISABLED}, 486 * {@link BluetoothA2dp#OPTIONAL_CODECS_PREF_ENABLED} 487 */ 488 @VisibleForTesting getA2dpOptionalCodecsEnabled(BluetoothDevice device)489 public int getA2dpOptionalCodecsEnabled(BluetoothDevice device) { 490 synchronized (mMetadataCache) { 491 if (device == null) { 492 Log.e(TAG, "getA2dpOptionalCodecEnabled: device is null"); 493 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 494 } 495 496 String address = device.getAddress(); 497 498 if (!mMetadataCache.containsKey(address)) { 499 Log.e(TAG, "getA2dpOptionalCodecEnabled: device " + address + " is not in cache"); 500 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 501 } 502 503 Metadata data = mMetadataCache.get(address); 504 return data.a2dpOptionalCodecsEnabled; 505 } 506 } 507 508 /** 509 * Get the {@link Looper} for the handler thread. This is used in testing and helper 510 * objects 511 * 512 * @return {@link Looper} for the handler thread 513 */ 514 @VisibleForTesting getHandlerLooper()515 public Looper getHandlerLooper() { 516 if (mHandlerThread == null) { 517 return null; 518 } 519 return mHandlerThread.getLooper(); 520 } 521 522 /** 523 * Start and initialize the DatabaseManager 524 * 525 * @param database the Bluetooth storage {@link MetadataDatabase} 526 */ start(MetadataDatabase database)527 public void start(MetadataDatabase database) { 528 if (DBG) { 529 Log.d(TAG, "start()"); 530 } 531 532 if (mAdapterService == null) { 533 Log.e(TAG, "stat failed, mAdapterService is null."); 534 return; 535 } 536 537 if (database == null) { 538 Log.e(TAG, "stat failed, database is null."); 539 return; 540 } 541 542 mDatabase = database; 543 544 mHandlerThread = new HandlerThread("BluetoothDatabaseManager"); 545 mHandlerThread.start(); 546 mHandler = new DatabaseHandler(mHandlerThread.getLooper()); 547 548 IntentFilter filter = new IntentFilter(); 549 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 550 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 551 mAdapterService.registerReceiver(mReceiver, filter); 552 553 loadDatabase(); 554 } 555 getDatabaseAbsolutePath()556 String getDatabaseAbsolutePath() { 557 //TODO backup database when Bluetooth turn off and FOTA? 558 return mAdapterService.getDatabasePath(MetadataDatabase.DATABASE_NAME) 559 .getAbsolutePath(); 560 } 561 562 /** 563 * Clear all persistence data in database 564 */ factoryReset()565 public void factoryReset() { 566 Log.w(TAG, "factoryReset"); 567 Message message = mHandler.obtainMessage(MSG_CLEAR_DATABASE); 568 mHandler.sendMessage(message); 569 } 570 571 /** 572 * Close and de-init the DatabaseManager 573 */ cleanup()574 public void cleanup() { 575 removeUnusedMetadata(); 576 mAdapterService.unregisterReceiver(mReceiver); 577 if (mHandlerThread != null) { 578 mHandlerThread.quit(); 579 mHandlerThread = null; 580 } 581 mMetadataCache.clear(); 582 } 583 createMetadata(String address)584 void createMetadata(String address) { 585 if (VERBOSE) { 586 Log.v(TAG, "createMetadata " + address); 587 } 588 Metadata data = new Metadata(address); 589 mMetadataCache.put(address, data); 590 updateDatabase(data); 591 } 592 593 @VisibleForTesting removeUnusedMetadata()594 void removeUnusedMetadata() { 595 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 596 synchronized (mMetadataCache) { 597 mMetadataCache.forEach((address, metadata) -> { 598 if (!address.equals(LOCAL_STORAGE) 599 && !Arrays.asList(bondedDevices).stream().anyMatch(device -> 600 address.equals(device.getAddress()))) { 601 List<Integer> list = metadata.getChangedCustomizedMeta(); 602 for (int key : list) { 603 mAdapterService.metadataChanged(address, key, null); 604 } 605 Log.i(TAG, "remove unpaired device from database " + address); 606 deleteDatabase(mMetadataCache.get(address)); 607 } 608 }); 609 } 610 } 611 cacheMetadata(List<Metadata> list)612 void cacheMetadata(List<Metadata> list) { 613 synchronized (mMetadataCache) { 614 Log.i(TAG, "cacheMetadata"); 615 // Unlock the main thread. 616 mSemaphore.release(); 617 618 if (!isMigrated(list)) { 619 // Wait for data migrate from Settings Global 620 mMigratedFromSettingsGlobal = false; 621 return; 622 } 623 mMigratedFromSettingsGlobal = true; 624 for (Metadata data : list) { 625 String address = data.getAddress(); 626 if (VERBOSE) { 627 Log.v(TAG, "cacheMetadata: found device " + address); 628 } 629 mMetadataCache.put(address, data); 630 } 631 if (VERBOSE) { 632 Log.v(TAG, "cacheMetadata: Database is ready"); 633 } 634 } 635 } 636 isMigrated(List<Metadata> list)637 boolean isMigrated(List<Metadata> list) { 638 for (Metadata data : list) { 639 String address = data.getAddress(); 640 if (address.equals(LOCAL_STORAGE) && data.migrated) { 641 return true; 642 } 643 } 644 return false; 645 } 646 migrateSettingsGlobal()647 void migrateSettingsGlobal() { 648 Log.i(TAG, "migrateSettingGlobal"); 649 650 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 651 ContentResolver contentResolver = mAdapterService.getContentResolver(); 652 653 for (BluetoothDevice device : bondedDevices) { 654 int a2dpPriority = Settings.Global.getInt(contentResolver, 655 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), 656 BluetoothProfile.PRIORITY_UNDEFINED); 657 int a2dpSinkPriority = Settings.Global.getInt(contentResolver, 658 Settings.Global.getBluetoothA2dpSrcPriorityKey(device.getAddress()), 659 BluetoothProfile.PRIORITY_UNDEFINED); 660 int hearingaidPriority = Settings.Global.getInt(contentResolver, 661 Settings.Global.getBluetoothHearingAidPriorityKey(device.getAddress()), 662 BluetoothProfile.PRIORITY_UNDEFINED); 663 int headsetPriority = Settings.Global.getInt(contentResolver, 664 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 665 BluetoothProfile.PRIORITY_UNDEFINED); 666 int headsetClientPriority = Settings.Global.getInt(contentResolver, 667 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 668 BluetoothProfile.PRIORITY_UNDEFINED); 669 int hidHostPriority = Settings.Global.getInt(contentResolver, 670 Settings.Global.getBluetoothHidHostPriorityKey(device.getAddress()), 671 BluetoothProfile.PRIORITY_UNDEFINED); 672 int mapPriority = Settings.Global.getInt(contentResolver, 673 Settings.Global.getBluetoothMapPriorityKey(device.getAddress()), 674 BluetoothProfile.PRIORITY_UNDEFINED); 675 int mapClientPriority = Settings.Global.getInt(contentResolver, 676 Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()), 677 BluetoothProfile.PRIORITY_UNDEFINED); 678 int panPriority = Settings.Global.getInt(contentResolver, 679 Settings.Global.getBluetoothPanPriorityKey(device.getAddress()), 680 BluetoothProfile.PRIORITY_UNDEFINED); 681 int pbapPriority = Settings.Global.getInt(contentResolver, 682 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()), 683 BluetoothProfile.PRIORITY_UNDEFINED); 684 int pbapClientPriority = Settings.Global.getInt(contentResolver, 685 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()), 686 BluetoothProfile.PRIORITY_UNDEFINED); 687 int sapPriority = Settings.Global.getInt(contentResolver, 688 Settings.Global.getBluetoothSapPriorityKey(device.getAddress()), 689 BluetoothProfile.PRIORITY_UNDEFINED); 690 int a2dpSupportsOptionalCodec = Settings.Global.getInt(contentResolver, 691 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()), 692 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN); 693 int a2dpOptionalCodecEnabled = Settings.Global.getInt(contentResolver, 694 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()), 695 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN); 696 697 String address = device.getAddress(); 698 Metadata data = new Metadata(address); 699 data.setProfilePriority(BluetoothProfile.A2DP, a2dpPriority); 700 data.setProfilePriority(BluetoothProfile.A2DP_SINK, a2dpSinkPriority); 701 data.setProfilePriority(BluetoothProfile.HEADSET, headsetPriority); 702 data.setProfilePriority(BluetoothProfile.HEADSET_CLIENT, headsetClientPriority); 703 data.setProfilePriority(BluetoothProfile.HID_HOST, hidHostPriority); 704 data.setProfilePriority(BluetoothProfile.PAN, panPriority); 705 data.setProfilePriority(BluetoothProfile.PBAP, pbapPriority); 706 data.setProfilePriority(BluetoothProfile.PBAP_CLIENT, pbapClientPriority); 707 data.setProfilePriority(BluetoothProfile.MAP, mapPriority); 708 data.setProfilePriority(BluetoothProfile.MAP_CLIENT, mapClientPriority); 709 data.setProfilePriority(BluetoothProfile.SAP, sapPriority); 710 data.setProfilePriority(BluetoothProfile.HEARING_AID, hearingaidPriority); 711 data.a2dpSupportsOptionalCodecs = a2dpSupportsOptionalCodec; 712 data.a2dpOptionalCodecsEnabled = a2dpOptionalCodecEnabled; 713 mMetadataCache.put(address, data); 714 updateDatabase(data); 715 } 716 717 // Mark database migrated from Settings Global 718 Metadata localData = new Metadata(LOCAL_STORAGE); 719 localData.migrated = true; 720 mMetadataCache.put(LOCAL_STORAGE, localData); 721 updateDatabase(localData); 722 723 // Reload database after migration is completed 724 loadDatabase(); 725 726 } 727 loadDatabase()728 private void loadDatabase() { 729 if (DBG) { 730 Log.d(TAG, "Load Database"); 731 } 732 Message message = mHandler.obtainMessage(MSG_LOAD_DATABASE); 733 mHandler.sendMessage(message); 734 try { 735 // Lock the thread until handler thread finish loading database. 736 mSemaphore.tryAcquire(LOAD_DATABASE_TIMEOUT, TimeUnit.MILLISECONDS); 737 } catch (InterruptedException e) { 738 Log.e(TAG, "loadDatabase: semaphore acquire failed"); 739 } 740 } 741 updateDatabase(Metadata data)742 private void updateDatabase(Metadata data) { 743 if (data.getAddress() == null) { 744 Log.e(TAG, "updateDatabase: address is null"); 745 return; 746 } 747 if (DBG) { 748 Log.d(TAG, "updateDatabase " + data.getAddress()); 749 } 750 Message message = mHandler.obtainMessage(MSG_UPDATE_DATABASE); 751 message.obj = data; 752 mHandler.sendMessage(message); 753 } 754 deleteDatabase(Metadata data)755 private void deleteDatabase(Metadata data) { 756 if (data.getAddress() == null) { 757 Log.e(TAG, "deleteDatabase: address is null"); 758 return; 759 } 760 Log.d(TAG, "deleteDatabase: " + data.getAddress()); 761 Message message = mHandler.obtainMessage(MSG_DELETE_DATABASE); 762 message.obj = data.getAddress(); 763 mHandler.sendMessage(message); 764 } 765 logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue)766 private void logManufacturerInfo(BluetoothDevice device, int key, byte[] bytesValue) { 767 String callingApp = mAdapterService.getPackageManager().getNameForUid( 768 Binder.getCallingUid()); 769 String manufacturerName = ""; 770 String modelName = ""; 771 String hardwareVersion = ""; 772 String softwareVersion = ""; 773 String value = Utils.byteArrayToUtf8String(bytesValue); 774 switch (key) { 775 case BluetoothDevice.METADATA_MANUFACTURER_NAME: 776 manufacturerName = value; 777 break; 778 case BluetoothDevice.METADATA_MODEL_NAME: 779 modelName = value; 780 break; 781 case BluetoothDevice.METADATA_HARDWARE_VERSION: 782 hardwareVersion = value; 783 break; 784 case BluetoothDevice.METADATA_SOFTWARE_VERSION: 785 softwareVersion = value; 786 break; 787 default: 788 // Do not log anything if metadata doesn't fall into above categories 789 return; 790 } 791 StatsLog.write(StatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, 792 mAdapterService.obfuscateAddress(device), 793 BluetoothProtoEnums.DEVICE_INFO_EXTERNAL, callingApp, manufacturerName, modelName, 794 hardwareVersion, softwareVersion); 795 } 796 } 797