diff mbox series

[BlueZ,v4,3/3] audio/avrcp: Determine Absolute Volume support from feature category 2

Message ID 20241005214321.84250-4-marijn.suijten@somainline.org
State New
Headers show
Series audio/avrcp: Determine Absolute Volume support from feature category 2 | expand

Commit Message

Marijn Suijten Oct. 5, 2024, 9:43 p.m. UTC
The AVRCP spec (1.6.2) does not mention anything about a version
requirement for Absolute Volume, despite this feature only existing
since spec version 1.4.  Android reports a version of 1.3 [1] for its
"AVRCP remote" (CT) service and mentions in the comment above it itself
relies on feature bits rather than the exposed version.  As it stands
BlueZ requires at least version 1.4 making it unable to communicate
absolute volume levels with even the most recent Android phones running
Fluoride (have not checked the version on Gabeldorsche).

The spec states that supporting SetAbsoluteVolume and
EVENT_VOLUME_CHANGED are mandatory when feature level 2 is declared,
excluded otherwise.  This feature bit is set on Android and, when used
by this patch, allows for successfully communicating volume back and
forth despite the version theoretically being too low.

In order to not affect spec tests too much (which I doubt would catch
this, and should have otherwise pointed out that Android itself is out
of spec) this behaviour is guarded behind a config option in main.conf,
as discussed in [2].

Note that this workaround is deliberately omitted for the "AVRCP
target" profile version, since Android already signals that to be 1.4
(which allows receiving SetAbsoluteVolume calls or registration for
EVENT_VOLUME_CHANGED notifications) for other reasons [3].

[1]: https://android.googlesource.com/platform/system/bt/+/android-11.0.0_r28/bta/av/bta_av_main.cc#761
[2]: https://marc.info/?l=linux-bluetooth&m=163463497503113&w=2
[3]: https://android.googlesource.com/platform/system/bt/+/android-11.0.0_r28/bta/av/bta_av_main.cc#755
---
 profiles/audio/avrcp.c | 20 ++++++++++++--------
 src/btd.h              |  1 +
 src/main.c             |  5 +++++
 src/main.conf          |  9 +++++++++
 4 files changed, 27 insertions(+), 8 deletions(-)

Comments

Luiz Augusto von Dentz Oct. 7, 2024, 3:56 p.m. UTC | #1
Hi Marijn,

