* [PATCH BlueZ v1 2/4] adapter: Unify the A2DP admin allowlist UUID mapping
2026-09-10 18:42 [PATCH BlueZ v1 1/4] gatt-server: Check prepare write length before reallocating Luiz Augusto von Dentz
@ 2026-09-10 18:42 ` Luiz Augusto von Dentz
2026-09-10 18:42 ` [PATCH BlueZ v1 3/4] battery: Document the unknown sentinels and fix a stale name Luiz Augusto von Dentz
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-10 18:42 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
The allowlist enforcement added across the adapter, device and A2DP
layers ended up using three different UUIDs for the same two profiles:
adapter.c mapped both a2dp-source and a2dp-sink to ADVANCED_AUDIO_UUID
(0x110d), while device.c and a2dp.c mapped them to the local role UUIDs
0x110a and 0x110b. An allowlist containing only 0x110a therefore blocked
the a2dp-sink adapter probe outright, so the finer grained SEP checks in
a2dp.c never ran.
Add btd_profile_get_policy_uuid() as the single place resolving a
profile to the UUID of the local service it provides. btd_profile names
and remote_uuid describe the remote role, so the A2DP profiles are
inverted there: "a2dp-source" registers the local Sink server and
"a2dp-sink" registers the local Source server. This matches what
device.c and a2dp.c already do.
Note that allowlisting 0x110d alone no longer enables A2DP; the local
role UUIDs 0x110a and/or 0x110b have to be listed.
This also drops the hardcoded profile name table in adapter.c. Every
entry but the two A2DP ones just returned profile->remote_uuid, and any
profile without a local_uuid that was missing from the table resolved to
NULL, i.e. was allowed unconditionally regardless of the allowlist.
Assisted-by: opencode:claude-opus-5
---
src/adapter.c | 49 +------------------------------------------------
src/device.c | 25 ++-----------------------
src/profile.c | 24 ++++++++++++++++++++++++
src/profile.h | 7 +++++++
4 files changed, 34 insertions(+), 71 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 157d7392d32c..fc734424be07 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -5353,59 +5353,12 @@ static void load_drivers(struct btd_adapter *adapter)
probe_driver(adapter, l->data);
}
-struct profile_allowlist_map {
- const char *name;
- const char *uuid;
- bool use_remote_uuid;
-};
-
-/*
- * Adapter server policy UUID defaults to local_uuid when available.
- * Profiles listed below are exceptions.
- *
- * A2DP profiles map to ADVANCED_AUDIO_UUID so profile probe gating matches
- * the A2DP profile class (0x110d), while adapter service registration/removal
- * still filters specific Source/Sink records by their own UUIDs.
- */
-static const struct profile_allowlist_map profile_allowlist_map[] = {
- { "a2dp-source", ADVANCED_AUDIO_UUID, false },
- { "a2dp-sink", ADVANCED_AUDIO_UUID, false },
- { "audio-avrcp-target", NULL, true },
- { "avrcp-controller", NULL, true },
- { "vcp", NULL, true },
- { "micp", NULL, true },
- { "ccp", NULL, true },
- { "gmap", NULL, true },
- { "tmap", NULL, true },
- { "bass", NULL, true },
- { "bap", NULL, true },
- { "mcp-gmcs", NULL, true },
-};
-
static const char *profile_allowlist_uuid(const struct btd_profile *profile)
{
- size_t i;
-
if (profile->local_uuid)
return profile->local_uuid;
- if (!profile->name)
- return NULL;
-
- for (i = 0; i < ARRAY_SIZE(profile_allowlist_map); i++) {
- const struct profile_allowlist_map *entry =
- &profile_allowlist_map[i];
-
- if (strcmp(profile->name, entry->name))
- continue;
-
- if (entry->use_remote_uuid)
- return profile->remote_uuid;
-
- return entry->uuid;
- }
-
- return NULL;
+ return btd_profile_get_policy_uuid(profile);
}
bool btd_adapter_is_profile_allowed(struct btd_adapter *adapter,
diff --git a/src/device.c b/src/device.c
index 8da576ec5932..ec74c44ac4aa 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2547,27 +2547,6 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
return NULL;
}
-static const char *service_policy_uuid(const struct btd_profile *profile)
-{
- if (!profile)
- return NULL;
-
- /*
- * For A2DP device services, apply admin policy by local role UUID:
- * - a2dp-sink profile is local source role (110a)
- * - a2dp-source profile is local sink role (110b)
- */
- if (profile->name) {
- if (!strcmp(profile->name, "a2dp-sink"))
- return A2DP_SOURCE_UUID;
-
- if (!strcmp(profile->name, "a2dp-source"))
- return A2DP_SINK_UUID;
- }
-
- return profile->remote_uuid;
-}
-
bool btd_device_all_services_allowed(struct btd_device *dev)
{
GSList *l;
@@ -2579,7 +2558,7 @@ bool btd_device_all_services_allowed(struct btd_device *dev)
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
service = l->data;
profile = btd_service_get_profile(service);
- uuid = service_policy_uuid(profile);
+ uuid = btd_profile_get_policy_uuid(profile);
if (!profile || !profile->auto_connect || !uuid)
continue;
@@ -2614,7 +2593,7 @@ void btd_device_update_allowed_services(struct btd_device *dev)
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
service = l->data;
profile = btd_service_get_profile(service);
- uuid = service_policy_uuid(profile);
+ uuid = btd_profile_get_policy_uuid(profile);
if (!profile || !uuid)
continue;
diff --git a/src/profile.c b/src/profile.c
index 97fffe9b4d5c..e5b1cdfc5d33 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -16,6 +16,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <glib.h>
@@ -753,6 +754,29 @@ void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
}
}
+const char *btd_profile_get_policy_uuid(const struct btd_profile *profile)
+{
+ if (!profile)
+ return NULL;
+
+ /*
+ * The admin allowlist is expressed in terms of the services the local
+ * adapter exposes, while btd_profile names and remote_uuid describe
+ * the remote role. The A2DP profiles therefore have to be inverted:
+ * the "a2dp-source" profile drives the local Sink server and the
+ * "a2dp-sink" profile drives the local Source server.
+ */
+ if (profile->name) {
+ if (!strcmp(profile->name, "a2dp-source"))
+ return A2DP_SINK_UUID;
+
+ if (!strcmp(profile->name, "a2dp-sink"))
+ return A2DP_SOURCE_UUID;
+ }
+
+ return profile->remote_uuid;
+}
+
static struct btd_profile *btd_profile_find_uuid(const char *uuid)
{
GSList *l, *next;
diff --git a/src/profile.h b/src/profile.h
index 04a99528b4be..270f09ea2b96 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -84,6 +84,13 @@ struct btd_profile {
void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
void *data);
+/*
+ * Return the UUID of the local service a profile provides, for use by the
+ * admin policy allowlist. Returns NULL when the profile has no service to
+ * match against.
+ */
+const char *btd_profile_get_policy_uuid(const struct btd_profile *profile);
+
int btd_profile_register(struct btd_profile *profile);
void btd_profile_unregister(struct btd_profile *profile);
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH BlueZ v1 3/4] battery: Document the unknown sentinels and fix a stale name
2026-09-10 18:42 [PATCH BlueZ v1 1/4] gatt-server: Check prepare write length before reallocating Luiz Augusto von Dentz
2026-09-10 18:42 ` [PATCH BlueZ v1 2/4] adapter: Unify the A2DP admin allowlist UUID mapping Luiz Augusto von Dentz
@ 2026-09-10 18:42 ` Luiz Augusto von Dentz
2026-09-10 18:42 ` [PATCH BlueZ v1 4/4] client/bluetoothctl: Declare the controller helper in its own header Luiz Augusto von Dentz
2026-09-10 20:06 ` [BlueZ,v1,1/4] gatt-server: Check prepare write length before reallocating bluez.test.bot
3 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-10 18:42 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
btd_battery_update() treats UINT8_MAX and btd_battery_update_charging()
treats -1 as "unknown", invalidating the D-Bus property rather than
setting it. Neither was documented at the prototypes.
Also rename unregister_if_path_has_prefix() to
unregister_if_provider_matches(); it no longer compares path prefixes,
it compares the owning provider.
Assisted-by: opencode:claude-opus-5
---
src/battery.c | 4 ++--
src/battery.h | 8 ++++++++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/battery.c b/src/battery.c
index 181bf3846d52..dbfe56881259 100644
--- a/src/battery.c
+++ b/src/battery.c
@@ -664,7 +664,7 @@ static bool match_provider_path(const void *data, const void *user_data)
return strcmp(provider->path, path) == 0;
}
-static void unregister_if_path_has_prefix(void *data, void *user_data)
+static void unregister_if_provider_matches(void *data, void *user_data)
{
struct btd_battery *battery = data;
struct battery_provider *provider = user_data;
@@ -678,7 +678,7 @@ static void battery_provider_free(gpointer data)
struct battery_provider *provider = data;
/* Unregister batteries registered by this provider. */
- queue_foreach(batteries, unregister_if_path_has_prefix, provider);
+ queue_foreach(batteries, unregister_if_provider_matches, provider);
if (provider->owner)
g_free(provider->owner);
diff --git a/src/battery.h b/src/battery.h
index 2b459809cba6..5990c8e0b87f 100644
--- a/src/battery.h
+++ b/src/battery.h
@@ -18,7 +18,15 @@ struct btd_battery *btd_battery_register_component(const char *device_path,
const char *identifier,
const char *source);
bool btd_battery_unregister(struct btd_battery *battery);
+
+/* Pass UINT8_MAX as percentage to mark the level as unknown, which
+ * invalidates the Percentage property instead of setting it.
+ */
bool btd_battery_update(struct btd_battery *battery, uint8_t percentage);
+
+/* charging is 0 or 1 when known, or -1 to mark the state as unknown, which
+ * invalidates the Charging property instead of setting it.
+ */
bool btd_battery_update_charging(struct btd_battery *battery, int charging);
struct btd_battery_provider_manager *
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH BlueZ v1 4/4] client/bluetoothctl: Declare the controller helper in its own header
2026-09-10 18:42 [PATCH BlueZ v1 1/4] gatt-server: Check prepare write length before reallocating Luiz Augusto von Dentz
2026-09-10 18:42 ` [PATCH BlueZ v1 2/4] adapter: Unify the A2DP admin allowlist UUID mapping Luiz Augusto von Dentz
2026-09-10 18:42 ` [PATCH BlueZ v1 3/4] battery: Document the unknown sentinels and fix a stale name Luiz Augusto von Dentz
@ 2026-09-10 18:42 ` Luiz Augusto von Dentz
2026-09-10 20:06 ` [BlueZ,v1,1/4] gatt-server: Check prepare write length before reallocating bluez.test.bot
3 siblings, 0 replies; 5+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-10 18:42 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
bluetoothctl_get_default_controller() is implemented in main.c but was
declared in admin.h, the header of one of the submenus that consumes it.
Move the declaration to a new client/main.h so main.c also sees it.
While at it, drop the bluetooth/bluetooth.h include added to admin.c,
which is unused.
Assisted-by: opencode:claude-opus-5
---
Makefile.tools | 1 +
client/admin.c | 2 +-
client/admin.h | 2 --
client/main.c | 1 +
client/main.h | 11 +++++++++++
5 files changed, 14 insertions(+), 3 deletions(-)
create mode 100644 client/main.h
diff --git a/Makefile.tools b/Makefile.tools
index cb113f96161e..6844a52ac739 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -12,6 +12,7 @@ client_bluetoothctl_SOURCES = client/main.c \
client/adv_monitor.c \
client/gatt.h client/gatt.c \
client/admin.h client/admin.c \
+ client/main.h \
client/player.h client/player.c \
client/mgmt.h client/mgmt.c \
client/assistant.h client/assistant.c \
diff --git a/client/admin.c b/client/admin.c
index a6fba2f1ce1d..98276f56c652 100644
--- a/client/admin.c
+++ b/client/admin.c
@@ -16,11 +16,11 @@
#include <string.h>
#include <stdlib.h>
-#include "bluetooth/bluetooth.h"
#include "gdbus/gdbus.h"
#include "src/shared/shell.h"
#include "admin.h"
+#include "main.h"
#define _GNU_SOURCE
static DBusConnection *dbus_conn;
diff --git a/client/admin.h b/client/admin.h
index 00423d9d35cb..0047770dc737 100644
--- a/client/admin.h
+++ b/client/admin.h
@@ -10,5 +10,3 @@
void admin_add_submenu(void);
void admin_remove_submenu(void);
-
-GDBusProxy *bluetoothctl_get_default_controller(void);
diff --git a/client/main.c b/client/main.c
index d8d2e32d9556..8f7a7c4135ca 100644
--- a/client/main.c
+++ b/client/main.c
@@ -28,6 +28,7 @@
#include "src/shared/util.h"
#include "src/shared/ad.h"
#include "gdbus/gdbus.h"
+#include "main.h"
#include "print.h"
#include "agent.h"
#include "gatt.h"
diff --git a/client/main.h b/client/main.h
new file mode 100644
index 000000000000..0d2af5c5014c
--- /dev/null
+++ b/client/main.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2026 Collabora Ltd.
+ *
+ *
+ */
+
+GDBusProxy *bluetoothctl_get_default_controller(void);
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread