// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/hid_detection/hid_detection_manager_impl.h"
#include "ash/constants/ash_features.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/hid_detection/bluetooth_hid_detector.h"
#include "chromeos/ash/components/hid_detection/fake_bluetooth_hid_detector.h"
#include "chromeos/ash/components/hid_detection/hid_detection_utils.h"
#include "services/device/public/cpp/hid/fake_input_service_linux.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash::hid_detection {
namespace {
using BluetoothHidMetadata = BluetoothHidDetector::BluetoothHidMetadata;
using BluetoothHidType = BluetoothHidDetector::BluetoothHidType;
using InputMetadata = HidDetectionManager::InputMetadata;
using InputState = HidDetectionManager::InputState;
using InputDeviceType = device::mojom::InputDeviceType;
using InputDevicesStatus = BluetoothHidDetector::InputDevicesStatus;
const char kTestHidName[] = "testName";
const char kTestPinCode[] = "123456";
enum TestHidType {
kMouse,
kTouchpad,
kKeyboard,
kTouchscreen,
kTablet,
};
class FakeHidDetectionManagerDelegate : public HidDetectionManager::Delegate {
public:
~FakeHidDetectionManagerDelegate() override = default;
size_t num_hid_detection_status_changed_calls() const {
return num_hid_detection_status_changed_calls_;
}
const std::optional<HidDetectionManager::HidDetectionStatus>&
last_hid_detection_status() const {
return last_hid_detection_status_;
}
private:
// HidDetectionManager::Delegate:
void OnHidDetectionStatusChanged(
HidDetectionManager::HidDetectionStatus status) override {
++num_hid_detection_status_changed_calls_;
last_hid_detection_status_ = std::move(status);
}
size_t num_hid_detection_status_changed_calls_ = 0u;
std::optional<HidDetectionManager::HidDetectionStatus>
last_hid_detection_status_;
};
} // namespace
class HidDetectionManagerImplTest : public testing::Test {
protected:
HidDetectionManagerImplTest() = default;
HidDetectionManagerImplTest(const HidDetectionManagerImplTest&) = delete;
HidDetectionManagerImplTest& operator=(const HidDetectionManagerImplTest&) =
delete;
~HidDetectionManagerImplTest() override = default;
// testing::Test:
void SetUp() override {
auto fake_bluetooth_hid_detector =
std::make_unique<FakeBluetoothHidDetector>();
fake_bluetooth_hid_detector_ = fake_bluetooth_hid_detector.get();
hid_detection_manager_ = std::make_unique<HidDetectionManagerImpl>(
/*device_service=*/nullptr);
hid_detection_manager_->SetBluetoothHidDetectorForTest(
std::move(fake_bluetooth_hid_detector));
HidDetectionManagerImpl::SetInputDeviceManagerBinderForTest(
base::BindRepeating(&device::FakeInputServiceLinux::Bind,
base::Unretained(&fake_input_service_)));
}
void TearDown() override {
HidDetectionManagerImpl::SetInputDeviceManagerBinderForTest(
base::NullCallback());
}
std::optional<bool> GetIsHidDetectionRequired() {
std::optional<bool> result;
hid_detection_manager_->GetIsHidDetectionRequired(
base::BindLambdaForTesting(
[&result](bool is_required) { result = is_required; }));
base::RunLoop().RunUntilIdle();
return result;
}
void StartHidDetection() {
EXPECT_FALSE(
fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
hid_detection_manager_->StartHidDetection(&delegate_);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
}
void StopHidDetection(bool should_be_using_bluetooth) {
EXPECT_TRUE(
fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
hid_detection_manager_->StopHidDetection();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
fake_bluetooth_hid_detector_->is_bluetooth_hid_detection_active());
EXPECT_EQ(should_be_using_bluetooth,
fake_bluetooth_hid_detector_->is_using_bluetooth());
}
size_t GetNumHidDetectionStatusChangedCalls() {
return delegate_.num_hid_detection_status_changed_calls();
}
const std::optional<HidDetectionManager::HidDetectionStatus>&
GetLastHidDetectionStatus() {
return delegate_.last_hid_detection_status();
}
void AddDevice(TestHidType hid_type,
InputDeviceType device_type,
std::string* id_out = nullptr,
const char* name = NULL) {
AddDevice(std::vector{hid_type}, device_type, id_out, name);
}
void AddDevice(const std::vector<TestHidType>& hid_types,
InputDeviceType device_type,
std::string* id_out = nullptr,
const char* name = NULL) {
auto device = device::mojom::InputDeviceInfo::New();
device->id = num_devices_created_++;
if (id_out)
*id_out = device->id;
device->name = name == NULL ? device->id : name;
device->subsystem = device::mojom::InputDeviceSubsystem::SUBSYSTEM_INPUT;
device->type = device_type;
for (const auto& hid_type : hid_types) {
switch (hid_type) {
case kMouse:
device->is_mouse = true;
break;
case kTouchpad:
device->is_touchpad = true;
break;
case kKeyboard:
device->is_keyboard = true;
break;
case kTouchscreen:
device->is_touchscreen = true;
break;
case kTablet:
device->is_tablet = true;
break;
}
}
fake_input_service_.AddDevice(std::move(device));
base::RunLoop().RunUntilIdle();
}
void RemoveDevice(const std::string& id) {
fake_input_service_.RemoveDevice(id);
base::RunLoop().RunUntilIdle();
}
void SimulatePairingStarted(
BluetoothHidDetector::BluetoothHidMetadata pairing_device) {
fake_bluetooth_hid_detector_->SimulatePairingStarted(
std::move(pairing_device));
base::RunLoop().RunUntilIdle();
}
void SimulatePairingCodeRequired(
const BluetoothHidPairingState& pairing_state) {
fake_bluetooth_hid_detector_->SetPairingState(BluetoothHidPairingState{
pairing_state.code, pairing_state.num_keys_entered});
base::RunLoop().RunUntilIdle();
}
void SimulatePairingSessionEnded() {
fake_bluetooth_hid_detector_->SimulatePairingSessionEnded();
base::RunLoop().RunUntilIdle();
}
void AssertHidDetectionStatus(
InputMetadata pointer_metadata,
InputMetadata keyboard_metadata,
bool touchscreen_detected,
const std::optional<BluetoothHidPairingState>& pairing_state) {
EXPECT_EQ(pointer_metadata.state,
GetLastHidDetectionStatus()->pointer_metadata.state);
EXPECT_EQ(pointer_metadata.detected_hid_name,
GetLastHidDetectionStatus()->pointer_metadata.detected_hid_name);
EXPECT_EQ(keyboard_metadata.state,
GetLastHidDetectionStatus()->keyboard_metadata.state);
EXPECT_EQ(keyboard_metadata.detected_hid_name,
GetLastHidDetectionStatus()->keyboard_metadata.detected_hid_name);
EXPECT_EQ(touchscreen_detected,
GetLastHidDetectionStatus()->touchscreen_detected);
EXPECT_EQ(pairing_state.has_value(),
GetLastHidDetectionStatus()->pairing_state.has_value());
if (pairing_state.has_value()) {
EXPECT_EQ(pairing_state->code,
GetLastHidDetectionStatus()->pairing_state->code);
EXPECT_EQ(pairing_state->num_keys_entered,
GetLastHidDetectionStatus()->pairing_state->num_keys_entered);
}
}
void AssertInputDevicesStatus(InputDevicesStatus input_devices_status) {
EXPECT_EQ(input_devices_status.pointer_is_missing,
fake_bluetooth_hid_detector_->input_devices_status()
.pointer_is_missing);
EXPECT_EQ(input_devices_status.keyboard_is_missing,
fake_bluetooth_hid_detector_->input_devices_status()
.keyboard_is_missing);
}
void AssertInitialHidsMissingCount(HidsMissing hids_missing, int count) {
histogram_tester_.ExpectBucketCount(
"OOBE.HidDetectionScreen.InitialHidsMissing", hids_missing, count);
histogram_tester_.ExpectTotalCount(
"OOBE.HidDetectionScreen.InitialHidsMissing", count);
}
void AssertHidConnectedCount(HidType hid_type, int count, int total_count) {
histogram_tester_.ExpectBucketCount("OOBE.HidDetectionScreen.HidConnected",
hid_type, count);
histogram_tester_.ExpectTotalCount("OOBE.HidDetectionScreen.HidConnected",
total_count);
}
void AssertHidDisconnectedCount(HidType hid_type,
int count,
int total_count) {
histogram_tester_.ExpectBucketCount(
"OOBE.HidDetectionScreen.HidDisconnected", hid_type, count);
histogram_tester_.ExpectTotalCount(
"OOBE.HidDetectionScreen.HidDisconnected", total_count);
}
size_t GetNumSetInputDevicesStatusCalls() {
return fake_bluetooth_hid_detector_->num_set_input_devices_status_calls();
}
private:
base::test::TaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
base::HistogramTester histogram_tester_;
device::FakeInputServiceLinux fake_input_service_;
size_t num_devices_created_ = 0;
FakeHidDetectionManagerDelegate delegate_;
raw_ptr<FakeBluetoothHidDetector, DanglingUntriaged>
fake_bluetooth_hid_detector_ = nullptr;
std::unique_ptr<hid_detection::HidDetectionManagerImpl>
hid_detection_manager_;
};
TEST_F(HidDetectionManagerImplTest,
GetIsHidDetectionRequired_NoDevicesConnected) {
AssertInitialHidsMissingCount(HidsMissing::kPointerAndKeyboard, /*count=*/0);
std::optional<bool> is_hid_detection_required = GetIsHidDetectionRequired();
ASSERT_TRUE(is_hid_detection_required.has_value());
ASSERT_TRUE(is_hid_detection_required.value());
AssertInitialHidsMissingCount(HidsMissing::kPointerAndKeyboard, /*count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
GetIsHidDetectionRequired_OnlyPointerConnected) {
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_USB);
AssertInitialHidsMissingCount(HidsMissing::kKeyboard, /*count=*/0);
std::optional<bool> is_hid_detection_required = GetIsHidDetectionRequired();
ASSERT_TRUE(is_hid_detection_required.has_value());
ASSERT_TRUE(is_hid_detection_required.value());
AssertInitialHidsMissingCount(HidsMissing::kKeyboard, /*count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
GetIsHidDetectionRequired_OnlyKeyboardConnected) {
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_USB);
AssertInitialHidsMissingCount(HidsMissing::kPointer, /*count=*/0);
std::optional<bool> is_hid_detection_required = GetIsHidDetectionRequired();
ASSERT_TRUE(is_hid_detection_required.has_value());
ASSERT_TRUE(is_hid_detection_required.value());
AssertInitialHidsMissingCount(HidsMissing::kPointer, /*count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
GetIsHidDetectionRequired_PointerAndKeyboardConnected) {
AddDevice(TestHidType::kTouchpad, InputDeviceType::TYPE_USB);
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_USB);
AssertInitialHidsMissingCount(HidsMissing::kPointer, /*count=*/0);
std::optional<bool> is_hid_detection_required = GetIsHidDetectionRequired();
ASSERT_TRUE(is_hid_detection_required.has_value());
ASSERT_FALSE(is_hid_detection_required.value());
AssertInitialHidsMissingCount(HidsMissing::kNone, /*count=*/1);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_TouchscreenPreConnected) {
AddDevice(TestHidType::kTouchscreen, InputDeviceType::TYPE_SERIO);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/true,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_PointerPreConnected) {
std::string device_id;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_SERIO, &device_id);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnected, device_id},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_KeyboardPreConnected) {
std::string device_id;
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_SERIO, &device_id);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kConnected, device_id},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_NonHidConnectedDisconnected) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
// Add a device without a HID type. This should not inform the delegate
// and no state changed.
std::vector<TestHidType> hid_types{};
std::string device_id;
AddDevice(hid_types, InputDeviceType::TYPE_USB, &device_id);
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
RemoveDevice(device_id);
StopHidDetection(/*should_be_using_bluetooth=*/false);
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_TouchscreenConnectedDisconnected) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
std::string touchscreen_id1;
AddDevice(TestHidType::kTouchscreen, InputDeviceType::TYPE_SERIO,
&touchscreen_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/true,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
RemoveDevice(touchscreen_id1);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/false);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Add another touchscreen device. This should not inform the delegate.
std::string touchscreen_id2;
AddDevice(TestHidType::kTouchscreen, InputDeviceType::TYPE_SERIO,
&touchscreen_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
// Remove the touchscreen device. This should not inform the delegate.
RemoveDevice(touchscreen_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_PointerConnectedDisconnected) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
std::string pointer_id1;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_BLUETOOTH, &pointer_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, pointer_id1},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/1,
/*total_count=*/1);
AssertHidDisconnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
RemoveDevice(pointer_id1);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kBluetoothPointer, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/false);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Add another pointer device. This should not inform the delegate.
std::string pointer_id2;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_USB, &pointer_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/1,
/*total_count=*/1);
// Remove the pointer device. This should not inform the delegate.
RemoveDevice(pointer_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kBluetoothPointer, /*count=*/1,
/*total_count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_KeyboardConnectedDisconnected) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kUsbKeyboard, /*count=*/0,
/*total_count=*/0);
std::string keyboard_id1;
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_USB, &keyboard_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kConnectedViaUsb, keyboard_id1},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
AssertHidConnectedCount(HidType::kUsbKeyboard, /*count=*/1,
/*total_count=*/1);
AssertHidDisconnectedCount(HidType::kUsbKeyboard, /*count=*/0,
/*total_count=*/0);
RemoveDevice(keyboard_id1);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kUsbKeyboard, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/false);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Add another keyboard device. This should not inform the delegate.
std::string keyboard_id2;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_USB, &keyboard_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kUsbKeyboard, /*count=*/1,
/*total_count=*/1);
// Remove the keyboard device. This should not inform the delegate.
RemoveDevice(keyboard_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kUsbKeyboard, /*count=*/1,
/*total_count=*/1);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_MultipleTouchscreensDisconnected) {
std::string device_id1;
AddDevice(TestHidType::kTablet, InputDeviceType::TYPE_SERIO, &device_id1);
std::string device_id2;
AddDevice(TestHidType::kTouchscreen, InputDeviceType::TYPE_SERIO,
&device_id2);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/true,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/0,
/*total_count=*/0);
// Remove the first touchscreen device. The second touchscreen should be
// detected and delegate notified.
RemoveDevice(device_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/true,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kTouchscreen, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_MultiplePointersDisconnected) {
std::string device_id1;
AddDevice(TestHidType::kTouchpad, InputDeviceType::TYPE_UNKNOWN, &device_id1);
std::string device_id2;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_BLUETOOTH, &device_id2);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnected, device_id1},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kUnknownPointer, /*count=*/0,
/*total_count=*/0);
// Remove the first pointer. The second pointer should be detected and
// delegate notified.
RemoveDevice(device_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, device_id2},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
AssertHidDisconnectedCount(HidType::kUnknownPointer, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/true);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_MultipleKeyboardsDisconnected) {
std::string device_id1;
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_BLUETOOTH,
&device_id1);
std::string device_id2;
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_SERIO, &device_id2);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, device_id1},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
AssertHidDisconnectedCount(HidType::kBluetoothKeyboard, /*count=*/0,
/*total_count=*/0);
// Remove the first keyboard. The second keyboard should be detected and
// delegate notified.
RemoveDevice(device_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kConnected, device_id2},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
AssertHidDisconnectedCount(HidType::kBluetoothKeyboard, /*count=*/1,
/*total_count=*/1);
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_DeviceMultipleHidTypesDisconnected) {
std::string device_id1;
AddDevice(TestHidType::kTouchpad, InputDeviceType::TYPE_USB, &device_id1);
std::string device_id2;
std::vector<TestHidType> hid_types{TestHidType::kKeyboard,
TestHidType::kTouchpad};
AddDevice(hid_types, InputDeviceType::TYPE_SERIO, &device_id2);
std::string device_id3;
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_UNKNOWN, &device_id3);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/{InputState::kConnected, device_id2},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
AssertHidDisconnectedCount(HidType::kUsbPointer, /*count=*/0,
/*total_count=*/0);
RemoveDevice(device_id1);
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnected, device_id2},
/*keyboard_metadata=*/{InputState::kConnected, device_id2},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
AssertHidDisconnectedCount(HidType::kUsbPointer, /*count=*/1,
/*total_count=*/1);
AssertHidDisconnectedCount(HidType::kSerialPointer, /*count=*/0,
/*total_count=*/1);
RemoveDevice(device_id2);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kConnected, device_id3},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
AssertHidDisconnectedCount(HidType::kUsbPointer, /*count=*/1,
/*total_count=*/2);
AssertHidDisconnectedCount(HidType::kSerialPointer, /*count=*/1,
/*total_count=*/2);
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
// TODO(gordonseto): Test add device for type already connected, remove device
// for type already connected.
TEST_F(HidDetectionManagerImplTest, StartDetection_BluetoothPointerSuccess) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
SimulatePairingStarted(
BluetoothHidMetadata{kTestHidName, BluetoothHidType::kPointer});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing succeeding.
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_BLUETOOTH,
/*id_out=*/nullptr, kTestHidName);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/1,
/*total_count=*/1);
SimulatePairingSessionEnded();
EXPECT_EQ(4u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/true);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_BluetoothPointerFailure) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
SimulatePairingStarted(
BluetoothHidMetadata{kTestHidName, BluetoothHidType::kPointer});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing failing.
SimulatePairingSessionEnded();
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_BluetoothKeyboardSuccess) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothKeyboard, /*count=*/0,
/*total_count=*/0);
SimulatePairingStarted(
BluetoothHidMetadata{kTestHidName, BluetoothHidType::kKeyboard});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
std::optional<BluetoothHidPairingState> pairing_state =
BluetoothHidPairingState{kTestPinCode, /*num_keys_entered=*/6};
SimulatePairingCodeRequired(pairing_state.value());
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false, pairing_state);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing succeeding.
AddDevice(TestHidType::kKeyboard, InputDeviceType::TYPE_BLUETOOTH,
/*id_out=*/nullptr, kTestHidName);
AssertHidConnectedCount(HidType::kBluetoothKeyboard, /*count=*/1,
/*total_count=*/1);
EXPECT_EQ(4u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false, pairing_state);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
SimulatePairingSessionEnded();
EXPECT_EQ(5u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = false});
StopHidDetection(/*should_be_using_bluetooth=*/true);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_BluetoothKeyboardFailure) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
SimulatePairingStarted(
BluetoothHidMetadata{kTestHidName, BluetoothHidType::kKeyboard});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
std::optional<BluetoothHidPairingState> pairing_state =
BluetoothHidPairingState{kTestPinCode, /*num_keys_entered=*/6};
SimulatePairingCodeRequired(pairing_state.value());
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching, /*detected_hid_name=*/""},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false, pairing_state);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing failing.
SimulatePairingSessionEnded();
EXPECT_EQ(4u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_BluetoothKeyboardPointerComboSuccess) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
SimulatePairingStarted(BluetoothHidMetadata{
kTestHidName, BluetoothHidType::kKeyboardPointerCombo});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing succeeding.
AddDevice(std::vector{TestHidType::kKeyboard, TestHidType::kTouchpad},
InputDeviceType::TYPE_BLUETOOTH,
/*id_out=*/nullptr, kTestHidName);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
SimulatePairingSessionEnded();
EXPECT_EQ(4u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
StopHidDetection(/*should_be_using_bluetooth=*/true);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_BluetoothKeyboardComboFailure) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
SimulatePairingStarted(BluetoothHidMetadata{
kTestHidName, BluetoothHidType::kKeyboardPointerCombo});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
// Simulate the pairing failing.
SimulatePairingSessionEnded();
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest,
StartDetection_BluetoothKeyboardPointerComboPointerPreConnected) {
std::string device_id1;
AddDevice(TestHidType::kTouchpad, InputDeviceType::TYPE_USB, &device_id1);
EXPECT_EQ(0u, GetNumHidDetectionStatusChangedCalls());
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
SimulatePairingStarted(BluetoothHidMetadata{
kTestHidName, BluetoothHidType::kKeyboardPointerCombo});
EXPECT_EQ(2u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/{InputState::kPairingViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
// Simulate the pairing succeeding.
std::string device_id2;
AddDevice(std::vector{TestHidType::kKeyboard, TestHidType::kTouchpad},
InputDeviceType::TYPE_BLUETOOTH, &device_id2, kTestHidName);
EXPECT_EQ(3u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
SimulatePairingSessionEnded();
EXPECT_EQ(4u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/{InputState::kPairedViaBluetooth, kTestHidName},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(1u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = false});
// Disconnect the Bluetooth device.
RemoveDevice(device_id2);
EXPECT_EQ(5u, GetNumHidDetectionStatusChangedCalls());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kConnectedViaUsb, device_id1},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(2u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = false, .keyboard_is_missing = true});
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
TEST_F(HidDetectionManagerImplTest, StartDetection_VirtialMouseConnected) {
StartHidDetection();
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
std::string pointer_id1;
AddDevice(TestHidType::kMouse, InputDeviceType::TYPE_BLUETOOTH, &pointer_id1,
"VIRTUAL_SUSPEND_UHID");
EXPECT_EQ(1u, GetNumHidDetectionStatusChangedCalls());
ASSERT_TRUE(GetLastHidDetectionStatus().has_value());
AssertHidDetectionStatus(
/*pointer_metadata=*/{InputState::kSearching,
/*detected_hid_name=*/""},
/*keyboard_metadata=*/
{InputState::kSearching, /*detected_hid_name=*/""},
/*touchscreen_detected=*/false,
/*pairing_state=*/std::nullopt);
EXPECT_EQ(0u, GetNumSetInputDevicesStatusCalls());
AssertInputDevicesStatus(
{.pointer_is_missing = true, .keyboard_is_missing = true});
AssertHidConnectedCount(HidType::kBluetoothPointer, /*count=*/0,
/*total_count=*/0);
StopHidDetection(/*should_be_using_bluetooth=*/false);
}
} // namespace ash::hid_detection