[CrOS Bluetooth] Add bluetooth pairing metrics

Bug: b:207588508
Change-Id: I23cbc59aa0af4d3898572698b3c50a6cccc53865
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3331228
Reviewed-by: Gordon Seto <gordonseto@google.com>
Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
Commit-Queue: Theo Johnson-kanu <tjohnsonkanu@google.com>
Cr-Commit-Position: refs/heads/main@{#952564}
diff --git a/chromeos/services/bluetooth_config/device_pairing_handler.cc b/chromeos/services/bluetooth_config/device_pairing_handler.cc
index 39002a0..878c39d 100644
--- a/chromeos/services/bluetooth_config/device_pairing_handler.cc
+++ b/chromeos/services/bluetooth_config/device_pairing_handler.cc
@@ -6,7 +6,10 @@
 
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/time/default_clock.h"
 #include "components/device_event_log/device_event_log.h"
+#include "device/bluetooth/bluetooth_common.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -29,6 +32,47 @@
        passkey_string});
 }
 
+device::BluetoothTransport GetBluetoothTransport(
+    device::BluetoothTransport type) {
+  switch (type) {
+    case device::BLUETOOTH_TRANSPORT_CLASSIC:
+      return device::BLUETOOTH_TRANSPORT_CLASSIC;
+    case device::BLUETOOTH_TRANSPORT_LE:
+      return device::BLUETOOTH_TRANSPORT_LE;
+    case device::BLUETOOTH_TRANSPORT_DUAL:
+      return device::BLUETOOTH_TRANSPORT_DUAL;
+    default:
+      return device::BLUETOOTH_TRANSPORT_INVALID;
+  }
+}
+
+mojom::PairingResult GetPairingResult(
+    absl::optional<device::ConnectionFailureReason> failure_reason) {
+  if (!failure_reason) {
+    return mojom::PairingResult::kSuccess;
+  }
+
+  switch (failure_reason.value()) {
+    case device::ConnectionFailureReason::kAuthTimeout:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kAuthFailed:
+      return mojom::PairingResult::kAuthFailed;
+
+    case device::ConnectionFailureReason::kUnknownError:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kSystemError:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kFailed:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kUnknownConnectionError:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kUnsupportedDevice:
+      FALLTHROUGH;
+    case device::ConnectionFailureReason::kNotConnectable:
+      return mojom::PairingResult::kNonAuthFailure;
+  }
+}
+
 }  // namespace
 
 DevicePairingHandler::DevicePairingHandler(
@@ -50,7 +94,7 @@
         << "Could not cancel pairing for device to due device no longer being "
            "found, identifier: "
         << current_pairing_device_id_;
-    FinishCurrentPairingRequest(mojom::PairingResult::kAuthFailed);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kAuthFailed);
     return;
   }
   device->CancelPairing();
@@ -72,6 +116,7 @@
   // There should only be one PairDevice request at a time.
   CHECK(current_pairing_device_id_.empty());
 
+  pairing_start_timestamp_ = base::Time();
   pair_device_callback_ = std::move(callback);
 
   delegate_.reset();
@@ -84,7 +129,7 @@
     BLUETOOTH_LOG(ERROR) << "Pairing failed due to Bluetooth not being "
                             "enabled, device identifier: "
                          << device_id;
-    FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
     return;
   }
 
@@ -95,7 +140,7 @@
     BLUETOOTH_LOG(ERROR) << "Pairing failed due to device not being "
                             "found, identifier: "
                          << device_id;
-    FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
     return;
   }
 
