/****************************************************************************** * * Copyright 2020 Google, Inc. * * 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 #include #include #include #include "metric_id_allocator.h" namespace bluetooth { namespace common { const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator"; const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200; const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000; const int MetricIdAllocator::kMinId = 1; const int MetricIdAllocator::kMaxId = 65534; // 2^16 - 2 // id space should always be larger than kMaxNumPairedDevicesInMemory + // kMaxNumUnpairedDevicesInMemory static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory + MetricIdAllocator::kMaxNumPairedDevicesInMemory) < (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId), "id space should always be larger than " "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory"); MetricIdAllocator::MetricIdAllocator() : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG), temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {} bool MetricIdAllocator::Init( const std::unordered_map& paired_device_map, Callback save_id_callback, Callback forget_device_callback) { std::lock_guard lock(id_allocator_mutex_); if (initialized_) { return false; } // init paired_devices_map if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) { LOG(FATAL) << LOGGING_TAG << "Paired device map is bigger than kMaxNumPairedDevicesInMemory"; // fail loudly to let caller know return false; } next_id_ = kMinId; for (const auto& p : paired_device_map) { if (p.second < kMinId || p.second > kMaxId) { LOG(FATAL) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config"; } auto evicted = paired_device_cache_.Put(p.first, p.second); if (evicted) { ForgetDevicePostprocess(evicted->first, evicted->second); } id_set_.insert(p.second); next_id_ = std::max(next_id_, p.second + 1); } if (next_id_ > kMaxId) { next_id_ = kMinId; } // init callbacks save_id_callback_ = save_id_callback; forget_device_callback_ = forget_device_callback; return initialized_ = true; } MetricIdAllocator::~MetricIdAllocator() { Close(); } bool MetricIdAllocator::Close() { std::lock_guard lock(id_allocator_mutex_); if (!initialized_) { return false; } paired_device_cache_.Clear(); temporary_device_cache_.Clear(); id_set_.clear(); initialized_ = false; return true; } MetricIdAllocator& MetricIdAllocator::GetInstance() { static MetricIdAllocator metric_id_allocator; return metric_id_allocator; } bool MetricIdAllocator::IsEmpty() const { std::lock_guard lock(id_allocator_mutex_); return paired_device_cache_.Size() == 0 && temporary_device_cache_.Size() == 0; } // call this function when a new device is scanned int MetricIdAllocator::AllocateId(const RawAddress& mac_address) { std::lock_guard lock(id_allocator_mutex_); int id = 0; // if already have an id, return it if (paired_device_cache_.Get(mac_address, &id)) { return id; } if (temporary_device_cache_.Get(mac_address, &id)) { return id; } // find next available id while (id_set_.count(next_id_) > 0) { next_id_++; if (next_id_ > kMaxId) { next_id_ = kMinId; LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow."; } } id = next_id_++; id_set_.insert(id); auto evicted = temporary_device_cache_.Put(mac_address, id); if (evicted) { this->id_set_.erase(evicted->second); } if (next_id_ > kMaxId) { next_id_ = kMinId; } return id; } // call this function when a device is paired bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) { std::lock_guard lock(id_allocator_mutex_); int id = 0; if (paired_device_cache_.Get(mac_address, &id)) { return true; } if (!temporary_device_cache_.Get(mac_address, &id)) { LOG(ERROR) << LOGGING_TAG << "Failed to save device because device is not in " << "temporary_device_cache_"; return false; } if (!temporary_device_cache_.Remove(mac_address)) { LOG(ERROR) << LOGGING_TAG << "Failed to remove device from temporary_device_cache_"; return false; } auto evicted = paired_device_cache_.Put(mac_address, id); if (evicted) { ForgetDevicePostprocess(evicted->first, evicted->second); } if (!save_id_callback_(mac_address, id)) { LOG(ERROR) << LOGGING_TAG << "Callback returned false after saving the device"; return false; } return true; } // call this function when a device is forgotten void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) { std::lock_guard lock(id_allocator_mutex_); int id = 0; if (!paired_device_cache_.Get(mac_address, &id)) { LOG(ERROR) << LOGGING_TAG << "Failed to forget device because device is not in " << "paired_device_cache_"; return; } if (!paired_device_cache_.Remove(mac_address)) { LOG(ERROR) << LOGGING_TAG << "Failed to remove device from paired_device_cache_"; return; } ForgetDevicePostprocess(mac_address, id); } bool MetricIdAllocator::IsValidId(const int id) { return id >= kMinId && id <= kMaxId; } void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address, const int id) { id_set_.erase(id); forget_device_callback_(mac_address, id); } } // namespace common } // namespace bluetooth