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