[go: nahoru, domu]

blob: 3e40890a4a7cd5a644439174ece7e7625233ef38 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/chromeos/bluetooth_utils.h"
#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "build/chromeos_buildflags.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "chromeos/ash/services/nearby/public/cpp/nearby_client_uuids.h"
#include "chromeos/ash/services/secure_channel/public/cpp/shared/ble_constants.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/lacros/lacros_test_helper.h"
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
namespace device {
namespace {
constexpr char kTestBluetoothDisplayName[] = "test_device_name";
constexpr char kTestBluetoothDeviceAddress[] = "01:02:03:04:05:06";
constexpr char kHIDServiceUUID[] = "1812";
constexpr char kSecurityKeyServiceUUID[] = "FFFD";
constexpr char kUnexpectedServiceUUID[] = "1234";
constexpr uint8_t kLimitedDiscoveryFlag = 0x01;
constexpr uint8_t kGeneralDiscoveryFlag = 0x02;
const BluetoothDevice::ServiceDataMap kTestServiceDataMap = {
{BluetoothUUID(kHIDServiceUUID), {1}}};
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Note: The first 3 hex bytes represent the OUI portion of the address, which
// indicates the device vendor. In this case, "64:16:7F:**:**:**" represents a
// device manufactured by Poly.
constexpr char kFakePolyDeviceAddress[] = "64:16:7F:12:34:56";
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
const size_t kMaxDevicesForFilter = 5;
} // namespace
class BluetoothUtilsTest : public testing::Test {
protected:
BluetoothUtilsTest() = default;
BluetoothUtilsTest(const BluetoothUtilsTest&) = delete;
BluetoothUtilsTest& operator=(const BluetoothUtilsTest&) = delete;
base::HistogramTester histogram_tester;
void SetUp() override {
BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
}
MockBluetoothDevice* AddMockBluetoothDeviceToAdapter(
BluetoothTransport transport) {
auto mock_bluetooth_device =
std::make_unique<testing::NiceMock<MockBluetoothDevice>>(
adapter_.get(), 0 /* bluetooth_class */, kTestBluetoothDisplayName,
kTestBluetoothDeviceAddress, false /* paired */,
false /* connected */);
ON_CALL(*mock_bluetooth_device, GetType)
.WillByDefault(testing::Return(transport));
auto* mock_bluetooth_device_ptr = mock_bluetooth_device.get();
adapter_->AddMockDevice(std::move(mock_bluetooth_device));
return mock_bluetooth_device_ptr;
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
void AddMockPolyDeviceToAdapter() {
MockBluetoothDevice* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
ON_CALL(*mock_bluetooth_device, GetName)
.WillByDefault(testing::Return(absl::nullopt));
ON_CALL(*mock_bluetooth_device, GetDeviceType)
.WillByDefault(testing::Return(BluetoothDeviceType::UNKNOWN));
ON_CALL(*mock_bluetooth_device, GetAddress)
.WillByDefault(testing::Return(kFakePolyDeviceAddress));
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
MockBluetoothAdapter* adapter() { return adapter_.get(); }
MockBluetoothDevice* GetMockBluetoothDevice(size_t index) {
return static_cast<MockBluetoothDevice*>(
adapter()->GetMockDevices()[index]);
}
void VerifyFilterBluetoothDeviceList(BluetoothFilterType filter_type,
size_t num_expected_remaining_devices) {
BluetoothAdapter::DeviceList filtered_device_list =
FilterBluetoothDeviceList(adapter_->GetMockDevices(), filter_type,
kMaxDevicesForFilter);
EXPECT_EQ(num_expected_remaining_devices, filtered_device_list.size());
}
private:
base::test::TaskEnvironment task_environment_;
scoped_refptr<MockBluetoothAdapter> adapter_ =
base::MakeRefCounted<testing::NiceMock<MockBluetoothAdapter>>();
#if BUILDFLAG(IS_CHROMEOS_LACROS)
chromeos::ScopedLacrosServiceTestHelper scoped_lacros_service_test_helper_;
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
};
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterAll_NoDevicesFiltered) {
// If BluetoothFilterType::KNOWN were passed, this device would otherwise be
// filtered out, but we expect it to not be.
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
VerifyFilterBluetoothDeviceList(BluetoothFilterType::ALL,
1u /* num_expected_remaining_devices */);
}
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterAll_MaxDevicesExceeded) {
for (size_t i = 0; i < kMaxDevicesForFilter * 2; ++i)
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
VerifyFilterBluetoothDeviceList(
BluetoothFilterType::ALL,
kMaxDevicesForFilter /* num_expected_remaining_devices */);
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_AlwaysKeepBondedDevices) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
EXPECT_CALL(*mock_bluetooth_device, GetDeviceType)
.WillRepeatedly(testing::Return(BluetoothDeviceType::PHONE));
EXPECT_CALL(*mock_bluetooth_device, IsPaired)
.WillRepeatedly(testing::Return(true));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
EXPECT_CALL(*mock_bluetooth_device, IsBonded)
.WillRepeatedly(testing::Return(true));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
1u /* num_expected_remaining_devices */);
}
#else
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_AlwaysKeepPairedDevices) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
EXPECT_CALL(*mock_bluetooth_device, IsPaired)
.WillRepeatedly(testing::Return(true));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
1u /* num_expected_remaining_devices */);
}
#endif
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_FilterPairedPhone) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
EXPECT_CALL(*mock_bluetooth_device, IsPaired)
.WillRepeatedly(testing::Return(true));
ON_CALL(*mock_bluetooth_device, GetDeviceType)
.WillByDefault(testing::Return(BluetoothDeviceType::PHONE));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveInvalidDevices) {
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_INVALID);
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_KeepClassicDevicesWithNames) {
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
1u /* num_expected_remaining_devices */);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Regression test for b/228118615.
TEST_F(BluetoothUtilsTest, ShowPolyDevice_PolyFlagEnabled) {
// Poly devices should not be filtered out, regardless of device type.
AddMockPolyDeviceToAdapter();
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
1u /* num_expected_remaining_devices */);
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveClassicDevicesWithoutNames) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
EXPECT_CALL(*mock_bluetooth_device, GetName)
.WillOnce(testing::Return(absl::nullopt));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveBleDevicesWithoutExpectedUuids) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device->AddUUID(device::BluetoothUUID(kUnexpectedServiceUUID));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveBleDevicesNonDiscoverable) {
auto* mock_bluetooth_device_1 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_1->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
mock_bluetooth_device_1->UpdateAdvertisementData(
1 /* rssi */, 0 /* flags */, BluetoothDevice::UUIDList(),
absl::nullopt /* tx_power */, kTestServiceDataMap,
BluetoothDevice::ManufacturerDataMap());
auto* mock_bluetooth_device_2 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_2->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
mock_bluetooth_device_2->UpdateAdvertisementData(
1 /* rssi */, kLimitedDiscoveryFlag /* flags */,
BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
auto* mock_bluetooth_device_3 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_3->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
mock_bluetooth_device_3->UpdateAdvertisementData(
1 /* rssi */, kGeneralDiscoveryFlag /* flags */,
BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
auto* mock_bluetooth_device_4 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_4->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
mock_bluetooth_device_4->UpdateAdvertisementData(
1 /* rssi */, kLimitedDiscoveryFlag | kGeneralDiscoveryFlag /* flags */,
BluetoothDevice::UUIDList(), absl::nullopt /* tx_power */,
kTestServiceDataMap, BluetoothDevice::ManufacturerDataMap());
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
3u /* num_expected_remaining_devices */);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveDevicesWithUnsupportedUuids) {
// These UUIDs are specific to Nearby Share and Phone Hub and are used to
// identify devices that should be filtered from the UI that otherwise would
// not have been correctly identified. These devices should always be filtered
// from the UI. For more information see b/219627324.
std::vector<BluetoothUUID> unsupported_uuids =
ash::nearby::GetNearbyClientUuids();
unsupported_uuids.push_back(
BluetoothUUID(ash::secure_channel::kGattServerUuid));
for (const auto& uuid : unsupported_uuids) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL);
mock_bluetooth_device->AddUUID(uuid);
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_KeepBleDevicesWithExpectedUuids) {
auto* mock_bluetooth_device_1 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_1->AddUUID(device::BluetoothUUID(kHIDServiceUUID));
auto* mock_bluetooth_device_2 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
mock_bluetooth_device_2->AddUUID(
device::BluetoothUUID(kSecurityKeyServiceUUID));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
2u /* num_expected_remaining_devices */);
}
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_KeepDualDevicesWithNamesAndAppearances) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL);
EXPECT_CALL(*mock_bluetooth_device, GetDeviceType)
.WillRepeatedly(testing::Return(BluetoothDeviceType::AUDIO));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
1u /* num_expected_remaining_devices */);
}
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_DualDevicesWithoutAppearances_RemoveWithFilterFlagEnabled) {
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL);
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(
BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_AppearanceComputer_RemoveWithFilterFlagEnabled) {
auto* mock_bluetooth_device_1 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_CLASSIC);
EXPECT_CALL(*mock_bluetooth_device_1, GetDeviceType)
.WillOnce(testing::Return(BluetoothDeviceType::COMPUTER));
auto* mock_bluetooth_device_2 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_LE);
EXPECT_CALL(*mock_bluetooth_device_2, GetDeviceType)
.WillOnce(testing::Return(BluetoothDeviceType::COMPUTER));
auto* mock_bluetooth_device_3 =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL);
EXPECT_CALL(*mock_bluetooth_device_3, GetDeviceType)
.WillOnce(testing::Return(BluetoothDeviceType::COMPUTER));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(BluetoothUtilsTest,
TestFilterBluetoothDeviceList_FilterKnown_RemoveAppearancePhone) {
auto* mock_bluetooth_device =
AddMockBluetoothDeviceToAdapter(BLUETOOTH_TRANSPORT_DUAL);
ON_CALL(*mock_bluetooth_device, GetDeviceType)
.WillByDefault(testing::Return(BluetoothDeviceType::PHONE));
VerifyFilterBluetoothDeviceList(BluetoothFilterType::KNOWN,
0u /* num_expected_remaining_devices */);
}
TEST_F(BluetoothUtilsTest, TestUiSurfaceDisplayedMetric) {
RecordUiSurfaceDisplayed(BluetoothUiSurface::kSettingsDeviceListSubpage);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.UiSurfaceDisplayed",
BluetoothUiSurface::kSettingsDeviceListSubpage, 1);
RecordUiSurfaceDisplayed(BluetoothUiSurface::kSettingsDeviceDetailSubpage);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.UiSurfaceDisplayed",
BluetoothUiSurface::kSettingsDeviceListSubpage, 1);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.UiSurfaceDisplayed",
BluetoothUiSurface::kSettingsDeviceDetailSubpage, 1);
}
TEST_F(BluetoothUtilsTest, TestPairMetric) {
size_t total_count = 0;
auto assert_histograms = [&](device::ConnectionFailureReason failure_reason) {
histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.Pairing.Result", 0,
total_count);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Result.Classic", 0, total_count);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Duration.Failure", 2000, total_count);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Duration.Failure.Classic", 2000,
total_count);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Result.FailureReason", failure_reason, 1);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Result.FailureReason.Classic",
failure_reason, 1);
};
auto assert_filtered_failure_histograms =
[&](device::ConnectionFailureReason error, size_t expected_count) {
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Result.FilteredFailureReason", error,
expected_count);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.Pairing.Result.FilteredFailureReason.Classic",
error, expected_count);
};
RecordPairingResult(device::ConnectionFailureReason::kAuthFailed,
device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC,
base::Seconds(2));
total_count++;
assert_histograms(device::ConnectionFailureReason::kAuthFailed);
assert_filtered_failure_histograms(
device::ConnectionFailureReason::kAuthFailed, /*expected_count=*/1);
RecordPairingResult(device::ConnectionFailureReason::kAuthCanceled,
device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC,
base::Seconds(2));
total_count++;
assert_histograms(device::ConnectionFailureReason::kAuthCanceled);
assert_filtered_failure_histograms(
device::ConnectionFailureReason::kAuthCanceled,
/*expected_count=*/0);
RecordPairingResult(device::ConnectionFailureReason::kAuthRejected,
device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC,
base::Seconds(2));
total_count++;
assert_histograms(device::ConnectionFailureReason::kAuthRejected);
assert_filtered_failure_histograms(
device::ConnectionFailureReason::kAuthRejected,
/*expected_count=*/0);
RecordPairingResult(device::ConnectionFailureReason::kInprogress,
device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC,
base::Seconds(2));
total_count++;
assert_histograms(device::ConnectionFailureReason::kInprogress);
assert_filtered_failure_histograms(
device::ConnectionFailureReason::kInprogress, /*expected_count=*/1);
}
TEST_F(BluetoothUtilsTest, TestUserAttemptedReconnectionMetric) {
RecordUserInitiatedReconnectionAttemptDuration(
device::ConnectionFailureReason::kFailed,
device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC,
base::Seconds(2));
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Duration.Failure",
2000, 1);
histogram_tester.ExpectBucketCount(
"Bluetooth.ChromeOS.UserInitiatedReconnectionAttempt.Duration.Failure."
"Classic",
2000, 1);
}
TEST_F(BluetoothUtilsTest, TestDisconnectMetric) {
RecordDeviceDisconnect(BluetoothDeviceType::MOUSE);
histogram_tester.ExpectBucketCount("Bluetooth.ChromeOS.DeviceDisconnect",
BluetoothDeviceType::MOUSE, 1);
}
} // namespace device