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