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