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 <base/logging.h>
20 #include <functional>
21 #include <mutex>
22 #include <thread>
23 
24 #include "metric_id_allocator.h"
25 
26 namespace bluetooth {
27 
28 namespace common {
29 
30 const std::string MetricIdAllocator::LOGGING_TAG = "BluetoothMetricIdAllocator";
31 const size_t MetricIdAllocator::kMaxNumUnpairedDevicesInMemory = 200;
32 const size_t MetricIdAllocator::kMaxNumPairedDevicesInMemory = 65000;
33 const int MetricIdAllocator::kMinId = 1;
34 const int MetricIdAllocator::kMaxId = 65534;  // 2^16 - 2
35 
36 // id space should always be larger than kMaxNumPairedDevicesInMemory +
37 // kMaxNumUnpairedDevicesInMemory
38 static_assert((MetricIdAllocator::kMaxNumUnpairedDevicesInMemory +
39                MetricIdAllocator::kMaxNumPairedDevicesInMemory) <
40                   (MetricIdAllocator::kMaxId - MetricIdAllocator::kMinId),
41               "id space should always be larger than "
42               "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");
43 
MetricIdAllocator()44 MetricIdAllocator::MetricIdAllocator()
45     : paired_device_cache_(kMaxNumPairedDevicesInMemory, LOGGING_TAG),
46       temporary_device_cache_(kMaxNumUnpairedDevicesInMemory, LOGGING_TAG) {}
47 
Init(const std::unordered_map<RawAddress,int> & paired_device_map,Callback save_id_callback,Callback forget_device_callback)48 bool MetricIdAllocator::Init(
49     const std::unordered_map<RawAddress, int>& paired_device_map,
50     Callback save_id_callback, Callback forget_device_callback) {
51   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
52   if (initialized_) {
53     return false;
54   }
55 
56   // init paired_devices_map
57   if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
58     LOG(FATAL)
59         << LOGGING_TAG
60         << "Paired device map is bigger than kMaxNumPairedDevicesInMemory";
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) << LOGGING_TAG << "Invalid Bluetooth Metric Id in config";
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 &&
110          temporary_device_cache_.Size() == 0;
111 }
112 
113 // call this function when a new device is scanned
AllocateId(const RawAddress & mac_address)114 int MetricIdAllocator::AllocateId(const RawAddress& mac_address) {
115   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
116   int id = 0;
117   // if already have an id, return it
118   if (paired_device_cache_.Get(mac_address, &id)) {
119     return id;
120   }
121   if (temporary_device_cache_.Get(mac_address, &id)) {
122     return id;
123   }
124 
125   // find next available id
126   while (id_set_.count(next_id_) > 0) {
127     next_id_++;
128     if (next_id_ > kMaxId) {
129       next_id_ = kMinId;
130       LOG(WARNING) << LOGGING_TAG << "Bluetooth metric id overflow.";
131     }
132   }
133   id = next_id_++;
134   id_set_.insert(id);
135   auto evicted = temporary_device_cache_.Put(mac_address, id);
136   if (evicted) {
137     this->id_set_.erase(evicted->second);
138   }
139 
140   if (next_id_ > kMaxId) {
141     next_id_ = kMinId;
142   }
143   return id;
144 }
145 
146 // call this function when a device is paired
SaveDevice(const RawAddress & mac_address)147 bool MetricIdAllocator::SaveDevice(const RawAddress& mac_address) {
148   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
149   int id = 0;
150   if (paired_device_cache_.Get(mac_address, &id)) {
151     return true;
152   }
153   if (!temporary_device_cache_.Get(mac_address, &id)) {
154     LOG(ERROR) << LOGGING_TAG
155                << "Failed to save device because device is not in "
156                << "temporary_device_cache_";
157     return false;
158   }
159   if (!temporary_device_cache_.Remove(mac_address)) {
160     LOG(ERROR) << LOGGING_TAG
161                << "Failed to remove device from temporary_device_cache_";
162     return false;
163   }
164   auto evicted = paired_device_cache_.Put(mac_address, id);
165   if (evicted) {
166     ForgetDevicePostprocess(evicted->first, evicted->second);
167   }
168   if (!save_id_callback_(mac_address, id)) {
169     LOG(ERROR) << LOGGING_TAG
170                << "Callback returned false after saving the device";
171     return false;
172   }
173   return true;
174 }
175 
176 // call this function when a device is forgotten
ForgetDevice(const RawAddress & mac_address)177 void MetricIdAllocator::ForgetDevice(const RawAddress& mac_address) {
178   std::lock_guard<std::mutex> lock(id_allocator_mutex_);
179   int id = 0;
180   if (!paired_device_cache_.Get(mac_address, &id)) {
181     LOG(ERROR) << LOGGING_TAG
182                << "Failed to forget device because device is not in "
183                << "paired_device_cache_";
184     return;
185   }
186   if (!paired_device_cache_.Remove(mac_address)) {
187     LOG(ERROR) << LOGGING_TAG
188                << "Failed to remove device from paired_device_cache_";
189     return;
190   }
191   ForgetDevicePostprocess(mac_address, id);
192 }
193 
IsValidId(const int id)194 bool MetricIdAllocator::IsValidId(const int id) {
195   return id >= kMinId && id <= kMaxId;
196 }
197 
ForgetDevicePostprocess(const RawAddress & mac_address,const int id)198 void MetricIdAllocator::ForgetDevicePostprocess(const RawAddress& mac_address,
199                                                 const int id) {
200   id_set_.erase(id);
201   forget_device_callback_(mac_address, id);
202 }
203 
204 }  // namespace common
205 }  // namespace bluetooth
206