@@ -168,7 +213,7 @@
 void DevicePairingHandler::OnDeviceConnect(
     absl::optional<device::BluetoothDevice::ConnectErrorCode> error_code) {
   if (!error_code.has_value()) {
-    FinishCurrentPairingRequest(mojom::PairingResult::kSuccess);
+    FinishCurrentPairingRequest(absl::nullopt);
     NotifyFinished();
     return;
   }
@@ -182,21 +227,26 @@
     case ErrorCode::ERROR_AUTH_FAILED:
       FALLTHROUGH;
     case ErrorCode::ERROR_AUTH_REJECTED:
-      FALLTHROUGH;
+      FinishCurrentPairingRequest(device::ConnectionFailureReason::kAuthFailed);
+      return;
     case ErrorCode::ERROR_AUTH_TIMEOUT:
-      FinishCurrentPairingRequest(mojom::PairingResult::kAuthFailed);
+      FinishCurrentPairingRequest(
+          device::ConnectionFailureReason::kAuthTimeout);
       return;
 
     case ErrorCode::ERROR_FAILED:
       FALLTHROUGH;
     case ErrorCode::ERROR_INPROGRESS:
-      FALLTHROUGH;
-    case ErrorCode::ERROR_UNKNOWN:
-      FALLTHROUGH;
-    case ErrorCode::ERROR_UNSUPPORTED_DEVICE:
-      FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+      FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
       return;
-
+    case ErrorCode::ERROR_UNKNOWN:
+      FinishCurrentPairingRequest(
+          device::ConnectionFailureReason::kUnknownError);
+      return;
+    case ErrorCode::ERROR_UNSUPPORTED_DEVICE:
+      FinishCurrentPairingRequest(
+          device::ConnectionFailureReason::kUnsupportedDevice);
+      return;
     default:
       BLUETOOTH_LOG(ERROR) << "Error code is invalid.";
       break;
@@ -210,7 +260,7 @@
         << "OnRequestPinCode failed due to device no longer being "
            "found, identifier: "
         << current_pairing_device_id_;
-    FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
     return;
   }
 
@@ -224,7 +274,7 @@
         << "OnRequestPasskey failed due to device no longer being "
            "found, identifier: "
         << current_pairing_device_id_;
-    FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
     return;
   }
 
@@ -245,7 +295,7 @@
         << "OnConfirmPairing failed due to device no longer being "
            "found, identifier: "
         << current_pairing_device_id_;
-    FinishCurrentPairingRequest(mojom::PairingResult::kNonAuthFailure);
+    FinishCurrentPairingRequest(device::ConnectionFailureReason::kFailed);
     return;
   }
 
@@ -256,9 +306,19 @@
 }
 
 void DevicePairingHandler::FinishCurrentPairingRequest(
-    mojom::PairingResult result) {
+    absl::optional<device::ConnectionFailureReason> failure_reason) {
+  device::BluetoothDevice* device = FindDevice(current_pairing_device_id_);
   current_pairing_device_id_.clear();
-  std::move(pair_device_callback_).Run(result);
+
+  device::BluetoothTransport transport =
+      device ? device->GetType()
+             : device::BluetoothTransport::BLUETOOTH_TRANSPORT_INVALID;
+
+  device::RecordPairingResult(
+      failure_reason, GetBluetoothTransport(transport),
+      base::DefaultClock::GetInstance()->Now() - pairing_start_timestamp_);
+
+  std::move(pair_device_callback_).Run(GetPairingResult(failure_reason));
 }
 
 void DevicePairingHandler::OnDelegateDisconnect() {
diff --git a/chromeos/services/bluetooth_config/device_pairing_handler.h b/chromeos/services/bluetooth_config/device_pairing_handler.h
index 61e5572..c9d0e42 100644
--- a/chromeos/services/bluetooth_config/device_pairing_handler.h
+++ b/chromeos/services/bluetooth_config/device_pairing_handler.h
@@ -11,6 +11,7 @@
 #include "chromeos/services/bluetooth_config/adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
@@ -86,9 +87,10 @@
   void OnRequestPasskey(const std::string& passkey);
   void OnConfirmPairing(bool confirmed);
 
-  // Invokes |pair_device_callback_| with |result| and resets
-  // this class' state to be ready for another pairing request.
-  void FinishCurrentPairingRequest(mojom::PairingResult result);
+  // Invokes |pair_device_callback_| and resets this class' state to be ready
+  // for another pairing request.
+  void FinishCurrentPairingRequest(
+      absl::optional<device::ConnectionFailureReason> failure_reason);
 
   void OnDelegateDisconnect();
 
@@ -97,6 +99,8 @@
   // Flushes queued Mojo messages in unit tests.
   void FlushForTesting();
 
+  base::Time pairing_start_timestamp_;
+
   // The identifier of the device currently being paired with. This is null if
   // there is no in-progress pairing attempt.
   std::string current_pairing_device_id_;
diff --git a/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc b/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
index 2cd20d4..171f039 100644
--- a/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/device_pairing_handler_impl_unittest.cc
@@ -6,10 +6,12 @@
 
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/fake_device_pairing_delegate.h"
 #include "chromeos/services/bluetooth_config/fake_key_entered_handler.h"
+#include "device/bluetooth/chromeos/bluetooth_utils.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -70,6 +72,18 @@
 
   void DestroyHandler() { device_pairing_handler_.reset(); }
 
+  void CheckPairingHistograms(device::BluetoothTransportType type,
+                              int type_count,
+                              int failure_count,
+                              int success_count) {
+    histogram_tester.ExpectBucketCount(
+        "Bluetooth.ChromeOS.Pairing.TransportType", type, type_count);
+    histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.Pairing.Result",
+                                       false, failure_count);
+    histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.Pairing.Result",
+                                       true, success_count);
+  }
+
   void AddDevice(std::string* id_out,
                  AuthType auth_type,
                  uint32_t passkey = kDefaultPinCodeNum) {
@@ -141,6 +155,10 @@
         .WillByDefault(testing::Invoke(
             [this](const uint32_t passkey) { received_passkey_ = passkey; }));
 
+    ON_CALL(*mock_device, GetType()).WillByDefault(testing::Invoke([]() {
+      return device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC;
+    }));
+
     mock_devices_.push_back(std::move(mock_device));
   }
 