On Sat, Oct 5, 2024 at 5:43 PM Marijn Suijten
<marijn.suijten@somainline.org> wrote:
>
> The AVRCP spec (1.6.2) does not mention anything about a version
> requirement for Absolute Volume, despite this feature only existing
> since spec version 1.4.  Android reports a version of 1.3 [1] for its
> "AVRCP remote" (CT) service and mentions in the comment above it itself
> relies on feature bits rather than the exposed version.  As it stands
> BlueZ requires at least version 1.4 making it unable to communicate
> absolute volume levels with even the most recent Android phones running
> Fluoride (have not checked the version on Gabeldorsche).
>
> The spec states that supporting SetAbsoluteVolume and
> EVENT_VOLUME_CHANGED are mandatory when feature level 2 is declared,
> excluded otherwise.  This feature bit is set on Android and, when used
> by this patch, allows for successfully communicating volume back and
> forth despite the version theoretically being too low.
>
> In order to not affect spec tests too much (which I doubt would catch
> this, and should have otherwise pointed out that Android itself is out
> of spec) this behaviour is guarded behind a config option in main.conf,
> as discussed in [2].
>
> Note that this workaround is deliberately omitted for the "AVRCP
> target" profile version, since Android already signals that to be 1.4
> (which allows receiving SetAbsoluteVolume calls or registration for
> EVENT_VOLUME_CHANGED notifications) for other reasons [3].
>
> [1]: https://android.googlesource.com/platform/system/bt/+/android-11.0.0_r28/bta/av/bta_av_main.cc#761
> [2]: https://marc.info/?l=linux-bluetooth&m=163463497503113&w=2
> [3]: https://android.googlesource.com/platform/system/bt/+/android-11.0.0_r28/bta/av/bta_av_main.cc#755
> ---
>  profiles/audio/avrcp.c | 20 ++++++++++++--------
>  src/btd.h              |  1 +
>  src/main.c             |  5 +++++
>  src/main.conf          |  9 +++++++++
>  4 files changed, 27 insertions(+), 8 deletions(-)
>
> diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
> index 005c3e306..84172a6f6 100644
> --- a/profiles/audio/avrcp.c
> +++ b/profiles/audio/avrcp.c
> @@ -1779,9 +1779,10 @@ static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
>          * The controller on the remote end is only allowed to call SetAbsoluteVolume
>          * on our target if it's at least version 1.4 and a category-2 device.
>          */
> -       if (!session->target || session->target->version < 0x0104 ||
> +       if (!session->target ||
> +                       (btd_opts.avrcp.volume_version && session->target->version < 0x0104) ||
>                         (btd_opts.avrcp.volume_category && !(session->target->features & AVRCP_FEATURE_CATEGORY_2))) {
> -               error("Remote SetAbsoluteVolume rejected from non-category-2 peer");
> +               error("Remote SetAbsoluteVolume rejected from non-category-2 or non-AVRCP-1.4 peer");
>                 goto err;
>         }
>
> @@ -4262,13 +4263,15 @@ static void target_init(struct avrcp *session)
>                                 (1 << AVRCP_EVENT_TRACK_REACHED_END) |
>                                 (1 << AVRCP_EVENT_SETTINGS_CHANGED);
>
> -       if (target->version < 0x0104)
> -               return;
> -
> -       if (!btd_opts.avrcp.volume_category || target->features & AVRCP_FEATURE_CATEGORY_2)
> +       /* Remote device supports receiving volume notifications */
> +       if ((!btd_opts.avrcp.volume_version || target->version >= 0x0104) &&
> +                       (!btd_opts.avrcp.volume_category || target->features & AVRCP_FEATURE_CATEGORY_2))
>                 session->supported_events |=
>                                 (1 << AVRCP_EVENT_VOLUME_CHANGED);
>
> +       if (target->version < 0x0104)
> +               return;
> +
>         session->supported_events |=
>                                 (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
>                                 (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED);
> @@ -4688,9 +4691,10 @@ int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
>                 return -ENOTCONN;
>
>         if (notify) {
> -               if (!session->target || session->target->version < 0x0104 ||
> +               if (!session->target ||
> +                               (btd_opts.avrcp.volume_version && session->target->version < 0x0104) ||
>                                 (btd_opts.avrcp.volume_category && !(session->target->features & AVRCP_FEATURE_CATEGORY_2))) {
> -                       error("Can't send EVENT_VOLUME_CHANGED to non-category-2 peer");
> +                       error("Can't send EVENT_VOLUME_CHANGED to non-category-2 or non-AVRCP-1.4 peer");
>                         return -ENOTSUP;
>                 }
>                 return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
> diff --git a/src/btd.h b/src/btd.h
> index 07205aa69..61e4d309d 100644
> --- a/src/btd.h
> +++ b/src/btd.h
> @@ -107,6 +107,7 @@ struct btd_avdtp_opts {
>  struct btd_avrcp_opts {
>         bool            volume_without_target;
>         bool            volume_category;
> +       bool            volume_version;
>  };
>
>  struct btd_advmon_opts {
> diff --git a/src/main.c b/src/main.c
> index 89ee6897c..e8504cbe3 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -168,6 +168,7 @@ static const char *avdtp_options[] = {
>  static const char *avrcp_options[] = {
>         "VolumeWithoutTarget",
>         "VolumeCategory",
> +       "VolumeVersion",
>         NULL
>  };
>
> @@ -1155,6 +1156,9 @@ static void parse_avrcp(GKeyFile *config)
>         parse_config_bool(config, "AVRCP",
>                 "VolumeCategory",
>                 &btd_opts.avrcp.volume_category);
> +       parse_config_bool(config, "AVRCP",
> +               "VolumeVersion",
> +               &btd_opts.avrcp.volume_version);
>  }
>
>  static void parse_advmon(GKeyFile *config)
> @@ -1225,6 +1229,7 @@ static void init_defaults(void)
>
>         btd_opts.avrcp.volume_without_target = false;
>         btd_opts.avrcp.volume_category = true;
> +       btd_opts.avrcp.volume_version = false;
>
>         btd_opts.advmon.rssi_sampling_period = 0xFF;
>         btd_opts.csis.encrypt = true;
> diff --git a/src/main.conf b/src/main.conf
> index fff13ed2f..b6b32a720 100644
> --- a/src/main.conf
> +++ b/src/main.conf
> @@ -316,6 +316,15 @@
>  # notifications.
>  #VolumeCategory = true
>
> +# Require peer AVRCP controllers to have at least version 1.4 before
> +# accessing category-2 (absolute volume) features (depending on the value
> +# of VolumeCategory above).  It is common for Android-powered devices to not
> +# signal the desired minimum version of 1.4 while still supporting absolute
> +# volume based on the feature category bit, as mentioned in this comment:
> +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
> +# av/bta_av_main.cc#621
> +#VolumeVersion = false

I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
user can switch to 1.3 or "any" in case he want to bypass version
checking, also perhaps we should create an issue for Android folks to
fix their version, as it seems they do support browsing features
channel for TG they should be able to do the same for CT.

> +
>  [Policy]
>  #
>  # The ReconnectUUIDs defines the set of remote services that should try
> --
> 2.46.2
>
Marijn Suijten Oct. 9, 2024, 10:30 a.m. UTC | #2
On 2024-10-07 11:56:06, Luiz Augusto von Dentz wrote:
> Hi Marijn,
> 
<snip>
> > diff --git a/src/main.conf b/src/main.conf
> > index fff13ed2f..b6b32a720 100644
> > --- a/src/main.conf
> > +++ b/src/main.conf
> > @@ -316,6 +316,15 @@
> >  # notifications.
> >  #VolumeCategory = true
> >
> > +# Require peer AVRCP controllers to have at least version 1.4 before
> > +# accessing category-2 (absolute volume) features (depending on the value
> > +# of VolumeCategory above).  It is common for Android-powered devices to not
> > +# signal the desired minimum version of 1.4 while still supporting absolute
> > +# volume based on the feature category bit, as mentioned in this comment:
> > +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
> > +# av/bta_av_main.cc#621
> > +#VolumeVersion = false
> 
> I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
> user can switch to 1.3 or "any" in case he want to bypass version
> checking

We can surely change this to parse a version which would override the version
of the remote CT, and rename it to CTVersion since it's no longer only affecting
volume?  Maybe add a TGVersion as well, and/or something else entirely?

That would save the ugly combinatorial explosion.  Maybe the same works for
VolumeCategory introduced in the previous patch as well?

> also perhaps we should create an issue for Android folks to
> fix their version, as it seems they do support browsing features
> channel for TG they should be able to do the same for CT.

I don't think this patch aged particularly well as hinted by the testing
steps in the cover letter: on my Android 14 phone AVRCP 1.5 is the default in
developer settings, so they might have realized that this was a problem in the
past.  Don't think we need to report it anymore, and we should perhaps start
discussing whether this patch is still desired in the first place?  Either way
I'd appreciate to land the first and second patch.

- Marijn

> > +
> >  [Policy]
> >  #
> >  # The ReconnectUUIDs defines the set of remote services that should try
> > --
> > 2.46.2
> >
> 
> 
> -- 
> Luiz Augusto von Dentz
Luiz Augusto von Dentz Oct. 9, 2024, 6:06 p.m. UTC | #3
Hi Marijn,

On Wed, Oct 9, 2024 at 6:30 AM Marijn Suijten
<marijn.suijten@somainline.org> wrote:
>
> On 2024-10-07 11:56:06, Luiz Augusto von Dentz wrote:
> > Hi Marijn,
> >
> <snip>
> > > diff --git a/src/main.conf b/src/main.conf
> > > index fff13ed2f..b6b32a720 100644
> > > --- a/src/main.conf
> > > +++ b/src/main.conf
> > > @@ -316,6 +316,15 @@
> > >  # notifications.
> > >  #VolumeCategory = true
> > >
> > > +# Require peer AVRCP controllers to have at least version 1.4 before
> > > +# accessing category-2 (absolute volume) features (depending on the value
> > > +# of VolumeCategory above).  It is common for Android-powered devices to not
> > > +# signal the desired minimum version of 1.4 while still supporting absolute
> > > +# volume based on the feature category bit, as mentioned in this comment:
> > > +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
> > > +# av/bta_av_main.cc#621
> > > +#VolumeVersion = false
> >
> > I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
> > user can switch to 1.3 or "any" in case he want to bypass version
> > checking
>
> We can surely change this to parse a version which would override the version
> of the remote CT, and rename it to CTVersion since it's no longer only affecting
> volume?  Maybe add a TGVersion as well, and/or something else entirely?
>
> That would save the ugly combinatorial explosion.  Maybe the same works for
> VolumeCategory introduced in the previous patch as well?

Yeah, well we could do something to disable strict version check so we
rely only on capabilities, anyway a good way to avoid interoperability
problems is to be very strict to the spec to what we send but more
relaxed to what we receive, the latter may not be possible if there
are qualification tests that requires us to strict though.

> > also perhaps we should create an issue for Android folks to
> > fix their version, as it seems they do support browsing features
> > channel for TG they should be able to do the same for CT.
>
> I don't think this patch aged particularly well as hinted by the testing
> steps in the cover letter: on my Android 14 phone AVRCP 1.5 is the default in
> developer settings, so they might have realized that this was a problem in the
> past.  Don't think we need to report it anymore, and we should perhaps start
> discussing whether this patch is still desired in the first place?  Either way
> I'd appreciate to land the first and second patch.

Oh, in that case why are we even bothering to change this? Or this is
to allow working with Android when the user has set it to AVRCP 1.3,
well that still sound like a bug if you ask me, even if it is not the
default behavior that should have been 1.4.

> - Marijn
>
> > > +
> > >  [Policy]
> > >  #
> > >  # The ReconnectUUIDs defines the set of remote services that should try
> > > --
> > > 2.46.2
> > >
> >
> >
> > --
> > Luiz Augusto von Dentz
Bartosz Fabianowski Oct. 9, 2024, 7:36 p.m. UTC | #4
On 09/10/2024 12:30, Marijn Suijten wrote:
> On 2024-10-07 11:56:06, Luiz Augusto von Dentz wrote:
>> Hi Marijn,
>>
> <snip>
>>> diff --git a/src/main.conf b/src/main.conf
>>> index fff13ed2f..b6b32a720 100644
>>> --- a/src/main.conf
>>> +++ b/src/main.conf
>>> @@ -316,6 +316,15 @@
>>>   # notifications.
>>>   #VolumeCategory = true
>>>
>>> +# Require peer AVRCP controllers to have at least version 1.4 before
>>> +# accessing category-2 (absolute volume) features (depending on the value
>>> +# of VolumeCategory above).  It is common for Android-powered devices to not
>>> +# signal the desired minimum version of 1.4 while still supporting absolute
>>> +# volume based on the feature category bit, as mentioned in this comment:
>>> +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
>>> +# av/bta_av_main.cc#621
>>> +#VolumeVersion = false
>>
>> I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
>> user can switch to 1.3 or "any" in case he want to bypass version
>> checking
> 
> We can surely change this to parse a version which would override the version
> of the remote CT, and rename it to CTVersion since it's no longer only affecting
> volume?  Maybe add a TGVersion as well, and/or something else entirely?
> 
> That would save the ugly combinatorial explosion.  Maybe the same works for
> VolumeCategory introduced in the previous patch as well?
> 
>> also perhaps we should create an issue for Android folks to
>> fix their version, as it seems they do support browsing features
>> channel for TG they should be able to do the same for CT.
> 
> I don't think this patch aged particularly well as hinted by the testing
> steps in the cover letter: on my Android 14 phone AVRCP 1.5 is the default in
> developer settings, so they might have realized that this was a problem in the
> past.  Don't think we need to report it anymore, and we should perhaps start
> discussing whether this patch is still desired in the first place?  Either way
> I'd appreciate to land the first and second patch.

Even if current Android versions report AVRCP 1.5, there will be a lot 
of phones stuck on older versions for many years to come. Compatibility 
with these should still be very desirable.

> 
> - Marijn
> 
>>> +
>>>   [Policy]
>>>   #
>>>   # The ReconnectUUIDs defines the set of remote services that should try
>>> --
>>> 2.46.2
>>>
>>
>>
>> -- 
>> Luiz Augusto von Dentz
>
Luiz Augusto von Dentz Oct. 21, 2024, 7:32 p.m. UTC | #5
Hi Bartosz,

On Wed, Oct 9, 2024 at 3:36 PM Bartosz Fabianowski
<bartosz@fabianowski.eu> wrote:
>
> On 09/10/2024 12:30, Marijn Suijten wrote:
> > On 2024-10-07 11:56:06, Luiz Augusto von Dentz wrote:
> >> Hi Marijn,
> >>
> > <snip>
> >>> diff --git a/src/main.conf b/src/main.conf
> >>> index fff13ed2f..b6b32a720 100644
> >>> --- a/src/main.conf
> >>> +++ b/src/main.conf
> >>> @@ -316,6 +316,15 @@
> >>>   # notifications.
> >>>   #VolumeCategory = true
> >>>
> >>> +# Require peer AVRCP controllers to have at least version 1.4 before
> >>> +# accessing category-2 (absolute volume) features (depending on the value
> >>> +# of VolumeCategory above).  It is common for Android-powered devices to not
> >>> +# signal the desired minimum version of 1.4 while still supporting absolute
> >>> +# volume based on the feature category bit, as mentioned in this comment:
> >>> +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
> >>> +# av/bta_av_main.cc#621
> >>> +#VolumeVersion = false
> >>
> >> I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
> >> user can switch to 1.3 or "any" in case he want to bypass version
> >> checking
> >
> > We can surely change this to parse a version which would override the version
> > of the remote CT, and rename it to CTVersion since it's no longer only affecting
> > volume?  Maybe add a TGVersion as well, and/or something else entirely?
> >
> > That would save the ugly combinatorial explosion.  Maybe the same works for
> > VolumeCategory introduced in the previous patch as well?
> >
> >> also perhaps we should create an issue for Android folks to
> >> fix their version, as it seems they do support browsing features
> >> channel for TG they should be able to do the same for CT.
> >
> > I don't think this patch aged particularly well as hinted by the testing
> > steps in the cover letter: on my Android 14 phone AVRCP 1.5 is the default in
> > developer settings, so they might have realized that this was a problem in the
> > past.  Don't think we need to report it anymore, and we should perhaps start
> > discussing whether this patch is still desired in the first place?  Either way
> > I'd appreciate to land the first and second patch.
>
> Even if current Android versions report AVRCP 1.5, there will be a lot
> of phones stuck on older versions for many years to come. Compatibility
> with these should still be very desirable.

They didn't bother backporting it? I wonder if we could somehow detect
this behavior based on the Android version if that is available
somewhere.

> >
> > - Marijn
> >
> >>> +
> >>>   [Policy]
> >>>   #
> >>>   # The ReconnectUUIDs defines the set of remote services that should try
> >>> --
> >>> 2.46.2
> >>>
> >>
> >>
> >> --
> >> Luiz Augusto von Dentz
> >
>
Bartosz Fabianowski Oct. 21, 2024, 7:53 p.m. UTC | #6
On 21/10/2024 21:32, Luiz Augusto von Dentz wrote:
> Hi Bartosz,
> 
> On Wed, Oct 9, 2024 at 3:36 PM Bartosz Fabianowski
> <bartosz@fabianowski.eu> wrote:
>>
>> On 09/10/2024 12:30, Marijn Suijten wrote:
>>> On 2024-10-07 11:56:06, Luiz Augusto von Dentz wrote:
>>>> Hi Marijn,
>>>>
>>> <snip>
>>>>> diff --git a/src/main.conf b/src/main.conf
>>>>> index fff13ed2f..b6b32a720 100644
>>>>> --- a/src/main.conf
>>>>> +++ b/src/main.conf
>>>>> @@ -316,6 +316,15 @@
>>>>>    # notifications.
>>>>>    #VolumeCategory = true
>>>>>
>>>>> +# Require peer AVRCP controllers to have at least version 1.4 before
>>>>> +# accessing category-2 (absolute volume) features (depending on the value
>>>>> +# of VolumeCategory above).  It is common for Android-powered devices to not
>>>>> +# signal the desired minimum version of 1.4 while still supporting absolute
>>>>> +# volume based on the feature category bit, as mentioned in this comment:
>>>>> +# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
>>>>> +# av/bta_av_main.cc#621
>>>>> +#VolumeVersion = false
>>>>
>>>> I'd change this to have the version e.g. #VolumeVersion = 1.4, so the
>>>> user can switch to 1.3 or "any" in case he want to bypass version
>>>> checking
>>>
>>> We can surely change this to parse a version which would override the version
>>> of the remote CT, and rename it to CTVersion since it's no longer only affecting
>>> volume?  Maybe add a TGVersion as well, and/or something else entirely?
>>>
>>> That would save the ugly combinatorial explosion.  Maybe the same works for
>>> VolumeCategory introduced in the previous patch as well?
>>>
>>>> also perhaps we should create an issue for Android folks to
>>>> fix their version, as it seems they do support browsing features
>>>> channel for TG they should be able to do the same for CT.
>>>
>>> I don't think this patch aged particularly well as hinted by the testing
>>> steps in the cover letter: on my Android 14 phone AVRCP 1.5 is the default in
>>> developer settings, so they might have realized that this was a problem in the
>>> past.  Don't think we need to report it anymore, and we should perhaps start
>>> discussing whether this patch is still desired in the first place?  Either way
>>> I'd appreciate to land the first and second patch.
>>
>> Even if current Android versions report AVRCP 1.5, there will be a lot
>> of phones stuck on older versions for many years to come. Compatibility
>> with these should still be very desirable.
> 
> They didn't bother backporting it? I wonder if we could somehow detect
> this behavior based on the Android version if that is available
> somewhere.

Looks like Android 14 and newer report AVRCP 1.5. Android 13 (which was 
released two years ago and is still on many phones) still uses AVRCP 1.3.

- Bartosz

> 
>>>
>>> - Marijn
>>>
>>>>> +
>>>>>    [Policy]
>>>>>    #
>>>>>    # The ReconnectUUIDs defines the set of remote services that should try
>>>>> --
>>>>> 2.46.2
>>>>>
>>>>
>>>>
>>>> --
>>>> Luiz Augusto von Dentz
>>>
>>
> 
>
diff mbox series

Patch

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 005c3e306..84172a6f6 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -1779,9 +1779,10 @@  static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
 	 * The controller on the remote end is only allowed to call SetAbsoluteVolume
 	 * on our target if it's at least version 1.4 and a category-2 device.
 	 */
-	if (!session->target || session->target->version < 0x0104 ||
+	if (!session->target ||
+			(btd_opts.avrcp.volume_version && session->target->version < 0x0104) ||
 			(btd_opts.avrcp.volume_category && !(session->target->features & AVRCP_FEATURE_CATEGORY_2))) {
-		error("Remote SetAbsoluteVolume rejected from non-category-2 peer");
+		error("Remote SetAbsoluteVolume rejected from non-category-2 or non-AVRCP-1.4 peer");
 		goto err;
 	}
 
@@ -4262,13 +4263,15 @@  static void target_init(struct avrcp *session)
 				(1 << AVRCP_EVENT_TRACK_REACHED_END) |
 				(1 << AVRCP_EVENT_SETTINGS_CHANGED);
 
-	if (target->version < 0x0104)
-		return;
-
-	if (!btd_opts.avrcp.volume_category || target->features & AVRCP_FEATURE_CATEGORY_2)
+	/* Remote device supports receiving volume notifications */
+	if ((!btd_opts.avrcp.volume_version || target->version >= 0x0104) &&
+			(!btd_opts.avrcp.volume_category || target->features & AVRCP_FEATURE_CATEGORY_2))
 		session->supported_events |=
 				(1 << AVRCP_EVENT_VOLUME_CHANGED);
 
+	if (target->version < 0x0104)
+		return;
+
 	session->supported_events |=
 				(1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
 				(1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED);
@@ -4688,9 +4691,10 @@  int avrcp_set_volume(struct btd_device *dev, int8_t volume, bool notify)
 		return -ENOTCONN;
 
 	if (notify) {
-		if (!session->target || session->target->version < 0x0104 ||
+		if (!session->target ||
+				(btd_opts.avrcp.volume_version && session->target->version < 0x0104) ||
 				(btd_opts.avrcp.volume_category && !(session->target->features & AVRCP_FEATURE_CATEGORY_2))) {
-			error("Can't send EVENT_VOLUME_CHANGED to non-category-2 peer");
+			error("Can't send EVENT_VOLUME_CHANGED to non-category-2 or non-AVRCP-1.4 peer");
 			return -ENOTSUP;
 		}
 		return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
diff --git a/src/btd.h b/src/btd.h
index 07205aa69..61e4d309d 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -107,6 +107,7 @@  struct btd_avdtp_opts {
 struct btd_avrcp_opts {
 	bool		volume_without_target;
 	bool		volume_category;
+	bool		volume_version;
 };
 
 struct btd_advmon_opts {
diff --git a/src/main.c b/src/main.c
index 89ee6897c..e8504cbe3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -168,6 +168,7 @@  static const char *avdtp_options[] = {
 static const char *avrcp_options[] = {
 	"VolumeWithoutTarget",
 	"VolumeCategory",
+	"VolumeVersion",
 	NULL
 };
 
@@ -1155,6 +1156,9 @@  static void parse_avrcp(GKeyFile *config)
 	parse_config_bool(config, "AVRCP",
 		"VolumeCategory",
 		&btd_opts.avrcp.volume_category);
+	parse_config_bool(config, "AVRCP",
+		"VolumeVersion",
+		&btd_opts.avrcp.volume_version);
 }
 
 static void parse_advmon(GKeyFile *config)
@@ -1225,6 +1229,7 @@  static void init_defaults(void)
 
 	btd_opts.avrcp.volume_without_target = false;
 	btd_opts.avrcp.volume_category = true;
+	btd_opts.avrcp.volume_version = false;
 
 	btd_opts.advmon.rssi_sampling_period = 0xFF;
 	btd_opts.csis.encrypt = true;
diff --git a/src/main.conf b/src/main.conf
index fff13ed2f..b6b32a720 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -316,6 +316,15 @@ 
 # notifications.
 #VolumeCategory = true
 
+# Require peer AVRCP controllers to have at least version 1.4 before
+# accessing category-2 (absolute volume) features (depending on the value
+# of VolumeCategory above).  It is common for Android-powered devices to not
+# signal the desired minimum version of 1.4 while still supporting absolute
+# volume based on the feature category bit, as mentioned in this comment:
+# https://android.googlesource.com/platform/system/bt/+/android-12.0.0_r1/bta/
+# av/bta_av_main.cc#621
+#VolumeVersion = false
+
 [Policy]
 #
 # The ReconnectUUIDs defines the set of remote services that should try