| /* |
| * Copyright 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "hci/le_scanning_reassembler.h" |
| |
| #include <memory> |
| #include <unordered_map> |
| |
| #include "hci/acl_manager.h" |
| #include "hci/controller.h" |
| #include "hci/hci_layer.h" |
| #include "hci/hci_packets.h" |
| #include "hci/le_periodic_sync_manager.h" |
| #include "hci/le_scanning_interface.h" |
| #include "hci/vendor_specific_event_manager.h" |
| #include "module.h" |
| #include "os/handler.h" |
| #include "os/log.h" |
| #include "storage/storage_module.h" |
| |
| namespace bluetooth::hci { |
| |
| std::optional<std::vector<uint8_t>> LeScanningReassembler::ProcessAdvertisingReport( |
| uint16_t event_type, |
| uint8_t address_type, |
| Address address, |
| uint8_t advertising_sid, |
| const std::vector<uint8_t>& advertising_data) { |
| bool is_scannable = event_type & (1 << kScannableBit); |
| bool is_scan_response = event_type & (1 << kScanResponseBit); |
| bool is_legacy = event_type & (1 << kLegacyBit); |
| DataStatus data_status = DataStatus((event_type >> kDataStatusBits) & 0x3); |
| |
| if (address_type != (uint8_t)DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED && |
| address == Address::kEmpty) { |
| LOG_WARN("Ignoring non-anonymous advertising report with empty address"); |
| return {}; |
| } |
| |
| AdvertisingKey key(address, DirectAdvertisingAddressType(address_type), advertising_sid); |
| |
| // Ignore scan responses received without a matching advertising event. |
| if (is_scan_response && (ignore_scan_responses_ || !ContainsFragment(key))) { |
| LOG_INFO("Ignoring scan response received without advertising event"); |
| return {}; |
| } |
| |
| // Legacy advertising is always complete, we can drop |
| // the previous data as safety measure if the report is not a scan |
| // response. |
| if (is_legacy && !is_scan_response) { |
| LOG_DEBUG("Dropping repeated legacy advertising data"); |
| RemoveFragment(key); |
| } |
| |
| // Concatenate the data with existing fragments. |
| std::list<AdvertisingFragment>::iterator advertising_fragment = |
| AppendFragment(key, advertising_data); |
| |
| // Trim the advertising data when the complete payload is received. |
| if (data_status != DataStatus::CONTINUING) { |
| advertising_fragment->data = TrimAdvertisingData(advertising_fragment->data); |
| } |
| |
| // TODO(b/272120114) waiting for a scan response here is prone to failure as the |
| // SCAN_REQ PDUs can be rejected by the advertiser according to the |
| // advertising filter parameter. |
| bool expect_scan_response = is_scannable && !is_scan_response && !ignore_scan_responses_; |
| |
| // Check if we should wait for additional fragments: |
| // - For legacy advertising, when a scan response is expected. |
| // - For extended advertising, when the current data is marked |
| // incomplete OR when a scan response is expected. |
| if (data_status == DataStatus::CONTINUING || expect_scan_response) { |
| return {}; |
| } |
| |
| // Otherwise the full advertising report has been reassembled, |
| // removed the cache entry and return the complete advertising data. |
| std::vector<uint8_t> complete_advertising_data = std::move(advertising_fragment->data); |
| cache_.erase(advertising_fragment); |
| return complete_advertising_data; |
| } |
| |
| /// Trim the advertising data by removing empty or overflowing |
| /// GAP Data entries. |
| std::vector<uint8_t> LeScanningReassembler::TrimAdvertisingData( |
| const std::vector<uint8_t>& advertising_data) { |
| // Remove empty and overflowing entries from the advertising data. |
| std::vector<uint8_t> significant_advertising_data; |
| for (size_t offset = 0; offset < advertising_data.size();) { |
| size_t remaining_size = advertising_data.size() - offset; |
| uint8_t entry_size = advertising_data[offset]; |
| |
| if (entry_size != 0 && entry_size < remaining_size) { |
| significant_advertising_data.push_back(entry_size); |
| significant_advertising_data.insert( |
| significant_advertising_data.end(), |
| advertising_data.begin() + offset + 1, |
| advertising_data.begin() + offset + 1 + entry_size); |
| } |
| |
| offset += entry_size + 1; |
| } |
| |
| return significant_advertising_data; |
| } |
| |
| LeScanningReassembler::AdvertisingKey::AdvertisingKey( |
| Address address, DirectAdvertisingAddressType address_type, uint8_t sid) |
| : address(), sid() { |
| // The address type is NO_ADDRESS_PROVIDED for anonymous advertising. |
| if (address_type != DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED) { |
| this->address = AddressWithType(address, AddressType(address_type)); |
| } |
| // 0xff is reserved to indicate that the ADI field was not present |
| // in the ADV_EXT_IND PDU. |
| if (sid != 0xff) { |
| this->sid = sid; |
| } |
| } |
| |
| bool LeScanningReassembler::AdvertisingKey::operator==(const AdvertisingKey& other) { |
| return address == other.address && sid == other.sid; |
| } |
| |
| /// Append to the current advertising data of the selected advertiser. |
| /// If the advertiser is unknown a new entry is added, optionally by |
| /// dropping the oldest advertiser. |
| std::list<LeScanningReassembler::AdvertisingFragment>::iterator |
| LeScanningReassembler::AppendFragment(const AdvertisingKey& key, const std::vector<uint8_t>& data) { |
| auto it = FindFragment(key); |
| if (it != cache_.end()) { |
| it->data.insert(it->data.end(), data.cbegin(), data.cend()); |
| return it; |
| } |
| |
| if (cache_.size() > kMaximumCacheSize) { |
| cache_.pop_back(); |
| } |
| |
| cache_.emplace_front(key, data); |
| return cache_.begin(); |
| } |
| |
| void LeScanningReassembler::RemoveFragment(const AdvertisingKey& key) { |
| auto it = FindFragment(key); |
| if (it != cache_.end()) { |
| cache_.erase(it); |
| } |
| } |
| |
| bool LeScanningReassembler::ContainsFragment(const AdvertisingKey& key) { |
| return FindFragment(key) != cache_.end(); |
| } |
| |
| std::list<LeScanningReassembler::AdvertisingFragment>::iterator LeScanningReassembler::FindFragment( |
| const AdvertisingKey& key) { |
| for (auto it = cache_.begin(); it != cache_.end(); it++) { |
| if (it->key == key) { |
| return it; |
| } |
| } |
| return cache_.end(); |
| } |
| |
| } // namespace bluetooth::hci |