• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
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 #include <bluetooth/log.h>
19 #include <stdio.h>
20 
21 #include <algorithm>
22 #include <cstddef>
23 #include <cstdint>
24 #include <cstring>
25 #include <functional>
26 #include <limits>
27 #include <list>
28 #include <map>
29 #include <mutex>
30 #include <ostream>
31 #include <sstream>
32 #include <unordered_set>
33 #include <utility>
34 #include <vector>
35 
36 #include "bta_groups.h"
37 #include "btif/include/btif_profile_storage.h"
38 #include "stack/include/bt_types.h"
39 #include "types/bluetooth/uuid.h"
40 #include "types/raw_address.h"
41 
42 using bluetooth::Uuid;
43 
44 namespace bluetooth {
45 namespace groups {
46 
47 class DeviceGroupsImpl;
48 DeviceGroupsImpl* instance;
49 std::mutex instance_mutex;
50 static constexpr int kMaxGroupId = 0xEF;
51 
52 class DeviceGroup {
53 public:
DeviceGroup(int group_id,Uuid uuid)54   DeviceGroup(int group_id, Uuid uuid) : group_id_(group_id), group_uuid_(uuid) {}
Add(const RawAddress & addr)55   void Add(const RawAddress& addr) { devices_.insert(addr); }
Remove(const RawAddress & addr)56   void Remove(const RawAddress& addr) { devices_.erase(addr); }
Contains(const RawAddress & addr) const57   bool Contains(const RawAddress& addr) const { return devices_.count(addr) != 0; }
58 
ForEachDevice(std::function<void (const RawAddress &)> cb) const59   void ForEachDevice(std::function<void(const RawAddress&)> cb) const {
60     for (auto const& addr : devices_) {
61       cb(addr);
62     }
63   }
64 
Size(void) const65   int Size(void) const { return devices_.size(); }
GetGroupId(void) const66   int GetGroupId(void) const { return group_id_; }
GetUuid(void) const67   const Uuid& GetUuid(void) const { return group_uuid_; }
68 
69 private:
70   friend std::ostream& operator<<(std::ostream& out, const bluetooth::groups::DeviceGroup& value);
71   int group_id_;
72   Uuid group_uuid_;
73   std::unordered_set<RawAddress> devices_;
74 };
75 
76 class DeviceGroupsImpl : public DeviceGroups {
77   static constexpr uint8_t GROUP_STORAGE_CURRENT_LAYOUT_MAGIC = 0x10;
78   static constexpr size_t GROUP_STORAGE_HEADER_SZ =
79           sizeof(GROUP_STORAGE_CURRENT_LAYOUT_MAGIC) + sizeof(uint8_t); /* num_of_groups */
80   static constexpr size_t GROUP_STORAGE_ENTRY_SZ =
81           sizeof(uint8_t) /* group_id */ + Uuid::kNumBytes128;
82 
83 public:
DeviceGroupsImpl(DeviceGroupsCallbacks * callbacks)84   DeviceGroupsImpl(DeviceGroupsCallbacks* callbacks) {
85     AddCallbacks(callbacks);
86     btif_storage_load_bonded_groups();
87   }
88 
GetGroupId(const RawAddress & addr,Uuid uuid) const89   int GetGroupId(const RawAddress& addr, Uuid uuid) const override {
90     for (const auto& [id, g] : groups_) {
91       if ((g.Contains(addr)) && (uuid == g.GetUuid())) {
92         return id;
93       }
94     }
95     return kGroupUnknown;
96   }
97 
add_to_group(const RawAddress & addr,DeviceGroup * group)98   void add_to_group(const RawAddress& addr, DeviceGroup* group) {
99     group->Add(addr);
100 
101     bool first_device_in_group = (group->Size() == 1);
102 
103     for (auto c : callbacks_) {
104       if (first_device_in_group) {
105         c->OnGroupAdded(addr, group->GetUuid(), group->GetGroupId());
106       } else {
107         c->OnGroupMemberAdded(addr, group->GetGroupId());
108       }
109     }
110   }
111 
AddDevice(const RawAddress & addr,Uuid uuid,int group_id)112   int AddDevice(const RawAddress& addr, Uuid uuid, int group_id) override {
113     DeviceGroup* group = nullptr;
114 
115     if (group_id == kGroupUnknown) {
116       auto gid = GetGroupId(addr, uuid);
117       if (gid != kGroupUnknown) {
118         return gid;
119       }
120       group = create_group(uuid);
121     } else {
122       group = get_or_create_group_with_id(group_id, uuid);
123       if (!group) {
124         return kGroupUnknown;
125       }
126     }
127 
128     log::assert_that(group, "assert failed: group");
129 
130     if (group->Contains(addr)) {
131       log::error("device {} already in the group: {}", addr, group_id);
132       return group->GetGroupId();
133     }
134 
135     add_to_group(addr, group);
136 
137     btif_storage_add_groups(addr);
138     return group->GetGroupId();
139   }
140 
RemoveDevice(const RawAddress & addr,int group_id)141   void RemoveDevice(const RawAddress& addr, int group_id) override {
142     int num_of_groups_dev_belongs = 0;
143 
144     /* Remove from all the groups. Usually happens on unbond */
145     for (auto it = groups_.begin(); it != groups_.end();) {
146       auto& [id, g] = *it;
147       if (!g.Contains(addr)) {
148         ++it;
149         continue;
150       }
151 
152       num_of_groups_dev_belongs++;
153 
154       if ((group_id != bluetooth::groups::kGroupUnknown) && (group_id != id)) {
155         ++it;
156         continue;
157       }
158 
159       num_of_groups_dev_belongs--;
160 
161       g.Remove(addr);
162       for (auto c : callbacks_) {
163         c->OnGroupMemberRemoved(addr, id);
164       }
165 
166       if (g.Size() == 0) {
167         for (auto c : callbacks_) {
168           c->OnGroupRemoved(g.GetUuid(), g.GetGroupId());
169         }
170         it = groups_.erase(it);
171       } else {
172         ++it;
173       }
174     }
175 
176     btif_storage_remove_groups(addr);
177     if (num_of_groups_dev_belongs > 0) {
178       btif_storage_add_groups(addr);
179     }
180   }
181 
SerializeGroups(const RawAddress & addr,std::vector<uint8_t> & out) const182   bool SerializeGroups(const RawAddress& addr, std::vector<uint8_t>& out) const {
183     auto num_groups = std::count_if(groups_.begin(), groups_.end(), [&addr](auto& id_group_pair) {
184       return id_group_pair.second.Contains(addr);
185     });
186     if ((num_groups == 0) || (num_groups > std::numeric_limits<uint8_t>::max())) {
187       return false;
188     }
189 
190     out.resize(GROUP_STORAGE_HEADER_SZ + (num_groups * GROUP_STORAGE_ENTRY_SZ));
191     auto* ptr = out.data();
192 
193     /* header */
194     UINT8_TO_STREAM(ptr, GROUP_STORAGE_CURRENT_LAYOUT_MAGIC);
195     UINT8_TO_STREAM(ptr, num_groups);
196 
197     /* group entries */
198     for (const auto& [id, g] : groups_) {
199       if (g.Contains(addr)) {
200         UINT8_TO_STREAM(ptr, id);
201 
202         Uuid::UUID128Bit uuid128 = g.GetUuid().To128BitLE();
203         memcpy(ptr, uuid128.data(), Uuid::kNumBytes128);
204         ptr += Uuid::kNumBytes128;
205       }
206     }
207 
208     return true;
209   }
210 
DeserializeGroups(const RawAddress & addr,const std::vector<uint8_t> & in)211   void DeserializeGroups(const RawAddress& addr, const std::vector<uint8_t>& in) {
212     if (in.size() < GROUP_STORAGE_HEADER_SZ + GROUP_STORAGE_ENTRY_SZ) {
213       return;
214     }
215 
216     auto* ptr = in.data();
217 
218     uint8_t magic;
219     STREAM_TO_UINT8(magic, ptr);
220 
221     if (magic == GROUP_STORAGE_CURRENT_LAYOUT_MAGIC) {
222       uint8_t num_groups;
223       STREAM_TO_UINT8(num_groups, ptr);
224 
225       if (in.size() < GROUP_STORAGE_HEADER_SZ + (num_groups * GROUP_STORAGE_ENTRY_SZ)) {
226         log::error("Invalid persistent storage data");
227         return;
228       }
229 
230       /* group entries */
231       while (num_groups--) {
232         uint8_t id;
233         STREAM_TO_UINT8(id, ptr);
234 
235         Uuid::UUID128Bit uuid128;
236         STREAM_TO_ARRAY(uuid128.data(), ptr, (int)Uuid::kNumBytes128);
237 
238         auto* group = get_or_create_group_with_id(id, Uuid::From128BitLE(uuid128));
239         if (group) {
240           add_to_group(addr, group);
241         }
242 
243         for (auto c : callbacks_) {
244           c->OnGroupAddFromStorage(addr, Uuid::From128BitLE(uuid128), id);
245         }
246       }
247     }
248   }
249 
AddCallbacks(DeviceGroupsCallbacks * callbacks)250   void AddCallbacks(DeviceGroupsCallbacks* callbacks) {
251     callbacks_.push_back(std::move(callbacks));
252 
253     /* Notify new user about known groups */
254     for (const auto& [id, g] : groups_) {
255       auto group_uuid = g.GetUuid();
256       auto group_id = g.GetGroupId();
257       g.ForEachDevice([&](auto& dev) { callbacks->OnGroupAdded(dev, group_uuid, group_id); });
258     }
259   }
260 
Clear(DeviceGroupsCallbacks * callbacks)261   bool Clear(DeviceGroupsCallbacks* callbacks) {
262     auto it = find_if(callbacks_.begin(), callbacks_.end(),
263                       [callbacks](auto c) { return c == callbacks; });
264 
265     if (it != callbacks_.end()) {
266       callbacks_.erase(it);
267     }
268 
269     if (callbacks_.size() != 0) {
270       return false;
271     }
272     /* When all clients were unregistered */
273     groups_.clear();
274     return true;
275   }
276 
Dump(int fd)277   void Dump(int fd) {
278     std::stringstream stream;
279 
280     stream << "  Num. registered clients: " << callbacks_.size() << std::endl;
281     stream << "  Groups:\n";
282     for (const auto& kv_pair : groups_) {
283       stream << kv_pair.second << std::endl;
284     }
285 
286     dprintf(fd, "%s", stream.str().c_str());
287   }
288 
289 private:
find_device_group(int group_id)290   DeviceGroup* find_device_group(int group_id) {
291     return groups_.count(group_id) ? &groups_.at(group_id) : nullptr;
292   }
293 
get_or_create_group_with_id(int group_id,Uuid uuid)294   DeviceGroup* get_or_create_group_with_id(int group_id, Uuid uuid) {
295     auto group = find_device_group(group_id);
296     if (group) {
297       if (group->GetUuid() != uuid) {
298         log::error("group {} exists but for different uuid: {}, user request uuid: {}", group_id,
299                    group->GetUuid(), uuid);
300         return nullptr;
301       }
302 
303       log::info("group already exists: {}", group_id);
304       return group;
305     }
306 
307     DeviceGroup new_group(group_id, uuid);
308     groups_.insert({group_id, std::move(new_group)});
309 
310     return &groups_.at(group_id);
311   }
312 
create_group(Uuid & uuid)313   DeviceGroup* create_group(Uuid& uuid) {
314     /* Generate new group id and return empty group */
315     /* Find first free id */
316 
317     int group_id = -1;
318     for (int i = 1; i < kMaxGroupId; i++) {
319       if (groups_.count(i) == 0) {
320         group_id = i;
321         break;
322       }
323     }
324 
325     if (group_id < 0) {
326       log::error("too many groups");
327       return nullptr;
328     }
329 
330     DeviceGroup group(group_id, uuid);
331     groups_.insert({group_id, std::move(group)});
332 
333     return &groups_.at(group_id);
334   }
335 
336   std::map<int, DeviceGroup> groups_;
337   std::list<DeviceGroupsCallbacks*> callbacks_;
338 };
339 
Initialize(DeviceGroupsCallbacks * callbacks)340 void DeviceGroups::Initialize(DeviceGroupsCallbacks* callbacks) {
341   std::scoped_lock<std::mutex> lock(instance_mutex);
342   if (instance == nullptr) {
343     instance = new DeviceGroupsImpl(callbacks);
344     return;
345   }
346 
347   instance->AddCallbacks(callbacks);
348 }
349 
AddFromStorage(const RawAddress & addr,const std::vector<uint8_t> & in)350 void DeviceGroups::AddFromStorage(const RawAddress& addr, const std::vector<uint8_t>& in) {
351   if (!instance) {
352     log::error("Not initialized yet");
353     return;
354   }
355 
356   instance->DeserializeGroups(addr, in);
357 }
358 
GetForStorage(const RawAddress & addr,std::vector<uint8_t> & out)359 bool DeviceGroups::GetForStorage(const RawAddress& addr, std::vector<uint8_t>& out) {
360   if (!instance) {
361     log::error("Not initialized yet");
362     return false;
363   }
364 
365   return instance->SerializeGroups(addr, out);
366 }
367 
CleanUp(DeviceGroupsCallbacks * callbacks)368 void DeviceGroups::CleanUp(DeviceGroupsCallbacks* callbacks) {
369   std::scoped_lock<std::mutex> lock(instance_mutex);
370   if (!instance) {
371     return;
372   }
373 
374   if (instance->Clear(callbacks)) {
375     delete (instance);
376     instance = nullptr;
377   }
378 }
379 
operator <<(std::ostream & out,bluetooth::groups::DeviceGroup const & group)380 std::ostream& operator<<(std::ostream& out, bluetooth::groups::DeviceGroup const& group) {
381   out << "    == Group id: " << group.group_id_ << " == \n"
382       << "      Uuid: " << group.group_uuid_ << std::endl;
383   out << "      Devices:\n";
384   for (auto const& addr : group.devices_) {
385     out << "        " << addr.ToRedactedStringForLogging() << std::endl;
386   }
387   return out;
388 }
389 
DebugDump(int fd)390 void DeviceGroups::DebugDump(int fd) {
391   std::scoped_lock<std::mutex> lock(instance_mutex);
392   dprintf(fd, "Device Groups Manager:\n");
393   if (instance) {
394     instance->Dump(fd);
395   } else {
396     dprintf(fd, "  Not initialized \n");
397   }
398 }
399 
Get()400 DeviceGroups* DeviceGroups::Get() { return instance; }
401 
402 }  // namespace groups
403 }  // namespace bluetooth
404