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
19 #include "metric_id_allocator.h"
20
21 #include <bluetooth/log.h>
22
23 #include <functional>
24 #include <mutex>
25 #include <thread>
26
27 #include "types/raw_address.h"
28
29 namespace bluetooth {
30
31 namespace common {
32
33 const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
34 const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
35 const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
36 const int MetricIdAllocator::kMinId = 1;
37 const int MetricIdAllocator::kMaxId = 65534; // 2^16 - 2
38
39 // id space should always be larger than kMaxNumPairedDevicesInMemory +
40 // kMaxNumUnpairedDevicesInMemory
41 static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
42 MetricIdAllocator::kMaxNumPairedDevicesInMemory) <
43 (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId),
44 "id space should always be larger than "
45 "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
46
MetricIdAllocator()47 MetricIdAllocator::MetricIdAllocator()
48 : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG),
49 temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {}
50
Init(const std::unordered_map<RawAddress,int> & paired_device_map,Callback save_id_callback,Callback forget_device_callback)51 bool MetricIdAllocator::Init(
52 const std::unordered_map<RawAddress, int>& paired_device_map,
53 Callback save_id_callback, Callback forget_device_callback) {
54 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
55 if (initialized_) {
56 return false;
57 }
58
59 // init paired_devices_map
60 if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
61 log::fatal(
62 "{}Paired device map is bigger than kMaxNumPairedDevicesInMemory",
63 LOGGING_TAG);
64 // fail loudly to let caller know
65 return false;
66 }
67
68 next_id_ = kMinId;
69 for (const auto& p : paired_device_map) {
70 if (p.second < kMinId || p.second > kMaxId) {
71 log::fatal("{}Invalid Bluetooth Metric Id in config", LOGGING_TAG);
72 }
73 auto evicted = paired_device_cache_.Put(p.first, p.second);
74 if (evicted) {
75 ForgetDevicePostprocess(evicted->first, evicted->second);
76 }
77 id_set_.insert(p.second);
78 next_id_ = std::max(next_id_, p.second + 1);
79 }
80 if (next_id_ > kMaxId) {
81 next_id_ = kMinId;
82 }
83
84 // init callbacks
85 save_id_callback_ = save_id_callback;
86 forget_device_callback_ = forget_device_callback;
87
88 return initialized_ = true;
89 }
90
~MetricIdAllocator()91 MetricIdAllocator::~MetricIdAllocator() { Close(); }
92
Close()93 bool MetricIdAllocator::Close() {
94 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
95 if (!initialized_) {
96 return false;
97 }
98 paired_device_cache_.Clear();
99 temporary_device_cache_.Clear();
100 id_set_.clear();
101 initialized_ = false;
102 return true;
103 }
104
GetInstance()105 MetricIdAllocator& MetricIdAllocator::GetInstance() {
106 static MetricIdAllocator metric_id_allocator;
107 return metric_id_allocator;
108 }
109
IsEmpty() const110 bool MetricIdAllocator::IsEmpty() const {
111 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
112 return paired_device_cache_.Size() == 0 &&
113 temporary_device_cache_.Size() == 0;
114 }
115
116 // call this function when a new device is scanned
AllocateId(const RawAddress & mac_address)117 int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
118 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
119 int id = 0;
120 // if already have an id, return it
121 if (paired_device_cache_.Get(mac_address, &id)) {
122 return id;
123 }
124 if (temporary_device_cache_.Get(mac_address, &id)) {
125 return id;
126 }
127
128 // find next available id
129 while (id_set_.count(next_id_) > 0) {
130 next_id_++;
131 if (next_id_ > kMaxId) {
132 next_id_ = kMinId;
133 log::warn("{}Bluetooth metric id overflow.", LOGGING_TAG);
134 }
135 }
136 id = next_id_++;
137 id_set_.insert(id);
138 auto evicted = temporary_device_cache_.Put(mac_address, id);
139 if (evicted) {
140 this->id_set_.erase(evicted->second);
141 }
142
143 if (next_id_ > kMaxId) {
144 next_id_ = kMinId;
145 }
146 return id;
147 }
148
149 // call this function when a device is paired
SaveDevice(const RawAddress & mac_address)150 bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
151 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
152 int id = 0;
153 if (paired_device_cache_.Get(mac_address, &id)) {
154 return true;
155 }
156 if (!temporary_device_cache_.Get(mac_address, &id)) {
157 log::error(
158 "{}Failed to save device because device is not in "
159 "temporary_device_cache_",
160 LOGGING_TAG);
161 return false;
162 }
163 if (!temporary_device_cache_.Remove(mac_address)) {
164 log::error("{}Failed to remove device from temporary_device_cache_",
165 LOGGING_TAG);
166 return false;
167 }
168 auto evicted = paired_device_cache_.Put(mac_address, id);
169 if (evicted) {
170 ForgetDevicePostprocess(evicted->first, evicted->second);
171 }
172 if (!save_id_callback_(mac_address, id)) {
173 log::error("{}Callback returned false after saving the device",
174 LOGGING_TAG);
175 return false;
176 }
177 return true;
178 }
179
180 // call this function when a device is forgotten
ForgetDevice(const RawAddress & mac_address)181 void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
182 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
183 int id = 0;
184 if (!paired_device_cache_.Get(mac_address, &id)) {
185 log::error(
186 "{}Failed to forget device because device is not in "
187 "paired_device_cache_",
188 LOGGING_TAG);
189 return;
190 }
191 if (!paired_device_cache_.Remove(mac_address)) {
192 log::error("{}Failed to remove device from paired_device_cache_",
193 LOGGING_TAG);
194 return;
195 }
196 ForgetDevicePostprocess(mac_address, id);
197 }
198
IsValidId(const int id)199 bool MetricIdAllocator::IsValidId(const int id) {
200 return id >= kMinId && id <= kMaxId;
201 }
202
ForgetDevicePostprocess(const RawAddress & mac_address,const int id)203 void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
204 const int id) {
205 id_set_.erase(id);
206 forget_device_callback_(mac_address, id);
207 }
208
209 } // namespace common
210 } // namespace bluetooth
211