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