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