@@ -200,6 +218,7 @@
   }
   std::string received_pin_code() const { return received_pin_code_; }
   uint32_t received_passkey() const { return received_passkey_; }
+  base::HistogramTester histogram_tester;
 
  private:
   std::vector<const device::BluetoothDevice*> GetMockDevices() {
@@ -256,6 +275,10 @@
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
 
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
+
   std::unique_ptr<FakeDevicePairingDelegate> delegate2 = PairDevice(device_id2);
   EXPECT_TRUE(delegate2->IsMojoPipeConnected());
 
@@ -263,6 +286,10 @@
   InvokePendingConnectCallback(/*success=*/true);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kSuccess);
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u);
+
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/2, /*failure_count=*/1,
+                         /*success_count=*/1);
 }
 
 TEST_F(DevicePairingHandlerImplTest, DisableBluetoothBeforePairing) {
@@ -278,6 +305,11 @@
   EXPECT_FALSE(HasPendingConnectCallback());
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
+
+  // Pairing result metric is only recorded for valid transport types.
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, DisableBluetoothDuringPairing) {
@@ -295,6 +327,9 @@
   EXPECT_EQ(num_cancel_pairing_calls(), 1u);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, DestroyHandlerBeforeConnectFinishes) {
@@ -311,6 +346,9 @@
 
   // Destroying the handler should call OnPairingAttemptFinished();
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, DestroyHandlerAfterConnectFinishes) {
@@ -332,6 +370,9 @@
 
   // Destroying the handler shouldn't call OnPairingAttemptFinished();
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 1u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/1);
 }
 
 TEST_F(DevicePairingHandlerImplTest, DisconnectDelegateBeforeConnectFinishes) {
@@ -349,6 +390,9 @@
 
   // Disconnecting the pipe should not call OnPairingAttemptFinished().
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest,
@@ -369,6 +413,9 @@
 
   // Disconnecting the pipe should not call OnPairingAttemptFinished().
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest,
@@ -389,6 +436,9 @@
 
   // Disconnecting the pipe should not call OnPairingAttemptFinished().
   EXPECT_EQ(num_pairing_attempt_finished_calls(), 0u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairDeviceNotFound) {
@@ -396,6 +446,9 @@
 
   EXPECT_FALSE(HasPendingConnectCallback());
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCode) {
@@ -410,6 +463,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPinCodeRemoveDevice) {
@@ -427,6 +483,9 @@
   // Failure result should be returned.
   EXPECT_TRUE(received_pin_code().empty());
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskey) {
@@ -441,6 +500,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyRemoveDevice) {
@@ -458,6 +520,9 @@
   // Failure result should be returned.
   EXPECT_EQ(received_passkey(), kUninitializedPasskey);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthRequestPasskeyInvalidKey) {
@@ -479,6 +544,9 @@
   // CancelPairing() should not be called again since we already cancelled the
   // pairing.
   EXPECT_EQ(num_cancel_pairing_calls(), 1u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCode) {
@@ -497,6 +565,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPinCodeDisconnectHandler) {
@@ -515,6 +586,11 @@
   EnterKeys(device_id, /*num_keys_entered=*/6u);
   // Number of keys entered should still be zero since pipe is disconnected.
   EXPECT_EQ(delegate->key_entered_handler()->num_keys_entered(), 0);
+
+  // Metrics is not recorded because pairing did not finish.
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/0, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskey) {
@@ -533,6 +609,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthDisplayPasskeyPadZeroes) {
@@ -547,6 +626,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 
   // Pair a new device.
   std::string device_id2;
@@ -557,6 +639,10 @@
   // Passkey displayed should be a 6-digit number, padded with zeroes if needed.
   EXPECT_EQ(delegate2->displayed_passkey(), "000000");
   EXPECT_TRUE(delegate2->key_entered_handler()->IsMojoPipeConnected());
+  // Expect value to be 1 since pairing was not finished.
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskey) {
@@ -574,6 +660,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 
   // Pair a new device.
   std::string device_id2;
@@ -592,6 +681,9 @@
   // ConfirmPairing() should not be called again, CancelPairing() should.
   EXPECT_EQ(num_confirm_pairing_calls(), 1u);
   EXPECT_EQ(num_cancel_pairing_calls(), 1u);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/2, /*failure_count=*/2,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthConfirmPasskeyRemoveDevice) {
@@ -611,6 +703,9 @@
   // Failure result should be returned.
   EXPECT_EQ(num_confirm_pairing_calls(), 0u);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kNonAuthFailure);
+  CheckPairingHistograms(device::BluetoothTransportType::kInvalid,
+                         /*type_count=*/1, /*failure_count=*/0,
+                         /*success_count=*/0);
 }
 
 TEST_F(DevicePairingHandlerImplTest, PairAuthAuthorizePairing) {
@@ -626,6 +721,9 @@
 
   InvokePendingConnectCallback(/*success=*/false);
   EXPECT_EQ(pairing_result(), mojom::PairingResult::kAuthFailed);
+  CheckPairingHistograms(device::BluetoothTransportType::kClassic,
+                         /*type_count=*/1, /*failure_count=*/1,
+                         /*success_count=*/0);
 }
 
 }  // namespace bluetooth_config
