[go: nahoru, domu]

blob: 7027f96a9602d2bc6f9c5142467fdebaace84a47 [file] [log] [blame]
Avi Drissman0db84842022-09-13 19:47:041// Copyright 2018 The Chromium Authors
Sarah Huad95e802018-04-12 06:24:292// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "device/bluetooth/chromeos/bluetooth_utils.h"
6
Lei Zhang589fe0a2021-05-12 03:17:437#include "base/containers/contains.h"
Kyle Horimoto419ea9a2022-05-13 02:10:088#include "base/containers/fixed_flat_set.h"
Sonny Sasaka7fecfeda2018-07-11 23:47:559#include "base/feature_list.h"
Ryan Hansberry27c9c582019-03-22 04:38:5210#include "base/metrics/field_trial_params.h"
Ryan Hansberryaf7c94e92019-06-14 19:39:3811#include "base/metrics/histogram_functions.h"
Theo Johnson-Kanu3c389db2021-07-22 21:49:0212#include "base/strings/strcat.h"
Ryan Hansberry27c9c582019-03-22 04:38:5213#include "base/strings/string_number_conversions.h"
14#include "base/strings/string_split.h"
15#include "base/strings/string_util.h"
Ryan Hansberryaf7c94e92019-06-14 19:39:3816#include "base/time/time.h"
Eric Willigers4f1da112022-03-28 22:55:2817#include "build/chromeos_buildflags.h"
Samuel Huang81b7b322021-11-19 06:19:5518#include "chromeos/constants/chromeos_features.h"
Eric Willigers4f1da112022-03-28 22:55:2819#include "device/base/features.h"
Anton Bikineev15c07002021-05-15 17:55:0220#include "third_party/abseil-cpp/absl/types/optional.h"
Alain Michaudda9d4912020-03-09 20:55:3021
Samuel Huang1f2e5dc2021-11-12 02:05:3322#if BUILDFLAG(IS_CHROMEOS_ASH)
Julie Jeongeun Kimdb5662462023-11-22 07:32:0723#include <string_view>
24
Kyle Horimoto419ea9a2022-05-13 02:10:0825#include "ash/constants/ash_features.h"
Samuel Huang1f2e5dc2021-11-12 02:05:3326#include "ash/constants/ash_switches.h"
Chad Duffinf7166572023-07-17 17:37:1527#include "chromeos/ash/services/nearby/public/cpp/nearby_client_uuids.h"
28#include "chromeos/ash/services/secure_channel/public/cpp/shared/ble_constants.h"
Samuel Huang1f2e5dc2021-11-12 02:05:3329#endif // BUILDFLAG(IS_CHROMEOS_ASH)
30
31#if BUILDFLAG(IS_CHROMEOS_LACROS)
Andrea Orru66066fcd2022-07-21 03:45:5432#include "chromeos/startup/browser_params_proxy.h"
Samuel Huang1f2e5dc2021-11-12 02:05:3333#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
34
Sarah Huad95e802018-04-12 06:24:2935namespace device {
36
37namespace {
38
Sarah Hu81c120a2018-05-10 00:33:5239// https://www.bluetooth.com/specifications/gatt/services.
40const char kHIDServiceUUID[] = "1812";
41
42// https://www.bluetooth.com/specifications/assigned-numbers/16-bit-uuids-for-sdos.
43const char kSecurityKeyServiceUUID[] = "FFFD";
44
Peter Kastinge5a38ed2021-10-02 03:06:3545constexpr base::TimeDelta kMaxDeviceSelectionDuration = base::Seconds(30);
Ryan Hansberryaf7c94e92019-06-14 19:39:3846
Michael Sund59d5bd2023-09-06 04:14:2847constexpr uint8_t kLimitedDiscoveryFlag = 0x01;
48constexpr uint8_t kGeneralDiscoveryFlag = 0x02;
49
Sarah Huad95e802018-04-12 06:24:2950// Get limited number of devices from |devices| and
51// prioritize paired/connecting devices over other devices.
52BluetoothAdapter::DeviceList GetLimitedNumDevices(
53 size_t max_device_num,
54 const BluetoothAdapter::DeviceList& devices) {
55 // If |max_device_num| is 0, it means there's no limit.
56 if (max_device_num == 0)
57 return devices;
58
59 BluetoothAdapter::DeviceList result;
60 for (BluetoothDevice* device : devices) {
61 if (result.size() == max_device_num)
62 break;
63
64 if (device->IsPaired() || device->IsConnecting())
65 result.push_back(device);
66 }
67
68 for (BluetoothDevice* device : devices) {
69 if (result.size() == max_device_num)
70 break;
71
72 if (!device->IsPaired() && !device->IsConnecting())
73 result.push_back(device);
74 }
75
76 return result;
77}
78
79// Filter out unknown devices from the list.
80BluetoothAdapter::DeviceList FilterUnknownDevices(
81 const BluetoothAdapter::DeviceList& devices) {
Samuel Huang1f2e5dc2021-11-12 02:05:3382#if BUILDFLAG(IS_CHROMEOS_ASH)
Henrique Ferreiro93dd33b2022-01-18 16:06:4583 if (ash::switches::IsUnfilteredBluetoothDevicesEnabled())
Sonny Sasaka7fecfeda2018-07-11 23:47:5584 return devices;
Samuel Huang1f2e5dc2021-11-12 02:05:3385#endif // BUILDFLAG(IS_CHROMEOS_ASH)
86
87#if BUILDFLAG(IS_CHROMEOS_LACROS)
Andrea Orru66066fcd2022-07-21 03:45:5488 if (chromeos::BrowserParamsProxy::Get()
89 ->IsUnfilteredBluetoothDeviceEnabled()) {
Samuel Huang1f2e5dc2021-11-12 02:05:3390 return devices;
91 }
92#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
Sonny Sasaka7fecfeda2018-07-11 23:47:5593
Sarah Huad95e802018-04-12 06:24:2994 BluetoothAdapter::DeviceList result;
95 for (BluetoothDevice* device : devices) {
Gordon Setoc1308992021-12-14 19:27:2096 if (device::IsUnsupportedDevice(device))
Ryan Hansberry2e462462019-06-19 18:03:1197 continue;
Ryan Hansberry2e462462019-06-19 18:03:1198
Gordon Setoc1308992021-12-14 19:27:2099 result.push_back(device);
Sarah Huad95e802018-04-12 06:24:29100 }
101 return result;
102}
103
Ryan Hansberry5ed351e2020-03-10 00:39:13104void RecordPairingDuration(const std::string& histogram_name,
105 base::TimeDelta pairing_duration) {
106 base::UmaHistogramCustomTimes(histogram_name, pairing_duration,
Peter Kastinge5a38ed2021-10-02 03:06:35107 base::Milliseconds(1) /* min */,
108 base::Seconds(30) /* max */, 50 /* buckets */);
Ryan Hansberry5ed351e2020-03-10 00:39:13109}
110
111void RecordPairingTransport(BluetoothTransport transport) {
112 BluetoothTransportType type;
113 switch (transport) {
114 case BLUETOOTH_TRANSPORT_CLASSIC:
115 type = BluetoothTransportType::kClassic;
116 break;
117 case BLUETOOTH_TRANSPORT_LE:
118 type = BluetoothTransportType::kLE;
119 break;
120 case BLUETOOTH_TRANSPORT_DUAL:
121 type = BluetoothTransportType::kDual;
122 break;
123 case BLUETOOTH_TRANSPORT_INVALID:
124 type = BluetoothTransportType::kInvalid;
125 break;
126 default:
127 type = BluetoothTransportType::kUnknown;
128 break;
129 }
130
131 base::UmaHistogramEnumeration("Bluetooth.ChromeOS.Pairing.TransportType",
132 type);
133}
134
Ryan Hansberryaf7c94e92019-06-14 19:39:38135void RecordDeviceSelectionDuration(const std::string& histogram_name,
136 base::TimeDelta duration) {
137 base::UmaHistogramCustomTimes(
Peter Kastinge5a38ed2021-10-02 03:06:35138 histogram_name, duration, base::Milliseconds(1) /* min */,
Ryan Hansberryaf7c94e92019-06-14 19:39:38139 kMaxDeviceSelectionDuration /* max */, 50 /* buckets */);
140}
141
Theo Johnson-Kanu3c389db2021-07-22 21:49:02142std::string GetTransportName(BluetoothTransport transport) {
143 switch (transport) {
144 case BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC:
145 return "Classic";
146 case BluetoothTransport::BLUETOOTH_TRANSPORT_LE:
147 return "BLE";
148 case BluetoothTransport::BLUETOOTH_TRANSPORT_DUAL:
149 return "Dual";
Theo Johnson-Kanu37fd36a2022-02-02 22:03:35150 case BLUETOOTH_TRANSPORT_INVALID:
151 return "Invalid";
Theo Johnson-Kanu3c389db2021-07-22 21:49:02152 default:
Theo Johnson-Kanu37fd36a2022-02-02 22:03:35153 // A transport type of other is unexpected, and no success
Theo Johnson-Kanu3c389db2021-07-22 21:49:02154 // metric for it exists.
155 return "";
156 }
157}
158
Theo Johnson-Kanu0a34d732023-03-23 20:16:09159void EmitFilteredFailureReason(ConnectionFailureReason failure_reason,
160 const std::string& transport_name) {
161 switch (failure_reason) {
162 case ConnectionFailureReason::kAuthCanceled:
163 [[fallthrough]];
164 case ConnectionFailureReason::kAuthRejected:
165 return;
166 case ConnectionFailureReason::kUnknownError:
167 [[fallthrough]];
168 case ConnectionFailureReason::kAuthFailed:
169 [[fallthrough]];
170 case ConnectionFailureReason::kAuthTimeout:
171 [[fallthrough]];
172 case ConnectionFailureReason::kUnknownConnectionError:
173 [[fallthrough]];
174 case ConnectionFailureReason::kUnsupportedDevice:
175 [[fallthrough]];
176 case ConnectionFailureReason::kNotConnectable:
177 [[fallthrough]];
178 case ConnectionFailureReason::kSystemError:
179 [[fallthrough]];
180 case ConnectionFailureReason::kFailed:
181 [[fallthrough]];
182 case ConnectionFailureReason::kInprogress:
183 const std::string result_histogram_name_prefix =
184 "Bluetooth.ChromeOS.Pairing.Result";
185 base::UmaHistogramEnumeration(
186 result_histogram_name_prefix + ".FilteredFailureReason",
187 failure_reason);
188 base::UmaHistogramEnumeration(result_histogram_name_prefix +
189 ".FilteredFailureReason." +
190 transport_name,
191 failure_reason);
192 return;
193 }
194 NOTREACHED();
195}
196
Kyle Horimoto419ea9a2022-05-13 02:10:08197#if BUILDFLAG(IS_CHROMEOS_ASH)
Kyle Horimoto419ea9a2022-05-13 02:10:08198bool IsPolyDevice(const device::BluetoothDevice* device) {
199 // OUI portions of Bluetooth addresses for devices manufactured by Poly. See
200 // https://standards-oui.ieee.org/.
Julie Jeongeun Kimdb5662462023-11-22 07:32:07201 constexpr auto kPolyOuis = base::MakeFixedFlatSet<std::string_view>(
Kyle Horimoto419ea9a2022-05-13 02:10:08202 {"64:16:7F", "48:25:67", "00:04:F2"});
203
204 return base::Contains(kPolyOuis, device->GetOuiPortionOfBluetoothAddress());
205}
Andrew Dear283c6e72022-12-01 17:49:54206#endif
Kyle Horimoto419ea9a2022-05-13 02:10:08207
Archieef62df352024-01-24 09:23:54208// Provide heuristics for which transport to use for a dual device
209BluetoothTransport InferDeviceTransport(const device::BluetoothDevice* device) {
210 if (device->GetType() != BLUETOOTH_TRANSPORT_DUAL) {
211 return device->GetType();
212 }
213
214#if BUILDFLAG(IS_CHROMEOS)
215 // Random address type indicates LE device.
216 if (device->GetAddressType() ==
217 BluetoothDevice::AddressType::ADDR_TYPE_RANDOM) {
218 return BLUETOOTH_TRANSPORT_LE;
219 }
220#endif
221
222 // Devices without type/appearance most likely signals that it is truly only
223 // a LE advertisement for a peripheral which is active, but not pairable. Many
224 // popular headphones behave in this exact way. Mark as invalid until they
225 // provide a type/appearance; this means they've become pairable. See
226 // https://crrev.com/c/1656971 for more.
227 if (device->GetDeviceType() == BluetoothDeviceType::UNKNOWN) {
228 return BLUETOOTH_TRANSPORT_INVALID;
229 }
230
231 return BLUETOOTH_TRANSPORT_CLASSIC;
232}
233
Sarah Huad95e802018-04-12 06:24:29234} // namespace
235
236device::BluetoothAdapter::DeviceList FilterBluetoothDeviceList(
237 const BluetoothAdapter::DeviceList& devices,
238 BluetoothFilterType filter_type,
239 int max_devices) {
240 BluetoothAdapter::DeviceList filtered_devices =
241 filter_type == BluetoothFilterType::KNOWN ? FilterUnknownDevices(devices)
242 : devices;
243 return GetLimitedNumDevices(max_devices, filtered_devices);
244}
245
Gordon Setoc1308992021-12-14 19:27:20246bool IsUnsupportedDevice(const device::BluetoothDevice* device) {
247#if BUILDFLAG(IS_CHROMEOS_ASH)
Chad Duffin77a85372023-08-17 10:05:16248 if (ash::switches::IsUnfilteredBluetoothDevicesEnabled()) {
Gordon Setoc1308992021-12-14 19:27:20249 return false;
Chad Duffin77a85372023-08-17 10:05:16250 }
Gordon Setoc1308992021-12-14 19:27:20251#endif // BUILDFLAG(IS_CHROMEOS_ASH)
252
253#if BUILDFLAG(IS_CHROMEOS_LACROS)
Andrea Orru66066fcd2022-07-21 03:45:54254 if (chromeos::BrowserParamsProxy::Get()
255 ->IsUnfilteredBluetoothDeviceEnabled()) {
Gordon Setoc1308992021-12-14 19:27:20256 return false;
257 }
258#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
259
Andrew Dear283c6e72022-12-01 17:49:54260#if BUILDFLAG(IS_CHROMEOS_ASH)
Kyle Horimoto419ea9a2022-05-13 02:10:08261 // Never filter out Poly devices; this requires a special case since these
262 // devices often identify themselves as phones, which are disallowed below.
263 // See b/228118615.
Chad Duffin77a85372023-08-17 10:05:16264 if (IsPolyDevice(device)) {
Kyle Horimoto419ea9a2022-05-13 02:10:08265 return false;
Chad Duffin77a85372023-08-17 10:05:16266 }
267#endif
268
269#if BUILDFLAG(IS_CHROMEOS)
270 // Always allow bonded devices to appear in the UI.
271 if (device->IsBonded()) {
272 return false;
273 }
Andrew Dear283c6e72022-12-01 17:49:54274#endif
Kyle Horimoto419ea9a2022-05-13 02:10:08275
Gordon Setoc1308992021-12-14 19:27:20276 // Always filter out laptops, etc. There is no intended use case or
277 // Bluetooth profile in this context.
Chad Duffin77a85372023-08-17 10:05:16278 if (device->GetDeviceType() == BluetoothDeviceType::COMPUTER) {
Gordon Setoc1308992021-12-14 19:27:20279 return true;
Chad Duffin77a85372023-08-17 10:05:16280 }
Gordon Setoc1308992021-12-14 19:27:20281
282 // Always filter out phones. There is no intended use case or Bluetooth
283 // profile in this context.
284 if (base::FeatureList::IsEnabled(chromeos::features::kBluetoothPhoneFilter) &&
285 device->GetDeviceType() == BluetoothDeviceType::PHONE) {
286 return true;
287 }
288
Chad Duffinf7166572023-07-17 17:37:15289#if BUILDFLAG(IS_CHROMEOS_ASH)
290 const BluetoothDevice::UUIDSet& uuids = device->GetUUIDs();
291
292 // These UUIDs are specific to Nearby Share and Phone Hub and are used to
293 // identify devices that should be filtered from the UI that otherwise would
294 // not have been correctly identified. These devices should always be filtered
295 // from the UI. For more information see b/219627324.
296 for (const auto& uuid : ash::nearby::GetNearbyClientUuids()) {
297 if (uuids.contains(uuid)) {
298 return true;
299 }
300 }
301 if (uuids.contains(BluetoothUUID(ash::secure_channel::kGattServerUuid))) {
302 return true;
303 }
304#endif
305
Chad Duffin77a85372023-08-17 10:05:16306#if !BUILDFLAG(IS_CHROMEOS)
Chad Duffindb4d3522023-03-20 19:27:44307 // Allow paired devices which are not filtered above to appear in the UI.
308 if (device->IsPaired()) {
309 return false;
310 }
311#endif
Gordon Setoc1308992021-12-14 19:27:20312
Archieef62df352024-01-24 09:23:54313 switch (InferDeviceTransport(device)) {
Michael Sund59d5bd2023-09-06 04:14:28314 // For LE devices, check the discoverable flag and UUIDs.
Gordon Setoc1308992021-12-14 19:27:20315 case BLUETOOTH_TRANSPORT_LE:
Michael Sund59d5bd2023-09-06 04:14:28316 // Hide the LE device that mark itself as non-discoverble.
317 if (device->GetAdvertisingDataFlags().has_value()) {
318 if (!((kLimitedDiscoveryFlag | kGeneralDiscoveryFlag) &
319 device->GetAdvertisingDataFlags().value())) {
320 return true;
321 }
322 }
323 // Check the service UUID to determine if it supports HID or second factor
324 // authenticator (security key).
Gordon Setoc1308992021-12-14 19:27:20325 if (base::Contains(device->GetUUIDs(),
326 device::BluetoothUUID(kHIDServiceUUID)) ||
327 base::Contains(device->GetUUIDs(),
328 device::BluetoothUUID(kSecurityKeyServiceUUID))) {
329 return false;
330 }
331 break;
332 // For classic mode devices, only filter out if the name is empty because
333 // the device could have an unknown or even known type and still also
334 // provide audio/HID functionality.
335 case BLUETOOTH_TRANSPORT_CLASSIC:
Chad Duffin77a85372023-08-17 10:05:16336 if (device->GetName()) {
Gordon Setoc1308992021-12-14 19:27:20337 return false;
Chad Duffin77a85372023-08-17 10:05:16338 }
Gordon Setoc1308992021-12-14 19:27:20339 break;
Archieef62df352024-01-24 09:23:54340 // Otherwise, they are invalid, so filter them out.
341 default:
Gordon Setoc1308992021-12-14 19:27:20342 break;
343 }
344
345 return true;
346}
347
Anton Bikineev15c07002021-05-15 17:55:02348void RecordPairingResult(absl::optional<ConnectionFailureReason> failure_reason,
Ryan Hansberry5ed351e2020-03-10 00:39:13349 BluetoothTransport transport,
350 base::TimeDelta duration) {
351 RecordPairingTransport(transport);
352
Theo Johnson-Kanu3c389db2021-07-22 21:49:02353 std::string transport_name = GetTransportName(transport);
354 if (transport_name.empty()) {
355 return;
Ryan Hansberry5ed351e2020-03-10 00:39:13356 }
357
Ryan Hansberryb9e645f2020-04-07 21:10:13358 bool success = !failure_reason.has_value();
359 std::string result_histogram_name_prefix =
360 "Bluetooth.ChromeOS.Pairing.Result";
361
Ryan Hansberry5ed351e2020-03-10 00:39:13362 base::UmaHistogramBoolean(result_histogram_name_prefix, success);
Theo Johnson-Kanu3c389db2021-07-22 21:49:02363 base::UmaHistogramBoolean(result_histogram_name_prefix + "." + transport_name,
364 success);
Ryan Hansberry5ed351e2020-03-10 00:39:13365
366 std::string duration_histogram_name_prefix =
367 "Bluetooth.ChromeOS.Pairing.Duration";
368 std::string success_histogram_name = success ? "Success" : "Failure";
369
370 std::string base_histogram_name =
371 duration_histogram_name_prefix + "." + success_histogram_name;
372 RecordPairingDuration(base_histogram_name, duration);
Theo Johnson-Kanu3c389db2021-07-22 21:49:02373 RecordPairingDuration(base_histogram_name + "." + transport_name, duration);
Ryan Hansberryb9e645f2020-04-07 21:10:13374
375 if (!success) {
376 base::UmaHistogramEnumeration(
377 result_histogram_name_prefix + ".FailureReason", *failure_reason);
Theo Johnson-Kanu3c389db2021-07-22 21:49:02378 base::UmaHistogramEnumeration(
379 result_histogram_name_prefix + ".FailureReason." + transport_name,
380 *failure_reason);
Theo Johnson-Kanu0a34d732023-03-23 20:16:09381 EmitFilteredFailureReason(*failure_reason, transport_name);
Ryan Hansberryb9e645f2020-04-07 21:10:13382 }
Ryan Hansberry5ed351e2020-03-10 00:39:13383}
384
Ryan Hansberryb9e645f2020-04-07 21:10:13385void RecordUserInitiatedReconnectionAttemptResult(
Anton Bikineev15c07002021-05-15 17:55:02386 absl::optional<ConnectionFailureReason> failure_reason,
Theo Johnson-Kanud95038f2021-07-20 17:45:12387 UserInitiatedReconnectionUISurfaces surface) {
Ryan Hansberryb9e645f2020-04-07 21:10:13388 bool success = !failure_reason.has_value();
Ryan Hansberry5ed351e2020-03-10 00:39:13389 std::string base_histogram_name =
390 "Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Result";
Ryan Hansberryb9e645f2020-04-07 21:10:13391
Ryan Hansberry5ed351e2020-03-10 00:39:13392 base::UmaHistogramBoolean(base_histogram_name, success);
393
394 std::string surface_name =
Theo Johnson-Kanud95038f2021-07-20 17:45:12395 (surface == UserInitiatedReconnectionUISurfaces::kSettings
396 ? "Settings"
397 : "SystemTray");
Ryan Hansberry5ed351e2020-03-10 00:39:13398 base::UmaHistogramBoolean(base_histogram_name + "." + surface_name, success);
Ryan Hansberryb9e645f2020-04-07 21:10:13399
400 if (!success) {
401 base::UmaHistogramEnumeration(base_histogram_name + ".FailureReason",
402 *failure_reason);
403 base::UmaHistogramEnumeration(
404 base_histogram_name + ".FailureReason." + surface_name,
405 *failure_reason);
406 }
Ryan Hansberry5ed351e2020-03-10 00:39:13407}
408
Ryan Hansberryaf7c94e92019-06-14 19:39:38409void RecordDeviceSelectionDuration(base::TimeDelta duration,
Theo Johnson-Kanud95038f2021-07-20 17:45:12410 DeviceSelectionUISurfaces surface,
Ryan Hansberryaf7c94e92019-06-14 19:39:38411 bool was_paired,
412 BluetoothTransport transport) {
413 // Throw out longtail results of the user taking longer than
414 // |kMaxDeviceSelectionDuration|. Assume that these thrown out results reflect
415 // the user not being actively engaged with device connection: leaving the
416 // page open for a long time, walking away from computer, etc.
417 if (duration > kMaxDeviceSelectionDuration)
418 return;
419
420 std::string base_histogram_name =
421 "Bluetooth.ChromeOS.DeviceSelectionDuration";
422 RecordDeviceSelectionDuration(base_histogram_name, duration);
423
424 std::string surface_name =
Theo Johnson-Kanud95038f2021-07-20 17:45:12425 (surface == DeviceSelectionUISurfaces::kSettings ? "Settings"
426 : "SystemTray");
Ryan Hansberryaf7c94e92019-06-14 19:39:38427 std::string surface_histogram_name = base_histogram_name + "." + surface_name;
428 RecordDeviceSelectionDuration(surface_histogram_name, duration);
429
430 std::string paired_name = (was_paired ? "Paired" : "NotPaired");
431 std::string paired_histogram_name =
432 surface_histogram_name + "." + paired_name;
433 RecordDeviceSelectionDuration(paired_histogram_name, duration);
434
435 if (!was_paired) {
Theo Johnson-Kanu3c389db2021-07-22 21:49:02436 std::string transport_name = GetTransportName(transport);
437 if (transport_name.empty()) {
438 return;
Ryan Hansberryaf7c94e92019-06-14 19:39:38439 }
Ryan Hansberryaf7c94e92019-06-14 19:39:38440 std::string transport_histogram_name =
441 paired_histogram_name + "." + transport_name;
442 RecordDeviceSelectionDuration(transport_histogram_name, duration);
443 }
444}
Theo Johnson-Kanu9138c202021-07-15 21:51:44445
Theo Johnson-Kanua4a9eac2021-07-24 02:48:33446void RecordPoweredStateOperationResult(PoweredStateOperation operation,
447 bool success) {
448 std::string operation_name =
449 operation == PoweredStateOperation::kEnable ? "Enable" : "Disable";
450
451 base::UmaHistogramBoolean(base::StrCat({"Bluetooth.ChromeOS.PoweredState.",
452 operation_name, ".Result"}),
453 success);
454}
455
Theo Johnson-Kanu9138c202021-07-15 21:51:44456void RecordPoweredState(bool is_powered) {
457 base::UmaHistogramBoolean("Bluetooth.ChromeOS.PoweredState", is_powered);
458}
459
Theo Johnson-Kanu9d51452f2021-07-24 01:05:08460void RecordForgetResult(ForgetResult forget_result) {
461 base::UmaHistogramEnumeration("Bluetooth.ChromeOS.Forget.Result",
462 forget_result);
463}
464
Theo Johnson-Kanud87c25902022-01-26 19:18:18465void RecordDeviceDisconnect(BluetoothDeviceType device_type) {
466 base::UmaHistogramEnumeration("Bluetooth.ChromeOS.DeviceDisconnect",
467 device_type);
468}
469
470void RecordUserInitiatedDisconnectResult(DisconnectResult disconnect_result,
471 BluetoothTransport transport) {
Theo Johnson-Kanue7b494f2021-07-24 03:09:47472 std::string transport_name = GetTransportName(transport);
473
474 if (transport_name.empty()) {
475 return;
476 }
477
Theo Johnson-Kanue7b494f2021-07-24 03:09:47478 base::UmaHistogramEnumeration(
Theo Johnson-Kanud87c25902022-01-26 19:18:18479 "Bluetooth.ChromeOS.UserInitiatedDisconnect.Result", disconnect_result);
480 base::UmaHistogramEnumeration(
481 base::StrCat({"Bluetooth.ChromeOS.UserInitiatedDisconnect.Result.",
482 transport_name}),
Theo Johnson-Kanue7b494f2021-07-24 03:09:47483 disconnect_result);
484}
485
Theo Johnson-Kanue7abbf7d2021-07-21 02:38:27486void RecordUiSurfaceDisplayed(BluetoothUiSurface ui_surface) {
487 base::UmaHistogramEnumeration("Bluetooth.ChromeOS.UiSurfaceDisplayed",
488 ui_surface);
489}
490
Theo Johnson-Kanu3c389db2021-07-22 21:49:02491void RecordUserInitiatedReconnectionAttemptDuration(
492 absl::optional<ConnectionFailureReason> failure_reason,
493 BluetoothTransport transport,
494 base::TimeDelta duration) {
495 bool success = !failure_reason.has_value();
496 std::string transport_name = GetTransportName(transport);
497
498 if (transport_name.empty()) {
499 return;
500 }
501 std::string success_histogram_name = success ? "Success" : "Failure";
502
503 std::string base_histogram_name = base::StrCat(
504 {"Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Duration.",
505 success_histogram_name});
506 base::UmaHistogramTimes(base_histogram_name, duration);
507 base::UmaHistogramTimes(
508 base::StrCat({base_histogram_name, ".", transport_name}), duration);
509}
510
Theo Johnson-Kanu7956e7d12021-12-14 22:13:40511void RecordSetDeviceNickName(SetNicknameResult set_nickname_result) {
512 base::UmaHistogramEnumeration("Bluetooth.ChromeOS.SetNickname.Result",
513 set_nickname_result);
514}
515
Sarah Huad95e802018-04-12 06:24:29516} // namespace device