tray_bluetooth_helper_legacy.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2017 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 "ash/system/bluetooth/tray_bluetooth_helper_legacy.h"
  5. #include <string>
  6. #include <utility>
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/public/cpp/system_tray_client.h"
  9. #include "ash/shell.h"
  10. #include "ash/system/bluetooth/bluetooth_power_controller.h"
  11. #include "ash/system/model/system_tray_model.h"
  12. #include "base/bind.h"
  13. #include "base/callback_helpers.h"
  14. #include "base/check.h"
  15. #include "base/metrics/user_metrics.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/strings/stringprintf.h"
  19. #include "base/time/default_clock.h"
  20. #include "base/time/time.h"
  21. #include "device/bluetooth/bluetooth_adapter.h"
  22. #include "device/bluetooth/bluetooth_adapter_factory.h"
  23. #include "device/bluetooth/bluetooth_device.h"
  24. #include "device/bluetooth/bluetooth_discovery_session.h"
  25. #include "device/bluetooth/chromeos/bluetooth_utils.h"
  26. #include "device/bluetooth/public/cpp/bluetooth_address.h"
  27. #include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
  28. using device::mojom::BluetoothDeviceBatteryInfo;
  29. using device::mojom::BluetoothDeviceBatteryInfoPtr;
  30. using device::mojom::BluetoothDeviceInfo;
  31. using device::mojom::BluetoothDeviceInfoPtr;
  32. using device::mojom::BluetoothSystem;
  33. namespace ash {
  34. namespace {
  35. // System tray shows a limited number of bluetooth devices.
  36. const int kMaximumDevicesShown = 50;
  37. // Client name for logging in BLE scanning.
  38. constexpr char kScanClientName[] = "Ash System Tray";
  39. device::ConnectionFailureReason GetConnectionFailureReason(
  40. device::BluetoothDevice::ConnectErrorCode error_code) {
  41. switch (error_code) {
  42. case device::BluetoothDevice::ConnectErrorCode::ERROR_AUTH_FAILED:
  43. return device::ConnectionFailureReason::kAuthFailed;
  44. case device::BluetoothDevice::ConnectErrorCode::ERROR_AUTH_TIMEOUT:
  45. return device::ConnectionFailureReason::kAuthTimeout;
  46. case device::BluetoothDevice::ConnectErrorCode::ERROR_FAILED:
  47. return device::ConnectionFailureReason::kFailed;
  48. case device::BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN:
  49. return device::ConnectionFailureReason::kUnknownConnectionError;
  50. case device::BluetoothDevice::ConnectErrorCode::ERROR_UNSUPPORTED_DEVICE:
  51. return device::ConnectionFailureReason::kUnsupportedDevice;
  52. default:
  53. return device::ConnectionFailureReason::kUnknownError;
  54. }
  55. }
  56. void BluetoothSetDiscoveringError() {
  57. LOG(ERROR) << "BluetoothSetDiscovering failed.";
  58. }
  59. // |reconnection_attempt_timestamp| can be null if the current device was not
  60. // previously paired before connection attempt.
  61. void OnBluetoothDeviceConnect(
  62. bool was_device_already_paired,
  63. device::BluetoothTransport transport,
  64. base::Time reconnection_attempt_timestamp,
  65. absl::optional<device::BluetoothDevice::ConnectErrorCode> error_code) {
  66. if (was_device_already_paired) {
  67. device::RecordUserInitiatedReconnectionAttemptResult(
  68. error_code
  69. ? absl::make_optional(GetConnectionFailureReason(*error_code))
  70. : absl::nullopt,
  71. device::UserInitiatedReconnectionUISurfaces::kSystemTray);
  72. device::RecordUserInitiatedReconnectionAttemptDuration(
  73. error_code
  74. ? absl::make_optional(GetConnectionFailureReason(*error_code))
  75. : absl::nullopt,
  76. transport,
  77. base::DefaultClock::GetInstance()->Now() -
  78. reconnection_attempt_timestamp);
  79. }
  80. if (error_code) {
  81. LOG(ERROR) << "Failed to connect to device, error code ["
  82. << error_code.value()
  83. << "]. The attempted device was previously ["
  84. << (was_device_already_paired ? "paired" : "not paired") << "].";
  85. }
  86. }
  87. std::string BluetoothAddressToStr(const BluetoothAddress& address) {
  88. static constexpr char kAddressFormat[] =
  89. "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX";
  90. return base::StringPrintf(kAddressFormat, address[0], address[1], address[2],
  91. address[3], address[4], address[5]);
  92. }
  93. // Converts a MAC Address string e.g. "00:11:22:33:44:55" into an
  94. // BluetoothAddress e.g. {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}.
  95. BluetoothAddress AddressStrToBluetoothAddress(const std::string& address_str) {
  96. BluetoothAddress address_array;
  97. // If the string is not a valid encoding of a Bluetooth address, then the
  98. // underlying Bluetooth API returned an incorrect value.
  99. CHECK(device::ParseBluetoothAddress(address_str, address_array));
  100. return address_array;
  101. }
  102. BluetoothDeviceInfoPtr GetBluetoothDeviceInfo(device::BluetoothDevice* device) {
  103. BluetoothDeviceInfoPtr info = BluetoothDeviceInfo::New();
  104. info->address = AddressStrToBluetoothAddress(device->GetAddress());
  105. info->name = device->GetName();
  106. info->is_paired = device->IsPaired();
  107. info->is_blocked_by_policy = device->IsBlockedByPolicy();
  108. absl::optional<device::BluetoothDevice::BatteryInfo> battery_info =
  109. device->GetBatteryInfo(device::BluetoothDevice::BatteryType::kDefault);
  110. if (battery_info && battery_info->percentage.has_value()) {
  111. info->battery_info =
  112. BluetoothDeviceBatteryInfo::New(battery_info->percentage.value());
  113. }
  114. switch (device->GetDeviceType()) {
  115. case device::BluetoothDeviceType::UNKNOWN:
  116. info->device_type = BluetoothDeviceInfo::DeviceType::kUnknown;
  117. break;
  118. case device::BluetoothDeviceType::COMPUTER:
  119. info->device_type = BluetoothDeviceInfo::DeviceType::kComputer;
  120. break;
  121. case device::BluetoothDeviceType::PHONE:
  122. info->device_type = BluetoothDeviceInfo::DeviceType::kPhone;
  123. break;
  124. case device::BluetoothDeviceType::MODEM:
  125. info->device_type = BluetoothDeviceInfo::DeviceType::kModem;
  126. break;
  127. case device::BluetoothDeviceType::AUDIO:
  128. info->device_type = BluetoothDeviceInfo::DeviceType::kAudio;
  129. break;
  130. case device::BluetoothDeviceType::CAR_AUDIO:
  131. info->device_type = BluetoothDeviceInfo::DeviceType::kCarAudio;
  132. break;
  133. case device::BluetoothDeviceType::VIDEO:
  134. info->device_type = BluetoothDeviceInfo::DeviceType::kVideo;
  135. break;
  136. case device::BluetoothDeviceType::PERIPHERAL:
  137. info->device_type = BluetoothDeviceInfo::DeviceType::kPeripheral;
  138. break;
  139. case device::BluetoothDeviceType::JOYSTICK:
  140. info->device_type = BluetoothDeviceInfo::DeviceType::kJoystick;
  141. break;
  142. case device::BluetoothDeviceType::GAMEPAD:
  143. info->device_type = BluetoothDeviceInfo::DeviceType::kGamepad;
  144. break;
  145. case device::BluetoothDeviceType::KEYBOARD:
  146. info->device_type = BluetoothDeviceInfo::DeviceType::kKeyboard;
  147. break;
  148. case device::BluetoothDeviceType::MOUSE:
  149. info->device_type = BluetoothDeviceInfo::DeviceType::kMouse;
  150. break;
  151. case device::BluetoothDeviceType::TABLET:
  152. info->device_type = BluetoothDeviceInfo::DeviceType::kTablet;
  153. break;
  154. case device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO:
  155. info->device_type = BluetoothDeviceInfo::DeviceType::kKeyboardMouseCombo;
  156. break;
  157. }
  158. if (device->IsConnecting()) {
  159. info->connection_state = BluetoothDeviceInfo::ConnectionState::kConnecting;
  160. } else if (device->IsConnected()) {
  161. info->connection_state = BluetoothDeviceInfo::ConnectionState::kConnected;
  162. } else {
  163. info->connection_state =
  164. BluetoothDeviceInfo::ConnectionState::kNotConnected;
  165. }
  166. return info;
  167. }
  168. } // namespace
  169. TrayBluetoothHelperLegacy::TrayBluetoothHelperLegacy() {
  170. DCHECK(!ash::features::IsBluetoothRevampEnabled());
  171. }
  172. TrayBluetoothHelperLegacy::~TrayBluetoothHelperLegacy() {
  173. if (adapter_)
  174. adapter_->RemoveObserver(this);
  175. }
  176. void TrayBluetoothHelperLegacy::InitializeOnAdapterReady(
  177. scoped_refptr<device::BluetoothAdapter> adapter) {
  178. adapter_ = adapter;
  179. CHECK(adapter_);
  180. adapter_->AddObserver(this);
  181. last_state_ = GetBluetoothState();
  182. StartOrStopRefreshingDeviceList();
  183. }
  184. void TrayBluetoothHelperLegacy::Initialize() {
  185. device::BluetoothAdapterFactory::Get()->GetAdapter(
  186. base::BindOnce(&TrayBluetoothHelperLegacy::InitializeOnAdapterReady,
  187. weak_ptr_factory_.GetWeakPtr()));
  188. }
  189. void TrayBluetoothHelperLegacy::StartBluetoothDiscovering() {
  190. discovery_start_timestamp_ = base::DefaultClock::GetInstance()->Now();
  191. if (HasBluetoothDiscoverySession()) {
  192. LOG(WARNING) << "Already have active Bluetooth device discovery session.";
  193. return;
  194. }
  195. VLOG(1) << "Requesting new Bluetooth device discovery session.";
  196. should_run_discovery_ = true;
  197. adapter_->StartDiscoverySession(
  198. kScanClientName,
  199. base::BindOnce(&TrayBluetoothHelperLegacy::OnStartDiscoverySession,
  200. weak_ptr_factory_.GetWeakPtr()),
  201. base::BindOnce(&BluetoothSetDiscoveringError));
  202. }
  203. void TrayBluetoothHelperLegacy::StopBluetoothDiscovering() {
  204. discovery_start_timestamp_ = base::Time();
  205. should_run_discovery_ = false;
  206. if (!HasBluetoothDiscoverySession()) {
  207. LOG(WARNING) << "No active Bluetooth device discovery session.";
  208. return;
  209. }
  210. VLOG(1) << "Stopping Bluetooth device discovery session.";
  211. discovery_session_.reset();
  212. }
  213. void TrayBluetoothHelperLegacy::ConnectToBluetoothDevice(
  214. const BluetoothAddress& address) {
  215. device::BluetoothDevice* device =
  216. adapter_->GetDevice(BluetoothAddressToStr(address));
  217. if (!device || device->IsConnecting() ||
  218. (device->IsConnected() && device->IsPaired())) {
  219. return;
  220. }
  221. if (!discovery_start_timestamp_.is_null()) {
  222. device::RecordDeviceSelectionDuration(
  223. base::DefaultClock::GetInstance()->Now() - discovery_start_timestamp_,
  224. device::DeviceSelectionUISurfaces::kSystemTray, device->IsPaired(),
  225. device->GetType());
  226. discovery_start_timestamp_ = base::Time();
  227. }
  228. // Extra consideration taken for already paired devices, for metrics
  229. // collection.
  230. if (device->IsPaired()) {
  231. base::RecordAction(
  232. base::UserMetricsAction("StatusArea_Bluetooth_Connect_Known"));
  233. if (!device->IsConnectable()) {
  234. device::RecordUserInitiatedReconnectionAttemptResult(
  235. device::ConnectionFailureReason::kNotConnectable,
  236. device::UserInitiatedReconnectionUISurfaces::kSystemTray);
  237. return;
  238. }
  239. device->Connect(
  240. /*pairing_delegate=*/nullptr,
  241. base::BindOnce(&OnBluetoothDeviceConnect,
  242. /*was_device_already_paired=*/true, device->GetType(),
  243. base::DefaultClock::GetInstance()->Now()));
  244. return;
  245. }
  246. // Simply connect without pairing for devices which do not support pairing.
  247. if (!device->IsPairable()) {
  248. // Use a null timestamp to represent the fact that the device was not
  249. // already paired.
  250. base::Time reconnection_attempt_timestamp;
  251. device->Connect(
  252. /*pairing_delegate=*/nullptr,
  253. base::BindOnce(&OnBluetoothDeviceConnect,
  254. /*was_device_already_paired=*/false, device->GetType(),
  255. reconnection_attempt_timestamp));
  256. return;
  257. }
  258. // Show pairing dialog for the unpaired device; this kicks off pairing.
  259. Shell::Get()->system_tray_model()->client()->ShowBluetoothPairingDialog(
  260. device->GetAddress());
  261. }
  262. BluetoothSystem::State TrayBluetoothHelperLegacy::GetBluetoothState() {
  263. // Eventually this will use the BluetoothSystem Mojo interface, but for now
  264. // use the current Bluetooth API to get a BluetoothSystem::State.
  265. if (!adapter_)
  266. return BluetoothSystem::State::kUnavailable;
  267. if (!adapter_->IsPresent())
  268. return BluetoothSystem::State::kUnavailable;
  269. if (adapter_->IsPowered())
  270. return BluetoothSystem::State::kPoweredOn;
  271. return BluetoothSystem::State::kPoweredOff;
  272. }
  273. void TrayBluetoothHelperLegacy::SetBluetoothEnabled(bool enabled) {
  274. if (enabled != (GetBluetoothState() == BluetoothSystem::State::kPoweredOn)) {
  275. if (enabled) {
  276. base::RecordAction(
  277. base::UserMetricsAction("StatusArea_Bluetooth_Enabled"));
  278. } else {
  279. base::RecordAction(
  280. base::UserMetricsAction("StatusArea_Bluetooth_Disabled"));
  281. }
  282. }
  283. Shell::Get()->bluetooth_power_controller()->SetBluetoothEnabled(enabled);
  284. }
  285. bool TrayBluetoothHelperLegacy::HasBluetoothDiscoverySession() {
  286. return discovery_session_ && discovery_session_->IsActive();
  287. }
  288. void TrayBluetoothHelperLegacy::GetBluetoothDevices(
  289. GetBluetoothDevicesCallback callback) const {
  290. BluetoothDeviceList device_list;
  291. device::BluetoothAdapter::DeviceList devices =
  292. device::FilterBluetoothDeviceList(adapter_->GetDevices(),
  293. device::BluetoothFilterType::KNOWN,
  294. kMaximumDevicesShown);
  295. for (device::BluetoothDevice* device : devices)
  296. device_list.push_back(GetBluetoothDeviceInfo(device));
  297. base::ThreadTaskRunnerHandle::Get()->PostTask(
  298. FROM_HERE, base::BindOnce(std::move(callback), std::move(device_list)));
  299. }
  300. ////////////////////////////////////////////////////////////////////////////////
  301. // BluetoothAdapter::Observer:
  302. void TrayBluetoothHelperLegacy::AdapterPresentChanged(
  303. device::BluetoothAdapter* adapter,
  304. bool present) {
  305. if (last_state_ == GetBluetoothState())
  306. return;
  307. last_state_ = GetBluetoothState();
  308. NotifyBluetoothSystemStateChanged();
  309. StartOrStopRefreshingDeviceList();
  310. }
  311. void TrayBluetoothHelperLegacy::AdapterPoweredChanged(
  312. device::BluetoothAdapter* adapter,
  313. bool powered) {
  314. if (last_state_ == GetBluetoothState())
  315. return;
  316. last_state_ = GetBluetoothState();
  317. NotifyBluetoothSystemStateChanged();
  318. StartOrStopRefreshingDeviceList();
  319. }
  320. void TrayBluetoothHelperLegacy::AdapterDiscoveringChanged(
  321. device::BluetoothAdapter* adapter,
  322. bool discovering) {
  323. NotifyBluetoothScanStateChanged();
  324. }
  325. void TrayBluetoothHelperLegacy::OnStartDiscoverySession(
  326. std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
  327. // If the discovery session was returned after a request to stop discovery
  328. // (e.g. the user dismissed the Bluetooth detailed view before the call
  329. // returned), don't claim the discovery session and let it clean up.
  330. if (!should_run_discovery_)
  331. return;
  332. VLOG(1) << "Claiming new Bluetooth device discovery session.";
  333. discovery_session_ = std::move(discovery_session);
  334. NotifyBluetoothScanStateChanged();
  335. }
  336. } // namespace ash