diff --git a/device/bluetooth/chromeos/bluetooth_utils.cc b/device/bluetooth/chromeos/bluetooth_utils.cc
index b0ab4d9a..02c5939c 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.cc
+++ b/device/bluetooth/chromeos/bluetooth_utils.cc
@@ -39,18 +39,6 @@
 
 constexpr base::TimeDelta kMaxDeviceSelectionDuration = base::Seconds(30);
 
-// This enum is tied directly to a UMA enum defined in
-// //tools/metrics/histograms/enums.xml, and should always reflect it (do not
-// change one without changing the other).
-enum class BluetoothTransportType {
-  kUnknown = 0,
-  kClassic = 1,
-  kLE = 2,
-  kDual = 3,
-  kInvalid = 4,
-  kMaxValue = kInvalid
-};
-
 // Get limited number of devices from |devices| and
 // prioritize paired/connecting devices over other devices.
 BluetoothAdapter::DeviceList GetLimitedNumDevices(
diff --git a/device/bluetooth/chromeos/bluetooth_utils.h b/device/bluetooth/chromeos/bluetooth_utils.h
index b78a3e4..d1ec8ae 100644
--- a/device/bluetooth/chromeos/bluetooth_utils.h
+++ b/device/bluetooth/chromeos/bluetooth_utils.h
@@ -98,6 +98,18 @@
   kMaxValue = kSuccess,
 };
 
+// This enum is tied directly to a UMA enum defined in
+// //tools/metrics/histograms/enums.xml, and should always reflect it (do not
+// change one without changing the other).
+enum class BluetoothTransportType {
+  kUnknown = 0,
+  kClassic = 1,
+  kLE = 2,
+  kDual = 3,
+  kInvalid = 4,
+  kMaxValue = kInvalid
+};
+
 // Return filtered devices based on the filter type and max number of devices.
 DEVICE_BLUETOOTH_EXPORT device::BluetoothAdapter::DeviceList
 FilterBluetoothDeviceList(const BluetoothAdapter::DeviceList& devices,