123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- // Copyright 2022 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/components/hid_detection/bluetooth_hid_detector_impl.h"
- #include "ash/public/cpp/bluetooth_config_service.h"
- #include "base/strings/utf_string_conversions.h"
- #include "components/device_event_log/device_event_log.h"
- #include "hid_detection_utils.h"
- namespace ash::hid_detection {
- namespace {
- using chromeos::bluetooth_config::mojom::BluetoothDevicePropertiesPtr;
- using chromeos::bluetooth_config::mojom::BluetoothSystemState;
- using chromeos::bluetooth_config::mojom::DeviceType;
- using chromeos::bluetooth_config::mojom::KeyEnteredHandler;
- // Returns the BluetoothHidType corresponding with |device|'s device type, or
- // absl::nullopt if |device| is not a HID.
- absl::optional<BluetoothHidDetector::BluetoothHidType> GetBluetoothHidType(
- const BluetoothDevicePropertiesPtr& device) {
- switch (device->device_type) {
- case DeviceType::kMouse:
- [[fallthrough]];
- case DeviceType::kTablet:
- return BluetoothHidDetector::BluetoothHidType::kPointer;
- case DeviceType::kKeyboard:
- return BluetoothHidDetector::BluetoothHidType::kKeyboard;
- case DeviceType::kKeyboardMouseCombo:
- return BluetoothHidDetector::BluetoothHidType::kKeyboardPointerCombo;
- default:
- return absl::nullopt;
- }
- }
- } // namespace
- BluetoothHidDetectorImpl::BluetoothHidDetectorImpl() = default;
- BluetoothHidDetectorImpl::~BluetoothHidDetectorImpl() {
- DCHECK_EQ(kNotStarted, state_) << " HID detection must be stopped before "
- << "BluetoothHidDetectorImpl is destroyed.";
- }
- void BluetoothHidDetectorImpl::SetInputDevicesStatus(
- InputDevicesStatus input_devices_status) {
- HID_LOG(EVENT) << "Input devices status set, pointer missing: "
- << input_devices_status.pointer_is_missing
- << ", keyboard missing: "
- << input_devices_status.keyboard_is_missing;
- input_devices_status_ = input_devices_status;
- if (!current_pairing_device_)
- return;
- absl::optional<BluetoothHidDetector::BluetoothHidType>
- current_pairing_device_hid_type =
- GetBluetoothHidType(current_pairing_device_.value());
- DCHECK(current_pairing_device_hid_type)
- << current_pairing_device_.value()->id
- << " does not have a valid HID type, device type: "
- << current_pairing_device_.value()->device_type;
- if (IsHidTypeMissing(current_pairing_device_hid_type.value()))
- return;
- // If the HID type of |current_pairing_device_| is no longer missing, this can
- // mean 2 things:
- // 1. |current_pairing_device_| has successfully finished pairing, and the
- // client of this class has now invoked this method to inform this class
- // that the HID type is no longer missing. Clear the current pairing state
- // and process the next device in the queue.
- // 2. |current_pairing_device_| is currently pairing, and a device of the
- // same type has been detected as connected. Clear the current pairing
- // state, which will cause the current pairing to cancel, and process the
- // next device in the queue.
- HID_LOG(EVENT) << "Device type of "
- << current_pairing_device_.value()->device_type << " for "
- << current_pairing_device_.value()->id
- << " is no longer missing";
- ClearCurrentPairingState();
- }
- const BluetoothHidDetector::BluetoothHidDetectionStatus
- BluetoothHidDetectorImpl::GetBluetoothHidDetectionStatus() {
- if (!current_pairing_device_.has_value()) {
- return BluetoothHidDetectionStatus(
- /*current_pairing_device*/ absl::nullopt,
- /*pairing_state=*/absl::nullopt);
- }
- absl::optional<BluetoothHidPairingState> pairing_state;
- if (current_pairing_state_.has_value()) {
- pairing_state = BluetoothHidPairingState{
- current_pairing_state_.value().code,
- current_pairing_state_.value().num_keys_entered};
- }
- absl::optional<BluetoothHidType> hid_type =
- GetBluetoothHidType(current_pairing_device_.value());
- DCHECK(hid_type) << " |current_pairing_device_| has an invalid HID type";
- return BluetoothHidDetectionStatus{
- BluetoothHidMetadata{
- base::UTF16ToUTF8(current_pairing_device_.value()->public_name),
- hid_type.value()},
- std::move(pairing_state)};
- }
- void BluetoothHidDetectorImpl::PerformStartBluetoothHidDetection(
- InputDevicesStatus input_devices_status) {
- DCHECK_EQ(kNotStarted, state_);
- HID_LOG(EVENT) << "Starting Bluetooth HID detection, pointer missing: "
- << input_devices_status.pointer_is_missing
- << ", keyboard missing: "
- << input_devices_status.keyboard_is_missing;
- input_devices_status_ = input_devices_status;
- state_ = kStarting;
- num_pairing_attempts_ = 0;
- GetBluetoothConfigService(
- cros_bluetooth_config_remote_.BindNewPipeAndPassReceiver());
- cros_bluetooth_config_remote_->ObserveSystemProperties(
- system_properties_observer_receiver_.BindNewPipeAndPassRemote());
- }
- void BluetoothHidDetectorImpl::PerformStopBluetoothHidDetection(
- bool is_using_bluetooth) {
- DCHECK_NE(kNotStarted, state_)
- << " Call to StopBluetoothHidDetection() while "
- << "HID detection is inactive.";
- HID_LOG(EVENT) << "Stopping Bluetooth HID detection, |is_using_bluetooth|: "
- << is_using_bluetooth;
- hid_detection::RecordBluetoothPairingAttempts(num_pairing_attempts_);
- state_ = kNotStarted;
- cros_bluetooth_config_remote_->SetBluetoothHidDetectionInactive(
- is_using_bluetooth);
- cros_bluetooth_config_remote_.reset();
- system_properties_observer_receiver_.reset();
- ResetDiscoveryState();
- }
- void BluetoothHidDetectorImpl::OnPropertiesUpdated(
- chromeos::bluetooth_config::mojom::BluetoothSystemPropertiesPtr
- properties) {
- switch (state_) {
- case kNotStarted:
- NOTREACHED() << "SystemPropertiesObserver should not be bound while in "
- "state |kNotStarted|";
- return;
- case kStarting:
- if (properties->system_state == BluetoothSystemState::kEnabled) {
- HID_LOG(EVENT)
- << "Bluetooth adapter is already enabled, starting discovery";
- state_ = kDetecting;
- cros_bluetooth_config_remote_->StartDiscovery(
- bluetooth_discovery_delegate_receiver_.BindNewPipeAndPassRemote());
- } else if (properties->system_state == BluetoothSystemState::kDisabled ||
- properties->system_state == BluetoothSystemState::kDisabling) {
- HID_LOG(EVENT) << "Bluetooth adapter is disabled or disabling, "
- << "enabling adapter";
- state_ = kEnablingAdapter;
- cros_bluetooth_config_remote_->SetBluetoothHidDetectionActive();
- } else {
- HID_LOG(EVENT)
- << "Bluetooth adapter is unavailable or enabling, waiting "
- << "for next state change";
- }
- return;
- case kEnablingAdapter:
- if (properties->system_state == BluetoothSystemState::kEnabled) {
- HID_LOG(EVENT)
- << "Bluetooth adapter has become enabled, starting discovery";
- state_ = kDetecting;
- cros_bluetooth_config_remote_->StartDiscovery(
- bluetooth_discovery_delegate_receiver_.BindNewPipeAndPassRemote());
- }
- return;
- case kDetecting:
- if (properties->system_state != BluetoothSystemState::kEnabled) {
- HID_LOG(EVENT) << "Bluetooth adapter has stopped being enabled while "
- << "Bluetooth HID detection is in progress";
- state_ = kStoppedExternally;
- }
- return;
- case kStoppedExternally:
- if (properties->system_state == BluetoothSystemState::kEnabled) {
- HID_LOG(EVENT) << "Bluetooth adapter has become enabled after being "
- << "unenabled externally, starting discovery";
- state_ = kDetecting;
- cros_bluetooth_config_remote_->StartDiscovery(
- bluetooth_discovery_delegate_receiver_.BindNewPipeAndPassRemote());
- }
- return;
- }
- }
- void BluetoothHidDetectorImpl::OnBluetoothDiscoveryStarted(
- mojo::PendingRemote<chromeos::bluetooth_config::mojom::DevicePairingHandler>
- handler) {
- HID_LOG(EVENT) << "Bluetooth discovery started.";
- DCHECK(!device_pairing_handler_remote_);
- device_pairing_handler_remote_.Bind(std::move(handler));
- }
- void BluetoothHidDetectorImpl::OnBluetoothDiscoveryStopped() {
- HID_LOG(EVENT) << "Bluetooth discovery stopped.";
- ResetDiscoveryState();
- }
- void BluetoothHidDetectorImpl::OnDiscoveredDevicesListChanged(
- std::vector<BluetoothDevicePropertiesPtr> discovered_devices) {
- for (const auto& discovered_device : discovered_devices) {
- if (!ShouldAttemptToPairWithDevice(discovered_device))
- continue;
- if (queued_device_ids_.contains(discovered_device->id))
- continue;
- queued_device_ids_.insert(discovered_device->id);
- queue_->emplace(discovered_device.Clone());
- HID_LOG(EVENT) << "Queuing device: " << discovered_device->id << ". ["
- << queue_->size() << "] devices now in queue.";
- }
- ProcessQueue();
- }
- void BluetoothHidDetectorImpl::RequestPinCode(RequestPinCodeCallback callback) {
- DCHECK(current_pairing_device_)
- << "RequestPinCode() called with no |current_pairing_device_|";
- // RequestPinCode auth is not attributed to HIDs, cancel the pairing.
- HID_LOG(EVENT) << "RequestPinCode auth required for "
- << current_pairing_device_.value()->id
- << ", cancelling pairing";
- ClearCurrentPairingState();
- }
- void BluetoothHidDetectorImpl::RequestPasskey(RequestPasskeyCallback callback) {
- DCHECK(current_pairing_device_)
- << "RequestPasskey() called with no |current_pairing_device_|";
- // RequestPasskey auth is not attributed to HIDs, cancel the pairing.
- HID_LOG(EVENT) << "RequestPasskey auth required for "
- << current_pairing_device_.value()->id
- << ", cancelling pairing";
- ClearCurrentPairingState();
- }
- void BluetoothHidDetectorImpl::DisplayPinCode(
- const std::string& pin_code,
- mojo::PendingReceiver<KeyEnteredHandler> handler) {
- DCHECK(current_pairing_device_)
- << "DisplayPinCode() called with no |current_pairing_device_|";
- HID_LOG(EVENT) << "DisplayPinCode auth required for "
- << current_pairing_device_.value()->id
- << ", pin code: " << pin_code;
- RequirePairingCode(pin_code, std::move(handler));
- }
- void BluetoothHidDetectorImpl::DisplayPasskey(
- const std::string& passkey,
- mojo::PendingReceiver<KeyEnteredHandler> handler) {
- DCHECK(current_pairing_device_)
- << "DisplayPasskey() called with no |current_pairing_device_|";
- HID_LOG(EVENT) << "DisplayPasskey auth required for "
- << current_pairing_device_.value()->id
- << ", passkey: " << passkey;
- RequirePairingCode(passkey, std::move(handler));
- }
- void BluetoothHidDetectorImpl::ConfirmPasskey(const std::string& passkey,
- ConfirmPasskeyCallback callback) {
- DCHECK(current_pairing_device_)
- << "ConfirmPasskey() called with no |current_pairing_device_|";
- // ConfirmPasskey auth is not attributed to HIDs, cancel the pairing.
- HID_LOG(EVENT) << "ConfirmPasskey auth required for "
- << current_pairing_device_.value()->id
- << ", cancelling pairing";
- ClearCurrentPairingState();
- }
- void BluetoothHidDetectorImpl::AuthorizePairing(
- AuthorizePairingCallback callback) {
- DCHECK(current_pairing_device_)
- << "AuthorizePairing() called with no |current_pairing_device_|";
- HID_LOG(EVENT) << "AuthorizePairing auth required for "
- << current_pairing_device_.value()->id
- << ", automatically authorizing";
- std::move(callback).Run(/*confirmed=*/true);
- }
- void BluetoothHidDetectorImpl::HandleKeyEntered(uint8_t num_keys_entered) {
- DCHECK(current_pairing_device_)
- << "HandleKeyEntered() called with no |current_pairing_device_|";
- DCHECK(current_pairing_state_)
- << "HandleKeyEntered() called with no |current_pairing_state_|";
- HID_LOG(EVENT) << "HandleKeyEntered called with "
- << static_cast<unsigned>(num_keys_entered) << " keys entered";
- current_pairing_state_->num_keys_entered = num_keys_entered;
- NotifyBluetoothHidDetectionStatusChanged();
- }
- bool BluetoothHidDetectorImpl::IsHidTypeMissing(
- BluetoothHidDetector::BluetoothHidType hid_type) {
- switch (hid_type) {
- case BluetoothHidDetector::BluetoothHidType::kPointer:
- return input_devices_status_.pointer_is_missing;
- case BluetoothHidDetector::BluetoothHidType::kKeyboard:
- return input_devices_status_.keyboard_is_missing;
- case BluetoothHidDetector::BluetoothHidType::kKeyboardPointerCombo:
- return input_devices_status_.pointer_is_missing ||
- input_devices_status_.keyboard_is_missing;
- }
- }
- bool BluetoothHidDetectorImpl::ShouldAttemptToPairWithDevice(
- const BluetoothDevicePropertiesPtr& device) {
- absl::optional<BluetoothHidDetector::BluetoothHidType> hid_type =
- GetBluetoothHidType(device);
- if (!hid_type)
- return false;
- return IsHidTypeMissing(hid_type.value());
- }
- void BluetoothHidDetectorImpl::ProcessQueue() {
- if (current_pairing_device_)
- return;
- if (queue_->empty()) {
- HID_LOG(DEBUG) << "No devices queued";
- return;
- }
- current_pairing_device_ = std::move(queue_->front());
- queue_->pop();
- HID_LOG(EVENT) << "Popped device with id: "
- << current_pairing_device_.value()->id
- << " from front of queue. [" << queue_->size()
- << "] devices now in queue.";
- if (!ShouldAttemptToPairWithDevice(current_pairing_device_.value())) {
- HID_LOG(EVENT) << "Device with id " << current_pairing_device_.value()->id
- << " no longer should be attempted to be paired with, "
- << "processing next device in queue. Device type: "
- << current_pairing_device_.value()->device_type;
- current_pairing_device_.reset();
- ProcessQueue();
- return;
- }
- HID_LOG(EVENT) << "Pairing with device with id: "
- << current_pairing_device_.value()->id;
- ++num_pairing_attempts_;
- device_pairing_handler_remote_->PairDevice(
- current_pairing_device_.value()->id,
- device_pairing_delegate_receiver_.BindNewPipeAndPassRemote(),
- base::BindOnce(&BluetoothHidDetectorImpl::OnPairDevice,
- weak_ptr_factory_.GetWeakPtr(),
- std::make_unique<base::ElapsedTimer>()));
- NotifyBluetoothHidDetectionStatusChanged();
- }
- void BluetoothHidDetectorImpl::OnPairDevice(
- std::unique_ptr<base::ElapsedTimer> pairing_timer,
- chromeos::bluetooth_config::mojom::PairingResult pairing_result) {
- DCHECK(current_pairing_device_)
- << "OnPairDevice() called with no |current_pairing_device_|";
- HID_LOG(EVENT) << "Finished pairing with "
- << current_pairing_device_.value()->id
- << ", result: " << pairing_result << ", [" << queue_->size()
- << "] devices still in queue.";
- const bool success =
- pairing_result ==
- chromeos::bluetooth_config::mojom::PairingResult::kSuccess;
- hid_detection::RecordBluetoothPairingResult(success,
- pairing_timer->Elapsed());
- // If pairing has succeeded, wait for SetInputDevicesStatus() to be called
- // with the corresponding HID type no longer missing.
- if (success) {
- HID_LOG(EVENT)
- << "Pairing succeeded, waiting for input devices status to update.";
- return;
- }
- HID_LOG(ERROR) << "Pairing failed, clearing current pairing state and "
- << "processing the next device in queue.";
- ClearCurrentPairingState();
- }
- void BluetoothHidDetectorImpl::ClearCurrentPairingState() {
- // If there is an ongoing pairing, it will be cancelled. Invalidate the
- // pairing finished callback.
- weak_ptr_factory_.InvalidateWeakPtrs();
- queued_device_ids_.erase(current_pairing_device_.value()->id);
- current_pairing_device_.reset();
- current_pairing_state_.reset();
- device_pairing_delegate_receiver_.reset();
- key_entered_handler_receiver_.reset();
- NotifyBluetoothHidDetectionStatusChanged();
- ProcessQueue();
- }
- void BluetoothHidDetectorImpl::ResetDiscoveryState() {
- // Reset Mojo-related properties.
- bluetooth_discovery_delegate_receiver_.reset();
- device_pairing_handler_remote_.reset();
- device_pairing_delegate_receiver_.reset();
- key_entered_handler_receiver_.reset();
- // Reset queue-related properties.
- current_pairing_device_.reset();
- current_pairing_state_.reset();
- queue_ = std::make_unique<base::queue<
- chromeos::bluetooth_config::mojom::BluetoothDevicePropertiesPtr>>();
- queued_device_ids_.clear();
- // Inform the client that no device is currently pairing.
- NotifyBluetoothHidDetectionStatusChanged();
- }
- void BluetoothHidDetectorImpl::RequirePairingCode(
- const std::string& code,
- mojo::PendingReceiver<chromeos::bluetooth_config::mojom::KeyEnteredHandler>
- handler) {
- DCHECK(!current_pairing_state_) << "RequirePairingCode() called "
- << "with |current_pairing_state_| already "
- << "initialized";
- DCHECK(!key_entered_handler_receiver_.is_bound());
- key_entered_handler_receiver_.Bind(std::move(handler));
- current_pairing_state_ =
- BluetoothHidPairingState{code, /*num_keys_entered=*/0u};
- NotifyBluetoothHidDetectionStatusChanged();
- }
- } // namespace ash::hid_detection
|