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(const std::unordered_map<RawAddress, int>& paired_device_map,
52                              Callback save_id_callback, Callback forget_device_callback) {
53   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
54   if (initialized_) {
55     return false;
56   }
57 
58   // init paired_devices_map
59   if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
60     log::fatal("{}Paired device map is bigger than kMaxNumPairedDevicesInMemory", LOGGING_TAG);
61     // fail loudly to let caller know
62     return false;
63   }
64 
65   next_id_ = kMinId;
66   for (const auto& p : paired_device_map) {
67     if (p.second < kMinId || p.second > kMaxId) {
68       log::fatal("{}Invalid Bluetooth Metric Id in config", LOGGING_TAG);
69     }
70     auto evicted = paired_device_cache_.Put(p.first, p.second);
71     if (evicted) {
72       ForgetDevicePostprocess(evicted->first, evicted->second);
73     }
74     id_set_.insert(p.second);
75     next_id_ = std::max(next_id_, p.second + 1);
76   }
77   if (next_id_ > kMaxId) {
78     next_id_ = kMinId;
79   }
80 
81   // init callbacks
82   save_id_callback_ = save_id_callback;
83   forget_device_callback_ = forget_device_callback;
84 
85   return initialized_ = true;
86 }
87 
~MetricIdAllocator()88 MetricIdAllocator::~MetricIdAllocator() { Close(); }
89 
Close()90 bool MetricIdAllocator::Close() {
91   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
92   if (!initialized_) {
93     return false;
94   }
95   paired_device_cache_.Clear();
96   temporary_device_cache_.Clear();
97   id_set_.clear();
98   initialized_ = false;
99   return true;
100 }
101 
GetInstance()102 MetricIdAllocator& MetricIdAllocator::GetInstance() {
103   static MetricIdAllocator metric_id_allocator;
104   return metric_id_allocator;
105 }
106 
IsEmpty() const107 bool MetricIdAllocator::IsEmpty() const {
108   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
109   return paired_device_cache_.Size() == 0 && temporary_device_cache_.Size() == 0;
110 }
111 
112 // call this function when a new device is scanned
AllocateId(const RawAddress & mac_address)113 int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
114   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
115   int id = 0;
116   // if already have an id, return it
117   if (paired_device_cache_.Get(mac_address, &id)) {
118     return id;
119   }
120   if (temporary_device_cache_.Get(mac_address, &id)) {
121     return id;
122   }
123 
124   // find next available id
125   while (id_set_.count(next_id_) > 0) {
126     next_id_++;
127     if (next_id_ > kMaxId) {
128       next_id_ = kMinId;
129       log::warn("{}Bluetooth metric id overflow.", LOGGING_TAG);
130     }
131   }
132   id = next_id_++;
133   id_set_.insert(id);
134   auto evicted = temporary_device_cache_.Put(mac_address, id);
135   if (evicted) {
136     this->id_set_.erase(evicted->second);
137   }
138 
139   if (next_id_ > kMaxId) {
140     next_id_ = kMinId;
141   }
142   return id;
143 }
144 
145 // call this function when a device is paired
SaveDevice(const RawAddress & mac_address)146 bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
147   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
148   int id = 0;
149   if (paired_device_cache_.Get(mac_address, &id)) {
150     return true;
151   }
152   if (!temporary_device_cache_.Get(mac_address, &id)) {
153     log::error(
154             "{}Failed to save device because device is not in "
155             "temporary_device_cache_",
156             LOGGING_TAG);
157     return false;
158   }
159   if (!temporary_device_cache_.Remove(mac_address)) {
160     log::error("{}Failed to remove device from temporary_device_cache_", LOGGING_TAG);
161     return false;
162   }
163   auto evicted = paired_device_cache_.Put(mac_address, id);
164   if (evicted) {
165     ForgetDevicePostprocess(evicted->first, evicted->second);
166   }
167   if (!save_id_callback_(mac_address, id)) {
168     log::error("{}Callback returned false after saving the device", LOGGING_TAG);
169     return false;
170   }
171   return true;
172 }
173 
174 // call this function when a device is forgotten
ForgetDevice(const RawAddress & mac_address)175 void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
176   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
177   int id = 0;
178   if (!paired_device_cache_.Get(mac_address, &id)) {
179     log::error(
180             "{}Failed to forget device because device is not in "
181             "paired_device_cache_",
182             LOGGING_TAG);
183     return;
184   }
185   if (!paired_device_cache_.Remove(mac_address)) {
186     log::error("{}Failed to remove device from paired_device_cache_", LOGGING_TAG);
187     return;
188   }
189   ForgetDevicePostprocess(mac_address, id);
190 }
191 
IsValidId(const int id)192 bool MetricIdAllocator::IsValidId(const int id) { return id >= kMinId && id <= kMaxId; }
193 
ForgetDevicePostprocess(const RawAddress & mac_address,const int id)194 void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address, const int id) {
195   id_set_.erase(id);
196   forget_device_callback_(mac_address, id);
197 }
198 
199 }  // namespace common
200 }  // namespace bluetooth
201