| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/quick_pair/pairing/retroactive_pairing_detector_impl.h" |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/public/cpp/session/session_controller.h" |
| #include "ash/quick_pair/common/constants.h" |
| #include "ash/quick_pair/common/device.h" |
| #include "ash/quick_pair/common/protocol.h" |
| #include "ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_lookup_impl.h" |
| #include "ash/quick_pair/message_stream/message_stream.h" |
| #include "ash/quick_pair/repository/fast_pair_repository.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "base/containers/contains.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/time/time.h" |
| #include "components/cross_device/logging/logging.h" |
| #include "device/bluetooth/bluetooth_adapter.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| |
| namespace { |
| |
| bool ShouldBeEnabledForLoginStatus(ash::LoginStatus status) { |
| switch (status) { |
| case ash::LoginStatus::NOT_LOGGED_IN: |
| case ash::LoginStatus::LOCKED: |
| case ash::LoginStatus::KIOSK_APP: |
| case ash::LoginStatus::GUEST: |
| case ash::LoginStatus::PUBLIC: |
| return false; |
| case ash::LoginStatus::USER: |
| case ash::LoginStatus::CHILD: |
| default: |
| return true; |
| } |
| } |
| |
| // Enforce a 60 second timeout to discover a device for the retroactive pairing |
| // scenario to align with Android implementation and adhere to the Fast Pair |
| // spec where providers can only allow account keys to be written within the |
| // 60 seconds following classic Bluetooth pairing: |
| // https://developers.google.com/nearby/fast-pair/specifications/extensions/retroactiveacctkey#RetroactivelyWritingAccountKey |
| // The device is expected to enforce this requirement, however as mentioned |
| // above, to align with Android, ChromeOS will include consideration for the |
| // 60 seconds expected to retroactively write the account key. |
| constexpr base::TimeDelta kRetroactiveDevicePairingTimeout = base::Seconds(60); |
| |
| } // namespace |
| |
| namespace ash { |
| namespace quick_pair { |
| |
| RetroactivePairingDetectorImpl::RetroactivePairingDetectorImpl( |
| PairerBroker* pairer_broker, |
| MessageStreamLookup* message_stream_lookup) |
| : pairer_broker_(pairer_broker), |
| message_stream_lookup_(message_stream_lookup) { |
| // If there is no signed in user, don't enabled the retroactive pairing |
| // scenario, so don't initiate any objects or observations, but store the |
| // pointers in the case that we get logged in later on. |
| if (!ShouldBeEnabledForLoginStatus( |
| Shell::Get()->session_controller()->login_status())) { |
| CD_LOG(INFO, Feature::FP) |
| << __func__ |
| << ": No logged in user to enable retroactive pairing scenario"; |
| |
| // Observe log in events in the case the login was delayed. |
| shell_observation_.Observe(Shell::Get()->session_controller()); |
| return; |
| } |
| |
| // If we get to this point in the constructor, it means that the user is |
| // logged in to enable this scenario, so we can being our observations. If we |
| // get any log in events, we know to ignore them, since we already |
| // instantiated our retroactive pairing detector. |
| retroactive_pairing_detector_instatiated_ = true; |
| |
| device::BluetoothAdapterFactory::Get()->GetAdapter( |
| base::BindOnce(&RetroactivePairingDetectorImpl::OnGetAdapter, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| message_stream_lookup_observation_.Observe(message_stream_lookup_.get()); |
| pairer_broker_observation_.Observe(pairer_broker_.get()); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnLoginStatusChanged( |
| LoginStatus login_status) { |
| if (!ShouldBeEnabledForLoginStatus(login_status) || !pairer_broker_ || |
| !message_stream_lookup_ || retroactive_pairing_detector_instatiated_) { |
| return; |
| } |
| |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ |
| << ": Logged in user, instantiate retroactive pairing scenario."; |
| |
| retroactive_pairing_detector_instatiated_ = true; |
| |
| device::BluetoothAdapterFactory::Get()->GetAdapter( |
| base::BindOnce(&RetroactivePairingDetectorImpl::OnGetAdapter, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| message_stream_lookup_observation_.Observe(message_stream_lookup_.get()); |
| pairer_broker_observation_.Observe(pairer_broker_.get()); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnGetAdapter( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| adapter_ = adapter; |
| adapter_observation_.Reset(); |
| adapter_observation_.Observe(adapter_.get()); |
| } |
| |
| RetroactivePairingDetectorImpl::~RetroactivePairingDetectorImpl() { |
| // Remove any observation of remaining MessageStreams. |
| for (auto it = message_streams_.begin(); it != message_streams_.end(); it++) { |
| it->second->RemoveObserver(this); |
| } |
| } |
| |
| void RetroactivePairingDetectorImpl::AddObserver( |
| RetroactivePairingDetector::Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void RetroactivePairingDetectorImpl::RemoveObserver( |
| RetroactivePairingDetector::Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnDevicePaired( |
| scoped_refptr<Device> device) { |
| // The classic address is assigned to the Device during the |
| // initial Fast Pair pairing protocol and if it doesn't exist, |
| // then it wasn't properly paired during initial Fast Pair |
| // pairing. |
| if (!device->classic_address()) |
| return; |
| |
| // The Bluetooth Adapter system event `DevicePairedChanged` fires before |
| // Fast Pair's `OnDevicePaired`, and a Fast Pair pairing is expected to have |
| // both events. If a device is Fast Paired, it is already inserted in the |
| // |potential_retroactive_addresses_| in `DevicePairedChanged`; we need to |
| // remove it to prevent a false positive. |
| if (base::Contains(potential_retroactive_addresses_, |
| device->classic_address().value())) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ |
| << ": encountered a false positive for a potential retroactive pairing " |
| "device. Removing device at address = " |
| << device->classic_address().value(); |
| RemoveDeviceInformation(device->classic_address().value()); |
| return; |
| } |
| } |
| |
| void RetroactivePairingDetectorImpl::DevicePairedChanged( |
| device::BluetoothAdapter* adapter, |
| device::BluetoothDevice* device, |
| bool new_paired_status) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": " << device->GetNameForDisplay() |
| << " new_paired_status=" << (new_paired_status ? "paired" : "not paired"); |
| // This event fires whenever a device pairing has changed with the adapter. |
| // If the |new_paired_status| is false, it means a device was unpaired with |
| // the adapter, so we early return since it would not be a device to |
| // retroactively pair to. |
| if (!new_paired_status) { |
| return; |
| } |
| |
| // Both classic paired and Fast paired devices call this function, so we |
| // have to add the device to |potential_retroactive_addresses_|. We expect |
| // devices paired via Fast Pair to always call `OnDevicePaired` after calling |
| // this function, which will remove the device from |
| // |potential_retroactive_addresses_|. |
| const std::string& classic_address = device->GetAddress(); |
| potential_retroactive_addresses_.insert(classic_address); |
| AddDevicePairingInformation(classic_address); |
| |
| // In order to confirm that this device is a retroactive pairing, we need to |
| // first check if it has already been saved to the user's account. If it has |
| // already been saved, we don't want to prompt the user to save a device |
| // again. |
| FastPairRepository::Get()->IsDeviceSavedToAccount( |
| classic_address, |
| base::BindOnce(&RetroactivePairingDetectorImpl::AttemptRetroactivePairing, |
| weak_ptr_factory_.GetWeakPtr(), classic_address)); |
| } |
| |
| void RetroactivePairingDetectorImpl::AttemptRetroactivePairing( |
| const std::string& classic_address, |
| bool is_device_saved_to_account) { |
| // This check handles the case where the request for checked if the device is |
| // saved takes longer than expected. We register `AttemptRetroactivePairing` |
| // as a callback for when this request completes, but it gets called after |
| // we get the call to `OnDevicePaired`, which removes the device information. |
| // If the device is removed via `OnDevicePaired`, this indicated a Fast Pair |
| // pairing event, in which case we will never show a retroactive pairing |
| // notification, so we can stop the flow here for this device. |
| if (!base::Contains(potential_retroactive_addresses_, classic_address)) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": device at " << classic_address |
| << ": was removed before call to Footprints completed"; |
| return; |
| } |
| |
| if (is_device_saved_to_account) { |
| CD_LOG(INFO, Feature::FP) |
| << __func__ << ": device already saved to user's account"; |
| RemoveDeviceInformation(classic_address); |
| return; |
| } |
| |
| device::BluetoothDevice* device = adapter_->GetDevice(classic_address); |
| if (!device) { |
| CD_LOG(WARNING, Feature::FP) |
| << __func__ << ": Lost device to potentially retroactively pair to."; |
| RemoveDeviceInformation(classic_address); |
| return; |
| } |
| |
| CD_LOG(VERBOSE, Feature::FP) << __func__ << ": device = " << classic_address; |
| |
| // For BLE devices, since we cannot connect to a message stream to retrieve |
| // the model ID and the BLE address is already known, the only remaining |
| // parameter needed is the model ID, which we retrieve via GATT characteristic |
| if (ash::features::IsFastPairHIDEnabled() && |
| device->GetType() == device::BLUETOOTH_TRANSPORT_LE) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": BLE device detected, creating GATT connection"; |
| CreateGattConnection(device); |
| return; |
| } |
| |
| // Attempt to retrieve a MessageStream instance immediately, if it was |
| // already connected. |
| MessageStream* message_stream = |
| message_stream_lookup_->GetMessageStream(classic_address); |
| if (!message_stream) |
| return; |
| |
| message_streams_[classic_address] = message_stream; |
| GetModelIdAndAddressFromMessageStream(classic_address, message_stream); |
| } |
| |
| void RetroactivePairingDetectorImpl::CreateGattConnection( |
| device::BluetoothDevice* device) { |
| auto* fast_pair_gatt_service_client = |
| FastPairGattServiceClientLookup::GetInstance()->Get(device); |
| |
| if (fast_pair_gatt_service_client) { |
| if (fast_pair_gatt_service_client->IsConnected()) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ |
| << ": Reusing existing GATT service client to retrieve model ID"; |
| fast_pair_gatt_service_client->ReadModelIdAsync( |
| base::BindOnce(&RetroactivePairingDetectorImpl::OnReadModelId, |
| weak_ptr_factory_.GetWeakPtr(), device->GetAddress())); |
| return; |
| } else { |
| // If the previous gatt service client did not connect successfully |
| // or is no longer connected, erase it before attempting to create a new |
| // gatt connection for the device. |
| FastPairGattServiceClientLookup::GetInstance()->Erase(device); |
| } |
| } |
| |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": Creating new GATT service client to retrieve model ID"; |
| |
| FastPairGattServiceClientLookup::GetInstance()->Create( |
| adapter_, device, |
| base::BindOnce( |
| &RetroactivePairingDetectorImpl::OnGattClientInitializedCallback, |
| weak_ptr_factory_.GetWeakPtr(), device)); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnGattClientInitializedCallback( |
| device::BluetoothDevice* device, |
| absl::optional<PairFailure> failure) { |
| if (failure) { |
| CD_LOG(WARNING, Feature::FP) |
| << __func__ |
| << ": Failed to initialize GATT service client with failure = " |
| << failure.value(); |
| return; |
| } |
| |
| // If |OnGattClientInitializedCallback| is called without a failure, |
| // |device*| is expected to exist and be valid. |
| auto* fast_pair_gatt_service_client = |
| FastPairGattServiceClientLookup::GetInstance()->Get(device); |
| |
| if (!fast_pair_gatt_service_client || |
| !fast_pair_gatt_service_client->IsConnected()) { |
| CD_LOG(WARNING, Feature::FP) << __func__ |
| << ": Fast Pair Gatt Service Client failed to " |
| "be created or is no longer connected."; |
| FastPairGattServiceClientLookup::GetInstance()->Erase(device); |
| return; |
| } |
| |
| CD_LOG(VERBOSE, Feature::FP) << __func__ |
| << ": Fast Pair GATT service client initialized " |
| "successfully. Reading Model ID."; |
| |
| fast_pair_gatt_service_client->ReadModelIdAsync( |
| base::BindOnce(&RetroactivePairingDetectorImpl::OnReadModelId, |
| weak_ptr_factory_.GetWeakPtr(), device->GetAddress())); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnReadModelId( |
| const std::string& address, |
| absl::optional<device::BluetoothGattService::GattErrorCode> error_code, |
| const std::vector<uint8_t>& value) { |
| if (error_code) { |
| CD_LOG(WARNING, Feature::FP) |
| << __func__ << ": Failed to read model ID with failure = " |
| << static_cast<uint32_t>(error_code.value()); |
| return; |
| } |
| |
| if (value.size() != 3) { |
| CD_LOG(WARNING, Feature::FP) << __func__ << ": model ID malformed."; |
| return; |
| } |
| |
| std::string model_id; |
| for (auto byte : value) { |
| model_id.append(base::StringPrintf("%02X", byte)); |
| } |
| |
| CD_LOG(INFO, Feature::FP) << __func__ << ": Model ID " << model_id |
| << " found for device " << address; |
| NotifyDeviceFound(model_id, address, address); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnMessageStreamConnected( |
| const std::string& device_address, |
| MessageStream* message_stream) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__ << ":" << device_address; |
| if (!message_stream) |
| return; |
| |
| if (!base::Contains(potential_retroactive_addresses_, device_address)) |
| return; |
| |
| message_streams_[device_address] = message_stream; |
| GetModelIdAndAddressFromMessageStream(device_address, message_stream); |
| } |
| |
| void RetroactivePairingDetectorImpl::AddDevicePairingInformation( |
| const std::string& device_address) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__; |
| |
| // There is potential for the device at |device_address| to already be in |
| // the map (in the case of repairing for example). If it is already in the |
| // map, update the timeout with the new timestamp. If it isn't already in |
| // the map, create a value with default empty values, and add the expiry |
| // timeout. |
| device_pairing_information_[device_address].expiry_timestamp = |
| base::Time::Now() + kRetroactiveDevicePairingTimeout; |
| |
| // Anytime |device_pairing_information_| is updated, parse list to remove |
| // expired devices. |
| RemoveExpiredDevicesFromStoredDeviceData(); |
| } |
| |
| void RetroactivePairingDetectorImpl::GetModelIdAndAddressFromMessageStream( |
| const std::string& device_address, |
| MessageStream* message_stream) { |
| DCHECK(message_stream); |
| |
| // The device at |device_address| is expected to be added in |
| // `AddDevicePairingInformation` once discovered. |
| DCHECK(base::Contains(device_pairing_information_, device_address)); |
| |
| // If the MessageStream is immediately available and |DevicePairedChanged| |
| // fires before FastPair's |OnDevicePaired|, it might be possible for us to |
| // find a false positive for a retroactive pairing scenario which we mitigate |
| // here. |
| if (!base::Contains(potential_retroactive_addresses_, device_address)) |
| return; |
| |
| // Iterate over messages for ble address and model id, which is what we |
| // need for retroactive pairing. |
| for (auto& message : message_stream->messages()) { |
| if (message->is_model_id()) { |
| device_pairing_information_[device_address].model_id = |
| message->get_model_id(); |
| } else if (message->is_ble_address_update()) { |
| device_pairing_information_[device_address].ble_address = |
| message->get_ble_address_update(); |
| } |
| } |
| |
| // If we don't have model id and ble address for device, then we will add |
| // ourselves as an observer and wait for these messages to come in. There is |
| // a possibility that they will not come in if the device does not |
| // support retroactive pairing. |
| if (device_pairing_information_[device_address].model_id.empty() || |
| device_pairing_information_[device_address].ble_address.empty()) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": BLE address = " |
| << (device_pairing_information_[device_address].ble_address.empty() |
| ? "empty" |
| : device_pairing_information_[device_address].ble_address) |
| << " model ID = " |
| << (device_pairing_information_[device_address].model_id.empty() |
| ? "empty" |
| : device_pairing_information_[device_address].model_id) |
| << " observing Message Stream for future messages for device = " |
| << device_address; |
| message_stream->AddObserver(this); |
| return; |
| } |
| |
| // At this point, we have both the model id and BLE address for the device, |
| // but we check if it has reached its expiry timeout. If so, we do not |
| // notify of the scenario being detected. `CheckAndRemoveIfDeviceExpired` |
| // will remove corresponding device information if it has expired. |
| if (CheckAndRemoveIfDeviceExpired(device_address)) { |
| return; |
| } |
| |
| NotifyDeviceFound(device_pairing_information_[device_address].model_id, |
| device_pairing_information_[device_address].ble_address, |
| device_address); |
| } |
| |
| bool RetroactivePairingDetectorImpl::CheckAndRemoveIfDeviceExpired( |
| const std::string& device_address) { |
| if (base::Time::Now() >= |
| device_pairing_information_[device_address].expiry_timestamp) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": device at " << device_address |
| << " has exceeded the time allotted for detecting " |
| "retroactive scenario. Removing device information."; |
| RemoveDeviceInformation(device_address); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void RetroactivePairingDetectorImpl::OnModelIdMessage( |
| const std::string& device_address, |
| const std::string& model_id) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__ << ": model id = " << model_id |
| << "for device = " << device_address; |
| device_pairing_information_[device_address].model_id = model_id; |
| CheckPairingInformation(device_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnBleAddressUpdateMessage( |
| const std::string& device_address, |
| const std::string& ble_address) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__ << ": ble address " << ble_address |
| << " for device = " << device_address; |
| device_pairing_information_[device_address].ble_address = ble_address; |
| CheckPairingInformation(device_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::CheckPairingInformation( |
| const std::string& device_address) { |
| // The device at |device_address| is expected to be added in |
| // `AddDevicePairingInformation` once discovered. |
| DCHECK(base::Contains(device_pairing_information_, device_address)); |
| |
| // If the MessageStream is immediately available and |DevicePairedChanged| |
| // fires before FastPair's |OnDevicePaired|, it might be possible for us to |
| // find a false positive for a retroactive pairing scenario which we mitigate |
| // here. Also check if the device has expired for detecting scenario, if so |
| // do not continue. `CheckAndRemoveIfDeviceExpired` will remove device |
| // information if it has expired. |
| if (!base::Contains(potential_retroactive_addresses_, device_address) || |
| CheckAndRemoveIfDeviceExpired(device_address)) { |
| return; |
| } |
| |
| if (device_pairing_information_[device_address].model_id.empty() || |
| device_pairing_information_[device_address].ble_address.empty()) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ |
| << ": don't have both model id and ble address for device = " |
| << device_address; |
| return; |
| } |
| |
| NotifyDeviceFound(device_pairing_information_[device_address].model_id, |
| device_pairing_information_[device_address].ble_address, |
| device_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnDisconnected( |
| const std::string& device_address) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__; |
| message_streams_[device_address]->RemoveObserver(this); |
| message_streams_.erase(device_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnMessageStreamDestroyed( |
| const std::string& device_address) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__; |
| message_streams_[device_address]->RemoveObserver(this); |
| message_streams_.erase(device_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::NotifyDeviceFound( |
| const std::string& model_id, |
| const std::string& ble_address, |
| const std::string& classic_address) { |
| CD_LOG(INFO, Feature::FP) << __func__; |
| |
| // Before we notify that the device is found for retroactive pairing, we |
| // should check if the user is opted in to saving devices to their account. |
| // The reason why we check this every time we want to notify a device is found |
| // rather than having the user's opt-in status determine whether or not the |
| // retroactive pairing scenario is instantiated is because the user might be |
| // opted out when the user initially logs in to the Chromebook (when this |
| // class is created), but then opted-in later one, and then unable to save |
| // devices to their account, or vice versa. By checking every time we want |
| // to notify a device is found, we can accurately reflect a user's status |
| // in the moment. This is flagged on whether the user has the Fast Pair |
| // Saved Devices flag enabled. |
| if (features::IsFastPairSavedDevicesEnabled() && |
| features::IsFastPairSavedDevicesStrictOptInEnabled()) { |
| FastPairRepository::Get()->CheckOptInStatus( |
| base::BindOnce(&RetroactivePairingDetectorImpl::OnCheckOptInStatus, |
| weak_ptr_factory_.GetWeakPtr(), model_id, ble_address, |
| classic_address)); |
| return; |
| } |
| |
| // If the SavedDevices flag is not enabled, we don't have to check opt in |
| // status and can move forward with verifying the device found. |
| VerifyDeviceFound(model_id, ble_address, classic_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::OnCheckOptInStatus( |
| const std::string& model_id, |
| const std::string& ble_address, |
| const std::string& classic_address, |
| nearby::fastpair::OptInStatus status) { |
| CD_LOG(INFO, Feature::FP) << __func__; |
| |
| if (status != nearby::fastpair::OptInStatus::STATUS_OPTED_IN) { |
| CD_LOG(INFO, Feature::FP) |
| << __func__ |
| << ": User is not opted in to save devices to their account"; |
| RemoveDeviceInformation(classic_address); |
| return; |
| } |
| |
| VerifyDeviceFound(model_id, ble_address, classic_address); |
| } |
| |
| void RetroactivePairingDetectorImpl::VerifyDeviceFound( |
| const std::string& model_id, |
| const std::string& ble_address, |
| const std::string& classic_address) { |
| CD_LOG(INFO, Feature::FP) << __func__; |
| |
| device::BluetoothDevice* bluetooth_device = |
| adapter_->GetDevice(classic_address); |
| if (!bluetooth_device) { |
| CD_LOG(WARNING, Feature::FP) |
| << __func__ << ": Lost device to potentially retroactively pair to."; |
| RemoveDeviceInformation(classic_address); |
| return; |
| } |
| |
| auto device = base::MakeRefCounted<Device>(model_id, ble_address, |
| Protocol::kFastPairRetroactive); |
| device->set_classic_address(classic_address); |
| device->set_display_name(bluetooth_device->GetName()); |
| CD_LOG(INFO, Feature::FP) |
| << __func__ << ": Found device for Retroactive Pairing " << device; |
| |
| for (auto& observer : observers_) |
| observer.OnRetroactivePairFound(device); |
| |
| DCHECK(device->classic_address()); |
| RemoveDeviceInformation(device->classic_address().value()); |
| } |
| |
| void RetroactivePairingDetectorImpl::RemoveDeviceInformation( |
| const std::string& device_address) { |
| CD_LOG(VERBOSE, Feature::FP) << __func__ << ": device = " << device_address; |
| RemoveDeviceInformationHelper(device_address); |
| |
| // Anytime |device_pairing_information_| is updated, parse list to remove |
| // expired devices. |
| RemoveExpiredDevicesFromStoredDeviceData(); |
| } |
| |
| void RetroactivePairingDetectorImpl::RemoveDeviceInformationHelper( |
| const std::string& device_address) { |
| CD_LOG(INFO, Feature::FP) << __func__; |
| potential_retroactive_addresses_.erase(device_address); |
| device_pairing_information_.erase(device_address); |
| |
| // We can potentially get to a state where we need to RemoveDeviceInformation |
| // before the MessageStreams are observed, connected, and/or added to our |
| // list here if we get a false positive instance of a potential retroactive |
| // pairing device. |
| if (base::Contains(message_streams_, device_address)) { |
| message_streams_[device_address]->RemoveObserver(this); |
| message_streams_.erase(device_address); |
| } |
| } |
| |
| void RetroactivePairingDetectorImpl:: |
| RemoveExpiredDevicesFromStoredDeviceData() { |
| // If the RetroactivePairingDetector never receives the model id or |
| // BLE address from the MessageStream, it will not be removed in |
| // `CheckPairingInformation` if it has exceeded the allotted time for |
| // detecting the scenario (kDetectRetroactiveScenarioTimeout). We clean up |
| // these devices here. |
| std::vector<std::string> devices_to_remove; |
| for (auto it = device_pairing_information_.begin(); |
| it != device_pairing_information_.end(); ++it) { |
| if (base::Time::Now() >= it->second.expiry_timestamp) { |
| devices_to_remove.push_back(it->first); |
| } |
| } |
| |
| for (const std::string& device_address : devices_to_remove) { |
| CD_LOG(VERBOSE, Feature::FP) |
| << __func__ << ": Removing device at " << device_address |
| << "that has exceeded the time allotted for detecting " |
| "retroactive scenario."; |
| RemoveDeviceInformationHelper(device_address); |
| } |
| } |
| |
| void RetroactivePairingDetectorImpl::OnPairFailure(scoped_refptr<Device> device, |
| PairFailure failure) {} |
| |
| void RetroactivePairingDetectorImpl::OnAccountKeyWrite( |
| scoped_refptr<Device> device, |
| absl::optional<AccountKeyFailure> error) {} |
| |
| } // namespace quick_pair |
| } // namespace ash |