simple-policy: remove all the bluetooth policy stuff

This is complex and it gets in the way of forward-porting
the bluez-alsa integration bits from the 0.1 branch.

simple-policy will be removed anyway and replaced with
the configuration-based logic that is in the works, so
we will not need this code again, even if we switch back
to the native pipewire bluez5 integration
This commit is contained in:
George Kiagiadakis 2019-10-07 17:39:00 +03:00
parent 9e7a5a4c30
commit 5779375e85
3 changed files with 78 additions and 304 deletions

View file

@ -68,7 +68,7 @@ group_find_backend (struct group *group, WpCore *core)
/* find the backend */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "mixer");
g_variant_dict_insert (&d, "media.class", "s", "Alsa/Sink");
g_variant_dict_insert (&d, "media.class", "s", "Audio/Sink");
g_variant_dict_insert (&d, "media.role", "s", group->name);
backend = wp_policy_find_endpoint (core, g_variant_dict_end (&d),

View file

@ -55,50 +55,24 @@ static gboolean
parse_alsa_properties (WpProperties *props, const gchar **name,
const gchar **media_class, enum pw_direction *direction)
{
const char *local_name = NULL;
const char *local_media_class = NULL;
enum pw_direction local_direction;
/* Get the name */
local_name = wp_properties_get (props, PW_KEY_NODE_NAME);
if (!local_name)
*name = wp_properties_get (props, PW_KEY_NODE_NAME);
if (!*name)
return FALSE;
/* Get the media class */
local_media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
if (!local_media_class)
*media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
if (!*media_class)
return FALSE;
/* Get the direction */
if (g_str_has_prefix (local_media_class, "Audio/Sink"))
local_direction = PW_DIRECTION_INPUT;
else if (g_str_has_prefix (local_media_class, "Audio/Source"))
local_direction = PW_DIRECTION_OUTPUT;
if (g_str_has_prefix (*media_class, "Audio/Sink"))
*direction = PW_DIRECTION_INPUT;
else if (g_str_has_prefix (*media_class, "Audio/Source"))
*direction = PW_DIRECTION_OUTPUT;
else
return FALSE;
/* Set the name */
if (name)
*name = local_name;
/* Set the media class */
if (media_class) {
switch (local_direction) {
case PW_DIRECTION_INPUT:
*media_class = "Alsa/Sink";
break;
case PW_DIRECTION_OUTPUT:
*media_class = "Alsa/Source";
break;
default:
break;
}
}
/* Set the direction */
if (direction)
*direction = local_direction;
return TRUE;
}

View file

@ -7,7 +7,6 @@
*/
#include <wp/wp.h>
#include <pipewire/pipewire.h>
enum {
DIRECTION_SINK = 0,
@ -126,80 +125,41 @@ select_endpoint (WpSimplePolicy *self, gint direction, WpEndpoint *ep,
}
static gboolean
try_select_new_endpoint (WpSimplePolicy *self, gint direction,
const gchar *media_class)
select_new_endpoint (WpSimplePolicy *self)
{
g_autoptr (WpCore) core = NULL;
g_autoptr (GPtrArray) ptr_array = NULL;
const gchar *media_class = NULL;
WpEndpoint *other;
guint32 control_id;
gint i;
/* Get the list of endpoints matching the media class */
core = wp_policy_get_core (WP_POLICY (self));
ptr_array = wp_endpoint_find (core, media_class);
/* Find the endpoint in the list */
for (i = 0; i < (ptr_array ? ptr_array->len : 0); i++) {
other = g_ptr_array_index (ptr_array, i);
if (g_str_has_prefix (media_class, "Alsa/")) {
/* If Alsa, select the "selected" endpoint */
control_id =
wp_endpoint_find_control (other, WP_STREAM_ID_NONE, "selected");
if (control_id == WP_CONTROL_ID_NONE)
continue;
select_endpoint (self, direction, other, control_id);
return TRUE;
} else {
/* If non-Alsa (Bluez and Stream), select the first endpoint */
select_endpoint (self, direction, other, WP_CONTROL_ID_NONE);
return TRUE;
}
}
return FALSE;
}
static gboolean
select_new_endpoint (WpSimplePolicy *self)
{
gint direction;
const gchar *bluez_headunit_media_class = NULL;
const gchar *bluez_a2dp_media_class = NULL;
const gchar *alsa_media_class = NULL;
gint direction, i;
if (!self->selected[DIRECTION_SINK]) {
direction = DIRECTION_SINK;
bluez_headunit_media_class = "Bluez/Sink/Headunit";
bluez_a2dp_media_class = "Bluez/Sink/A2dp";
alsa_media_class = "Alsa/Sink";
media_class = "Audio/Sink";
} else if (!self->selected[DIRECTION_SOURCE]) {
direction = DIRECTION_SOURCE;
bluez_headunit_media_class = "Bluez/Source/Headunit";
bluez_a2dp_media_class = "Bluez/Source/A2dp";
alsa_media_class = "Alsa/Source";
media_class = "Audio/Source";
} else
return G_SOURCE_REMOVE;
/* Bluez has higher priority than Alsa. Bluez A2DP profile has lower
* priority than Bluez non-gatewat profile (Headunit). Bluez Gateway profiles
* are not handled here because they always need to be linked with Alsa
* endpoints, so the priority list is as folows (from higher to lower):
* - Bluez Headunit
* - Bluez A2DP
* - Alsa
*/
core = wp_policy_get_core (WP_POLICY (self));
/* Try to select a Bluez Headunit endpoint */
if (try_select_new_endpoint (self, direction, bluez_headunit_media_class))
return G_SOURCE_REMOVE;
/* Get all the endpoints with the same media class */
ptr_array = wp_endpoint_find (core, media_class);
/* Try to select a Bluez A2dp endpoint */
if (try_select_new_endpoint (self, direction, bluez_a2dp_media_class))
return G_SOURCE_REMOVE;
/* select the first available that has the "selected" control */
for (i = 0; i < (ptr_array ? ptr_array->len : 0); i++) {
other = g_ptr_array_index (ptr_array, i);
/* Try to select an Alsa endpoint */
try_select_new_endpoint (self, direction, alsa_media_class);
control_id =
wp_endpoint_find_control (other, WP_STREAM_ID_NONE, "selected");
if (control_id == WP_CONTROL_ID_NONE)
continue;
select_endpoint (self, direction, other, control_id);
break;
}
return G_SOURCE_REMOVE;
}
@ -213,7 +173,7 @@ simple_policy_endpoint_added (WpPolicy *policy, WpEndpoint *ep)
gint direction;
/* we only care about alsa device endpoints here */
if (!g_str_has_prefix (media_class, "Alsa/"))
if (!g_str_has_prefix (media_class, "Audio/"))
return;
/* verify it has the "selected" control available */
@ -304,32 +264,38 @@ on_endpoint_link_created(GObject *initable, GAsyncResult *res, gpointer d)
}
}
static gboolean
link_endpoint (WpPolicy *policy, WpEndpoint *ep, GVariant *target_props)
static void
handle_client (WpPolicy *policy, WpEndpoint *ep)
{
const char *media_class = wp_endpoint_get_media_class(ep);
GVariantDict d;
g_autoptr (WpCore) core = NULL;
g_autoptr (WpEndpoint) target = NULL;
guint32 stream_id;
guint direction;
gboolean is_capture = FALSE;
g_autofree gchar *role = NULL;
/* Check if the endpoint is capture or not */
direction = wp_endpoint_get_direction (WP_ENDPOINT (ep));
switch (direction) {
case PW_DIRECTION_INPUT:
is_capture = TRUE;
break;
case PW_DIRECTION_OUTPUT:
is_capture = FALSE;
break;
default:
return FALSE;
}
/* Detect if the client is doing capture or playback */
is_capture = g_str_has_prefix (media_class, "Stream/Input");
/* Locate the target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_capture ? "Audio/Source" : "Audio/Sink");
g_object_get (ep, "role", &role, NULL);
if (role)
g_variant_dict_insert (&d, "media.role", "s", role);
/* TODO: more properties are needed here */
core = wp_policy_get_core (policy);
target = wp_policy_find_endpoint (core, target_props, &stream_id);
if (!target)
return FALSE;
target = wp_policy_find_endpoint (core, g_variant_dict_end (&d), &stream_id);
if (!target) {
g_warning ("Could not find target endpoint");
return;
}
/* if the client is already linked... */
if (wp_endpoint_is_linked (ep)) {
@ -345,7 +311,7 @@ link_endpoint (WpPolicy *policy, WpEndpoint *ep, GVariant *target_props)
/* ... do nothing if it's already linked to the correct target */
g_debug ("Client '%s' already linked correctly",
wp_endpoint_get_name (ep));
return TRUE;
return;
} else {
/* ... or else unlink it and continue */
g_debug ("Unlink client '%s' from its previous target",
@ -370,7 +336,7 @@ link_endpoint (WpPolicy *policy, WpEndpoint *ep, GVariant *target_props)
wp_endpoint_unlink (target);
}
/* Link the endpoint with the target */
/* Link the client with the target */
if (is_capture) {
wp_endpoint_link_new (core, target, stream_id, ep, WP_STREAM_ID_NONE,
on_endpoint_link_created, NULL);
@ -378,101 +344,6 @@ link_endpoint (WpPolicy *policy, WpEndpoint *ep, GVariant *target_props)
wp_endpoint_link_new (core, ep, WP_STREAM_ID_NONE, target, stream_id,
on_endpoint_link_created, NULL);
}
return TRUE;
}
static void
handle_client (WpPolicy *policy, WpEndpoint *ep)
{
const char *media_class = wp_endpoint_get_media_class(ep);
GVariantDict d;
gboolean is_capture = FALSE;
const gchar *role;
/* Detect if the client is doing capture or playback */
is_capture = g_str_has_prefix (media_class, "Stream/Input");
/* All Stream client endpoints need to be linked with a Bluez non-gateway
* endpoint if any. If there is no Bluez non-gateway endpoints, the Stream
* client needs to be linked with a Bluez A2DP endpoint. Finally, if none
* of the previous endpoints are found, the Stream client needs to be linked
* with an Alsa endpoint.
*/
/* Link endpoint with Bluez non-gateway target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_capture ? "Bluez/Source/Headunit" : "Bluez/Sink/Headunit");
if (link_endpoint (policy, ep, g_variant_dict_end (&d)))
return;
/* Link endpoint with Bluez A2DP target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_capture ? "Bluez/Source/A2dp" : "Bluez/Sink/A2dp");
if (link_endpoint (policy, ep, g_variant_dict_end (&d)))
return;
/* Link endpoint with Alsa target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_capture ? "Alsa/Source" : "Alsa/Sink");
g_object_get (ep, "role", &role, NULL);
if (role)
g_variant_dict_insert (&d, "media.role", "s", role);
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
g_info ("Could not find alsa target endpoint for client stream");
}
static void
handle_bluez_non_gateway (WpPolicy *policy, WpEndpoint *ep)
{
GVariantDict d;
const char *media_class = wp_endpoint_get_media_class(ep);
gboolean is_sink = FALSE;
/* All bluetooth non-gateway endpoints (A2DP/HSP_HS/HFP_HF) always
* need to be linked with the stream endpoints so that the computer
* does not play any sound
*/
/* Detect if the client is a sink or not */
is_sink = g_str_has_prefix (media_class, "Bluez/Sink");
/* Link endpoint with Stream target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_sink ? "Stream/Output/Audio" : "Stream/Input/Audio");
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
g_info ("Could not find stream target endpoint for non-gateway bluez");
}
static void
handle_bluez_gateway (WpPolicy *policy, WpEndpoint *ep)
{
/* All bluetooth gateway endpoints (HSP_GW/HFP_GW) always need to
* be linked with the alsa endpoints so that the computer can act
* as a head unit
*/
GVariantDict d;
const char *media_class = wp_endpoint_get_media_class(ep);
gboolean is_sink = FALSE;
/* Detect if the client is a sink or not */
is_sink = g_str_has_prefix (media_class, "Bluez/Sink");
/* Link endpoint with Alsa target endpoint */
g_variant_dict_init (&d, NULL);
g_variant_dict_insert (&d, "action", "s", "link");
g_variant_dict_insert (&d, "media.class", "s",
is_sink ? "Alsa/Source" : "Alsa/Sink");
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
g_info ("Could not find alsa target endpoint for gateway bluez");
}
static gint
@ -504,103 +375,35 @@ compare_client_priority (gconstpointer a, gconstpointer b, gpointer user_data)
return ret;
}
static gint
compare_bluez_non_gateway_priority (gconstpointer a, gconstpointer b,
gpointer user_data)
{
WpEndpoint *ae = *(const gpointer *) a;
WpEndpoint *be = *(const gpointer *) b;
const char *a_media_class, *b_media_class;
gint a_priority, b_priority;
/* Bluez priorities (Gateway is a different case):
- Headset (1)
- A2dp (0)
*/
/* Get endpoint A priority */
a_media_class = wp_endpoint_get_media_class(ae);
a_priority = g_str_has_suffix (a_media_class, "Headset") ? 1 : 0;
/* Get endpoint B priority */
b_media_class = wp_endpoint_get_media_class(be);
b_priority = g_str_has_suffix (b_media_class, "Headset") ? 1 : 0;
/* Return the difference of both priorities */
return a_priority - b_priority;
}
static gint
compare_bluez_gateway_priority (gconstpointer a, gconstpointer b,
gpointer user_data)
{
/* Since Bluez Gateway profile does not have any priorities, just
* return positive to indicate endpoint A has higher priority than
* endpoint B */
return 1;
}
static void
rescan_sink_endpoints (WpSimplePolicy *self, const gchar *media_class,
void (*handler) (WpPolicy *policy, WpEndpoint *ep))
static gboolean
simple_policy_rescan_in_idle (WpSimplePolicy *self)
{
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
g_autoptr (GPtrArray) endpoints = NULL;
WpEndpoint *ep;
gint i;
endpoints = wp_endpoint_find (core, media_class);
g_debug ("rescanning for clients that need linking");
endpoints = wp_endpoint_find (core, "Stream/Input/Audio");
if (endpoints) {
/* link all sink endpoints */
/* link all capture clients */
for (i = 0; i < endpoints->len; i++) {
ep = g_ptr_array_index (endpoints, i);
handler (WP_POLICY (self), ep);
handle_client (WP_POLICY (self), ep);
}
}
}
static void
rescan_source_endpoints (WpSimplePolicy *self, const gchar *media_class,
void (*handle) (WpPolicy *policy, WpEndpoint *ep),
GCompareDataFunc comp_func)
{
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
g_autoptr (GPtrArray) endpoints = NULL;
WpEndpoint *ep;
endpoints = wp_endpoint_find (core, media_class);
endpoints = wp_endpoint_find (core, "Stream/Output/Audio");
if (endpoints && endpoints->len > 0) {
/* sort based on priorities */
g_ptr_array_sort_with_data (endpoints, comp_func, self->role_priorities);
/* sort based on role priorities */
g_ptr_array_sort_with_data (endpoints, compare_client_priority,
self->role_priorities);
/* link the highest priority */
/* link the highest priority client */
ep = g_ptr_array_index (endpoints, 0);
handle (WP_POLICY (self), ep);
handle_client (WP_POLICY (self), ep);
}
}
static gboolean
simple_policy_rescan_in_idle (WpSimplePolicy *self)
{
/* Alsa endpoints are never handled */
/* Handle clients */
rescan_sink_endpoints (self, "Stream/Input/Audio", handle_client);
rescan_source_endpoints (self, "Stream/Output/Audio", handle_client,
compare_client_priority);
/* Handle Bluez non-gateway */
rescan_sink_endpoints (self, "Bluez/Sink/Headunit", handle_bluez_non_gateway);
rescan_source_endpoints (self, "Bluez/Source/Headunit",
handle_bluez_non_gateway, compare_bluez_non_gateway_priority);
rescan_sink_endpoints (self, "Bluez/Sink/A2dp", handle_bluez_non_gateway);
rescan_source_endpoints (self, "Bluez/Source/A2dp", handle_bluez_non_gateway,
compare_bluez_non_gateway_priority);
/* Handle Bluez gateway */
rescan_sink_endpoints (self, "Bluez/Sink/Gateway", handle_bluez_gateway);
rescan_source_endpoints (self, "Bluez/Source/Gateway",
handle_bluez_gateway, compare_bluez_gateway_priority);
self->pending_rescan = 0;
return G_SOURCE_REMOVE;
@ -618,17 +421,18 @@ static gboolean
simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
{
WpSimplePolicy *self = WP_SIMPLE_POLICY (policy);
const char *media_class = wp_endpoint_get_media_class(ep);
const char *media_class = NULL;
/* Schedule rescan only if endpoint is audio stream or bluez */
if ((g_str_has_prefix (media_class, "Stream") &&
g_str_has_suffix (media_class, "Audio")) ||
g_str_has_prefix (media_class, "Bluez")) {
simple_policy_rescan (self);
return TRUE;
/* TODO: For now we only accept audio stream clients */
media_class = wp_endpoint_get_media_class(ep);
if (!g_str_has_prefix (media_class, "Stream") ||
!g_str_has_suffix (media_class, "Audio")) {
return FALSE;
}
return FALSE;
/* Schedule a rescan that will handle the endpoint */
simple_policy_rescan (self);
return TRUE;
}
static WpEndpoint *
@ -656,7 +460,7 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
/* Get the endpoint with the "selected" flag (if it is an alsa endpoint) */
for (i = 0; i < ptr_array->len; i++) {
ep = g_ptr_array_index (ptr_array, i);
if (g_str_has_prefix (media_class, "Alsa/")) {
if (g_str_has_prefix (media_class, "Audio/")) {
g_autoptr (GVariant) value = NULL;
guint id;
@ -676,10 +480,6 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
ep = (ptr_array->len > 0) ?
g_object_ref (g_ptr_array_index (ptr_array, 0)) : NULL;
/* Don't select any stream if it is not an alsa endpoint */
if (!g_str_has_prefix (media_class, "Alsa/"))
return ep;
select_stream:
g_variant_lookup (props, "media.role", "&s", &role);
if (!g_strcmp0 (action, "mixer") && !g_strcmp0 (role, "Master"))