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 <base/logging.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 << LOGGING_TAG
63 << "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
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) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
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(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
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) << LOGGING_TAG
158 << "Failed to save device because device is not in "
159 << "temporary_device_cache_";
160 return false;
161 }
162 if (!temporary_device_cache_.Remove(mac_address)) {
163 LOG(ERROR) << LOGGING_TAG
164 << "Failed to remove device from temporary_device_cache_";
165 return false;
166 }
167 auto evicted = paired_device_cache_.Put(mac_address, id);
168 if (evicted) {
169 ForgetDevicePostprocess(evicted->first, evicted->second);
170 }
171 if (!save_id_callback_(mac_address, id)) {
172 LOG(ERROR) << LOGGING_TAG
173 << "Callback returned false after saving the device";
174 return false;
175 }
176 return true;
177 }
178
179 // call this function when a device is forgotten
ForgetDevice(const RawAddress & mac_address)180 void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
181 std::lock_guard<std::mutex> lock(id_allocator_mutex_);
182 int id = 0;
183 if (!paired_device_cache_.Get(mac_address, &id)) {
184 LOG(ERROR) << LOGGING_TAG
185 << "Failed to forget device because device is not in "
186 << "paired_device_cache_";
187 return;
188 }
189 if (!paired_device_cache_.Remove(mac_address)) {
190 LOG(ERROR) << LOGGING_TAG
191 << "Failed to remove device from paired_device_cache_";
192 return;
193 }
194 ForgetDevicePostprocess(mac_address, id);
195 }
196
IsValidId(const int id)197 bool MetricIdAllocator::IsValidId(const int id) {
198 return id >= kMinId && id <= kMaxId;
199 }
200
ForgetDevicePostprocess(const RawAddress & mac_address,const int id)201 void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& 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