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