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