bluetooth_low_energy_device_watcher_mac.mm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/bluetooth/bluetooth_low_energy_device_watcher_mac.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/files/file_util.h"
  8. #include "base/logging.h"
  9. #include "base/strings/sys_string_conversions.h"
  10. #include "base/task/task_traits.h"
  11. #include "device/bluetooth/bluetooth_adapter_mac.h"
  12. namespace device {
  13. namespace {
  14. constexpr char kBluetoothPlistFilePath[] =
  15. "/Library/Preferences/com.apple.Bluetooth.plist";
  16. } // namespace
  17. // static
  18. scoped_refptr<BluetoothLowEnergyDeviceWatcherMac>
  19. BluetoothLowEnergyDeviceWatcherMac::CreateAndStartWatching(
  20. scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner,
  21. LowEnergyDeviceListUpdatedCallback
  22. low_energy_device_list_updated_callback) {
  23. auto watcher = base::MakeRefCounted<BluetoothLowEnergyDeviceWatcherMac>(
  24. std::move(ui_thread_task_runner),
  25. std::move(low_energy_device_list_updated_callback));
  26. watcher->Init();
  27. return watcher;
  28. }
  29. BluetoothLowEnergyDeviceWatcherMac::BluetoothLowEnergyDeviceWatcherMac(
  30. scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner,
  31. LowEnergyDeviceListUpdatedCallback low_energy_device_list_updated_callback)
  32. : ui_thread_task_runner_(std::move(ui_thread_task_runner)),
  33. low_energy_device_list_updated_callback_(
  34. std::move(low_energy_device_list_updated_callback)) {
  35. DETACH_FROM_SEQUENCE(sequence_checker_);
  36. }
  37. BluetoothLowEnergyDeviceWatcherMac::~BluetoothLowEnergyDeviceWatcherMac() {
  38. file_thread_task_runner_->DeleteSoon(FROM_HERE,
  39. property_list_watcher_.release());
  40. }
  41. void BluetoothLowEnergyDeviceWatcherMac::OnPropertyListFileChangedOnFileThread(
  42. const base::FilePath& path,
  43. bool error) {
  44. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  45. if (error) {
  46. LOG(WARNING) << "Failed to read com.apple.Bluetooth.plist.";
  47. return;
  48. }
  49. // Bluetooth property list file is expected to have the following format:
  50. //
  51. // "CoreBluetoothCache" => {
  52. // "E7F8589A-A7D9-4B94-9A08-D89076A159F4" => {
  53. // "DeviceAddress" => "11-11-11-11-11-11"
  54. // "DeviceAddressType" => 1
  55. // "ServiceChangedHandle" => 3
  56. // "ServiceChangedSubscribed" => 0
  57. // "ServiceDiscoveryComplete" => 0
  58. // }
  59. // "D3CAC59E-C501-4599-97DA-2DF491544EEE" => {
  60. // "DeviceAddress" => "22-22-22-22-22-22"
  61. // "DeviceAddressType" => 1
  62. // "ServiceChangedHandle" => 3
  63. // "ServiceChangedSubscribed" => 0
  64. // "ServiceDiscoveryComplete" => 0
  65. // }
  66. // }
  67. NSString* plist_file_path = base::SysUTF8ToNSString(path.value());
  68. NSDictionary* bluetooth_info_dictionary =
  69. [NSDictionary dictionaryWithContentsOfFile:plist_file_path];
  70. // |bluetooth_info_dictionary| is nil if there was an error reading the file
  71. // or if the content of the read file cannot be represented by a dictionary.
  72. if (!bluetooth_info_dictionary)
  73. return;
  74. auto parsed_data =
  75. ParseBluetoothDevicePropertyListData(bluetooth_info_dictionary);
  76. ui_thread_task_runner_->PostTask(
  77. FROM_HERE, base::BindOnce(low_energy_device_list_updated_callback_,
  78. std::move(parsed_data)));
  79. }
  80. void BluetoothLowEnergyDeviceWatcherMac::Init() {
  81. file_thread_task_runner_->PostTask(
  82. FROM_HERE, base::BindOnce(&BluetoothLowEnergyDeviceWatcherMac::
  83. AddBluetoothPropertyListFileWatcher,
  84. this));
  85. }
  86. void BluetoothLowEnergyDeviceWatcherMac::ReadBluetoothPropertyListFile() {
  87. file_thread_task_runner_->PostTask(
  88. FROM_HERE,
  89. base::BindOnce(&BluetoothLowEnergyDeviceWatcherMac::
  90. OnPropertyListFileChangedOnFileThread,
  91. this, BluetoothPlistFilePath(), false /* error */));
  92. }
  93. std::map<std::string, std::string>
  94. BluetoothLowEnergyDeviceWatcherMac::ParseBluetoothDevicePropertyListData(
  95. NSDictionary* data) {
  96. std::map<std::string, std::string> updated_low_energy_devices_info;
  97. NSDictionary* low_energy_devices_info = data[@"CoreBluetoothCache"];
  98. if (!low_energy_devices_info)
  99. return updated_low_energy_devices_info;
  100. for (NSString* identifier in low_energy_devices_info) {
  101. NSDictionary* device_info = low_energy_devices_info[identifier];
  102. if (!device_info)
  103. continue;
  104. NSString* raw_device_address = device_info[@"DeviceAddress"];
  105. if (!raw_device_address)
  106. continue;
  107. NSString* formatted_device_address =
  108. [raw_device_address stringByReplacingOccurrencesOfString:@"-"
  109. withString:@":"];
  110. updated_low_energy_devices_info[base::SysNSStringToUTF8(identifier)] =
  111. base::SysNSStringToUTF8(formatted_device_address);
  112. }
  113. return updated_low_energy_devices_info;
  114. }
  115. // static
  116. const base::FilePath&
  117. BluetoothLowEnergyDeviceWatcherMac::BluetoothPlistFilePath() {
  118. static const base::FilePath file_path(kBluetoothPlistFilePath);
  119. return file_path;
  120. }
  121. void BluetoothLowEnergyDeviceWatcherMac::AddBluetoothPropertyListFileWatcher() {
  122. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  123. property_list_watcher_->Watch(
  124. BluetoothPlistFilePath(), base::FilePathWatcher::Type::kNonRecursive,
  125. base::BindRepeating(&BluetoothLowEnergyDeviceWatcherMac::
  126. OnPropertyListFileChangedOnFileThread,
  127. this));
  128. }
  129. } // namespace device