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 <base/logging.h>
20 #include <functional>
21 #include <mutex>
22 #include <thread>
23
24 #include "metric_id_allocator.h"
25
26 namespace bluetooth {
27
28 namespace common {
29
30 const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
31 const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
32 const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
33 const int MetricIdAllocator::kMinId = 1;
34 const int MetricIdAllocator::kMaxId = 65534; // 2^16 - 2
35
36 // id space should always be larger than kMaxNumPairedDevicesInMemory +
37 // kMaxNumUnpairedDevicesInMemory
38 static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
39 MetricIdAllocator::kMaxNumPairedDevicesInMemory) <
40 (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId),
41 "id space should always be larger than "
42 "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
43
MetricIdAllocator()44 MetricIdAllocator::MetricIdAllocator()
45 : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG),
46 temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {}
47
Init(const std::unordered_map<RawAddress,int> & paired_device_map,Callback save_id_callback,Callback forget_device_callback)48 bool MetricIdAllocator::Init(
49 const std::unordered_map<RawAddress, int>& paired_device_map,
50 Callback save_id_callback, Callback forget_device_callback) {
51 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
52 if (initialized_) {
53 return false;
54 }
55
56 // init paired_devices_map
57 if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
58 LOG(FATAL)
59 << LOGGING_TAG
60 << "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
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) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
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 &&
110 temporary_device_cache_.Size() == 0;
111 }
112
113 // call this function when a new device is scanned
AllocateId(const RawAddress & mac_address)114 int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
115 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
116 int id = 0;
117 // if already have an id, return it
118 if (paired_device_cache_.Get(mac_address, &id)) {
119 return id;
120 }
121 if (temporary_device_cache_.Get(mac_address, &id)) {
122 return id;
123 }
124
125 // find next available id
126 while (id_set_.count(next_id_) > 0) {
127 next_id_++;
128 if (next_id_ > kMaxId) {
129 next_id_ = kMinId;
130 LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
131 }
132 }
133 id = next_id_++;
134 id_set_.insert(id);
135 auto evicted = temporary_device_cache_.Put(mac_address, id);
136 if (evicted) {
137 this->id_set_.erase(evicted->second);
138 }
139
140 if (next_id_ > kMaxId) {
141 next_id_ = kMinId;
142 }
143 return id;
144 }
145
146 // call this function when a device is paired
SaveDevice(const RawAddress & mac_address)147 bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
148 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
149 int id = 0;
150 if (paired_device_cache_.Get(mac_address, &id)) {
151 return true;
152 }
153 if (!temporary_device_cache_.Get(mac_address, &id)) {
154 LOG(ERROR) << LOGGING_TAG
155 << "Failed to save device because device is not in "
156 << "temporary_device_cache_";
157 return false;
158 }
159 if (!temporary_device_cache_.Remove(mac_address)) {
160 LOG(ERROR) << LOGGING_TAG
161 << "Failed to remove device from temporary_device_cache_";
162 return false;
163 }
164 auto evicted = paired_device_cache_.Put(mac_address, id);
165 if (evicted) {
166 ForgetDevicePostprocess(evicted->first, evicted->second);
167 }
168 if (!save_id_callback_(mac_address, id)) {
169 LOG(ERROR) << LOGGING_TAG
170 << "Callback returned false after saving the device";
171 return false;
172 }
173 return true;
174 }
175
176 // call this function when a device is forgotten
ForgetDevice(const RawAddress & mac_address)177 void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
178 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
179 int id = 0;
180 if (!paired_device_cache_.Get(mac_address, &id)) {
181 LOG(ERROR) << LOGGING_TAG
182 << "Failed to forget device because device is not in "
183 << "paired_device_cache_";
184 return;
185 }
186 if (!paired_device_cache_.Remove(mac_address)) {
187 LOG(ERROR) << LOGGING_TAG
188 << "Failed to remove device from paired_device_cache_";
189 return;
190 }
191 ForgetDevicePostprocess(mac_address, id);
192 }
193
IsValidId(const int id)194 bool MetricIdAllocator::IsValidId(const int id) {
195 return id >= kMinId && id <= kMaxId;
196 }
197
ForgetDevicePostprocess(const RawAddress & mac_address,const int id)198 void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
199 const int id) {
200 id_set_.erase(id);
201 forget_device_callback_(mac_address, id);
202 }
203
204 } // namespace common
205 } // namespace bluetooth
206