1 /******************************************************************************
2 *
3 * Copyright 2020 Google, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18 #define LOG_TAG "BluetoothMetricIdManager"
19
20 #include "common/metric_id_manager.h"
21
22 #include <bluetooth/log.h>
23
24 #include <functional>
25 #include <iterator>
26 #include <mutex>
27 #include <optional>
28 #include <thread>
29
30 #include "os/log.h"
31
32 namespace bluetooth {
33 namespace common {
34
35 using hci::Address;
36
37 const size_t MetricIdManager::kMaxNumUnpairedDevicesInMemory = 200;
38 const size_t MetricIdManager::kMaxNumPairedDevicesInMemory = 65000;
39 const int MetricIdManager::kMinId = 1;
40 const int MetricIdManager::kMaxId = 65534; // 2^16 - 2
41
42 // id space should always be larger than kMaxNumPairedDevicesInMemory +
43 // kMaxNumUnpairedDevicesInMemory
44 static_assert((MetricIdManager::kMaxNumUnpairedDevicesInMemory +
45 MetricIdManager::kMaxNumPairedDevicesInMemory) <
46 (MetricIdManager::kMaxId - MetricIdManager::kMinId),
47 "id space should always be larger than "
48 "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
49
MetricIdManager()50 MetricIdManager::MetricIdManager()
51 : paired_device_cache_(kMaxNumPairedDevicesInMemory),
52 temporary_device_cache_(kMaxNumUnpairedDevicesInMemory) {}
53
Init(const std::unordered_map<Address,int> & paired_device_map,Callback save_id_callback,Callback forget_device_callback)54 bool MetricIdManager::Init(
55 const std::unordered_map<Address, int>& paired_device_map,
56 Callback save_id_callback, Callback forget_device_callback) {
57 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
58 if (initialized_) {
59 return false;
60 }
61
62 // init paired_devices_map
63 if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
64 log::fatal(
65 "Paired device map has size {}, which is bigger than kMaxNumPairedDevicesInMemory {}",
66 paired_device_map.size(),
67 kMaxNumPairedDevicesInMemory);
68 // fail loudly to let caller know
69 return false;
70 }
71
72 next_id_ = kMinId;
73 for (const auto& p : paired_device_map) {
74 if (p.second < kMinId || p.second > kMaxId) {
75 log::fatal(
76 "Invalid Bluetooth Metric Id in config. Id {} of {} is out of range [{}, {}]",
77 p.second,
78 p.first,
79 kMinId,
80 kMaxId);
81 }
82 auto evicted = paired_device_cache_.insert_or_assign(p.first, p.second);
83 if (evicted) {
84 ForgetDevicePostprocess(evicted->first, evicted->second);
85 }
86 id_set_.insert(p.second);
87 next_id_ = std::max(next_id_, p.second + 1);
88 }
89 if (next_id_ > kMaxId) {
90 next_id_ = kMinId;
91 }
92
93 // init callbacks
94 save_id_callback_ = save_id_callback;
95 forget_device_callback_ = forget_device_callback;
96
97 return initialized_ = true;
98 }
99
~MetricIdManager()100 MetricIdManager::~MetricIdManager() { Close(); }
101
Close()102 bool MetricIdManager::Close() {
103 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
104 if (!initialized_) {
105 return false;
106 }
107 paired_device_cache_.clear();
108 temporary_device_cache_.clear();
109 id_set_.clear();
110 initialized_ = false;
111 return true;
112 }
113
GetInstance()114 MetricIdManager& MetricIdManager::GetInstance() {
115 static MetricIdManager metric_id_allocator;
116 return metric_id_allocator;
117 }
118
IsEmpty() const119 bool MetricIdManager::IsEmpty() const {
120 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
121 return paired_device_cache_.size() == 0 &&
122 temporary_device_cache_.size() == 0;
123 }
124
125 // call this function when a new device is scanned
AllocateId(const Address & mac_address)126 int MetricIdManager::AllocateId(const Address& mac_address) {
127 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
128 auto it = paired_device_cache_.find(mac_address);
129 // if already have an id, return it
130 if (it != paired_device_cache_.end()) {
131 return it->second;
132 }
133 it = temporary_device_cache_.find(mac_address);
134 if (it != temporary_device_cache_.end()) {
135 return it->second;
136 }
137
138 // find next available id
139 while (id_set_.count(next_id_) > 0) {
140 next_id_++;
141 if (next_id_ > kMaxId) {
142 next_id_ = kMinId;
143 log::warn("Bluetooth metric id overflow.");
144 }
145 }
146 int id = next_id_++;
147 id_set_.insert(id);
148 auto evicted = temporary_device_cache_.insert_or_assign(mac_address, id);
149 if (evicted) {
150 this->id_set_.extract(evicted->second);
151 }
152
153 if (next_id_ > kMaxId) {
154 next_id_ = kMinId;
155 }
156 return id;
157 }
158
159 // call this function when a device is paired
SaveDevice(const Address & mac_address)160 bool MetricIdManager::SaveDevice(const Address& mac_address) {
161 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
162 if (paired_device_cache_.contains(mac_address)) {
163 return true;
164 }
165 if (!temporary_device_cache_.contains(mac_address)) {
166 log::error("Failed to save device because device is not in temporary_device_cache_");
167 return false;
168 }
169 auto opt = temporary_device_cache_.extract(mac_address);
170 if (!opt) {
171 log::error("Failed to remove device from temporary_device_cache_");
172 return false;
173 }
174 int id = opt->second;
175 auto evicted = paired_device_cache_.insert_or_assign(mac_address, id);
176 if (evicted) {
177 ForgetDevicePostprocess(evicted->first, evicted->second);
178 }
179 if (!save_id_callback_(mac_address, id)) {
180 log::error("Callback returned false after saving the device");
181 return false;
182 }
183 return true;
184 }
185
186 // call this function when a device is forgotten
ForgetDevice(const Address & mac_address)187 void MetricIdManager::ForgetDevice(const Address& mac_address) {
188 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
189 auto opt = paired_device_cache_.extract(mac_address);
190 if (!opt) {
191 log::error("Failed to remove device from paired_device_cache_");
192 return;
193 }
194 ForgetDevicePostprocess(mac_address, opt->second);
195 }
196
IsValidId(const int id)197 bool MetricIdManager::IsValidId(const int id) {
198 return id >= kMinId && id <= kMaxId;
199 }
200
ForgetDevicePostprocess(const Address & mac_address,const int id)201 void MetricIdManager::ForgetDevicePostprocess(const Address& mac_address,
202 const int id) {
203 id_set_.erase(id);
204 forget_device_callback_(mac_address, id);
205 }
206
207 } // namespace common
208 } // namespace bluetooth
209