Merge the former "hsp-output" and "a2dp-output" ports into one single
port, in order to fix the regression of having several independent
entries in the UI.
Without this patch, device modules will be left around after the
device has been disconnected and when they are reconnected, the
discovery module will load duplicate device module instances.
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=57239
This is a minor optimization too, but the main benefit is that it's
makes the code easier to understand (I hope), since run_callback()
won't be called at times when it's not needed.
The new helper function makes it easier to check whether any audio
profiles are connected. That information is needed by the discovery
module for deciding whether a new device module should be loaded. The
device module should use this information too to unload itself at the
right time, but that's currently not implemented.
Use a more accurate name for the function since it doesn't just check
if it is an audio device (which can be detected quite early), but it
also checks if the most relevant properties (device info, etc.) have
been received.
Besides, add the const qualifier to the pointer since it's not going to
be modified.
The Device.Connected was only used for tracking whether a device module
should be loaded, but that information is already included in the
individual profile state properties. The property can therefore be
completely ignored without any loss in functionality.
Since some devices can be chatty with regards to how often they return
from poll(), this adds a PA_UNLIKELY() to all the the rewind_requested
checks in our sink modules to make the general case (no rewind was
requested) the fast path.
UUIDs might be announced at any time, so a hook is needed to notify any
interested module. In practice, the UUIDs are quite stable with the
exception of the pairing procedure, where the UUIDs are reported by
BlueZ as soon as they are discovered.
Instead of repeatedly asking the discovery API to find a device given
our device path, let's hold a pointer to the device and make sure we
remove the reference when the hook is fired reporting that the device
has been removed. This makes the code easier to follow and slightly
more efficient.
The internal API in bluetooth-util should not use the const qualifier
for operations involving a device object. After all, the structure
contains many pointers and thus the const qualifier provides no real
protection.
Instead of repeatedly asking the discovery API to find a transport given
our transport path, let's hold a pointer to the transport and make sure
we remove the reference when the hook is fired reporting that the
transport has been removed. This makes the code easier to follow and
slightly more efficient.
The recently added hook can be used to detect that the transport being
used has been removed. In this case, the profile needs to be set to off.
Additionally, the change fixes a significant problem: without this
transition, the transport could be destroyed while the hook slots (i.e.
nrec_changed_slot) were still set. This led to a double free of these
objects in stop_thread().
The internal API in bluetooth-util should not use the const qualifier
for operations modifying the transport object. This is specially useful
in order to use the available hooks.
If profile could not be successfully initialized, the card should be
set to PROFILE_OFF automatically. If sinks or sources exist, they need
to be destroyed, therefore stop_thread() is called.
Remove stream moving policies from module-bluetooth-device. It is not
clear if such policies are needed at all and in case yes, they should be
implemented in module-bluetooth-policy.
If the card is being set to off profile, it is not necessary to check
if the device exists. This could potentially happen during shutdown,
immediately before the module is unloaded.
It might happen that a PropertyChanged signal is received but the
corresponding card profile has not been created, leading to an assertion
failure in filter_cb() due to inexistent ports. This can happen if BlueZ
misbehaves, or also if the UUIDs are reported later on (i.e. during
pairing discovery). In any case, the signal should just be ignored.
Handling the signal DisconnectRequested should be unnecessary since the
profile-specific interfaces will be later disconnected, leading to
module unload.
Additionally, the signal is problematic: if an interface (i.e.
A2DP AudioSource) is playing at the time DisconnectRequested is
signaled, the following sequence can occur:
1. AudioSource is playing
2. DisconnectRequested is received
3. Module is unloaded due to DisconnectRequested
4. AudioSource state changes from playing to connected
5. module-bluetooth-discover loads the module
6. AudioSource state changes from connected to disconnected
Therefore the module is unnecessarily loaded, to be unloaded immediately
afterwards. This can easily be reproduced if a device is unpaired while
the audio is streaming.
The simplest solution to this consists of removing step 3, by just
ignoring the DisconnectRequested signal. This reverts commit
8169a6a6c9.
Handle availability changes in Bluetooth ports inside
module-bluetooth-policy. The implemented behavior is similar to how
module-switch-on-port-available behaves, but the conditions are more
relaxed and thus more profile changes are triggered.
When PA is doing gateway role, let module-suspend-on-idle resume the
audio stream automatically. This will work until the user (or the remote
side, which we also consider user-initiated) suspend the stream
manually.
Card profile hfgw should be no different from the rest, and thus no
internal policy inside module-bluetooth-device should decide to switch
to its profile automatically.
This should be handled by policy modules.
Handle the Playing->Connected transition gracefully by releasing the
transport and setting the sink and sources as suspended. This is
necessary since the IO thread might not encounter a HUP always.
Until today, setting the card to some profile resulted in a transport
acquisition, leading to audio stream setup. This is generally not very
interesting and even undesireable for HFGW use-cases, where the
Gateway role (the remote end) would typically request the SCO link.
Nevertheless, there is no safe way to implement such check without race
conditions, since the BlueZ's state can change between the state report
and the call to Acquire(). The chances for this to reproduce are quite
low though, since interface state changes are relatively slow.
This race condition requires that BlueZ's API is extended in order to
perform the operation atomically, which has already been discussed and
ack-ed in the BlueZ mailing list.
Note that this patch does not introduce a new race condition, since it
already existed before (the PropertyChanged->Acquire race condition,
affecting HFGW use-cases). It is just more explicit now.
If the acquisition of the transport fails, the profile should still be
set. In this case the audio is not actually streaming, so the sink and
source will be created but left suspended.
If the transport needs to be acquired later, for example because the
user wants to route the audio the remote device, the suspend flag should
have to be changed.
Use the port availability flag to expose whether a certain profile is
connected and whether it's doing actual audio streaming.
The proposed mapping is the following:
- Profile disconnected: port is unavailable
- Profile is connected (but not streaming/playing): availability unknown
- Profile is streaming/playing: port is available
The availability-unknown is specially interesting: it involves that if
the sink/source exists (corresponding card profile set), it is currently
in suspended state.
For example, for SCO cases (HFGW or HSP), this means the SCO is down. A
policy module would typically not change this, unless someone is really
trying to use the sink/source. This situation would be nicely handled by
module-suspend-on-idle, which would automatically connect SCO.
On the other hand, if the user wants to control the status of the SCO,
it will still be possible by resuming the sink or source (suspend=0).
This works out-of-the-box since most UIs would show to the user ports
whose availability is unknown.
The configuration of the transport that depends on the MTU should be
performed every time the transport has been acquired, since the
parameters depend on what the Media API provides. This requires to
update the parameters of the sinks and sources as well.
This patch moves this code into a new function that will be called
when the stream is starting (setup_stream), from the IO thread.
This makes the code more robust, since the existing multiple calls to
bt_transport_acquire() do not rely on setup_bt() being able to acquire
the transport.
There should be one port per sink/source so a dummy set_port callback
will be enough.
Adding this callback avoid the "operation not implemented" error
message and additionally makes the module work nicely with
module-switch-on-port-available.
The transport might have disapeared exactly before acquiring, so we
should avoid an assertion failure, in this case inside the function
pa_bluetooth_discovery_get_by_path().
The HFGW source should be consistent with the sink by not setting the
"phone" intended role.
Even though setting this role seems to make sense strictly speaking, the
rest of the codebase doesn't handle this well. Therefore, the audio
coming from a Bluetooth phone can be routed back to the same device.
Make code more readable by introducing the helper function
bt_transport_is_acquired(). This also adds assertions to check whether
the internal state is consistent.