Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 246f0bb8 authored by Krzysztof Kopyscinski (xWF)'s avatar Krzysztof Kopyscinski (xWF) Committed by Gerrit Code Review
Browse files

Merge changes I1fa01b7e,Ia0c0af6a into main

* changes:
  pandora: add support to VCP tests
  VCS: check group only for group set in `setDeviceVolume`
parents 6ee1a2f8 43ab3cae
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -450,13 +450,14 @@ public class VolumeControlService extends ProfileService {
            Log.e(TAG, "leAudioService not available");
            return;
        }

        if (isGroupOp) {
            int groupId = leAudioService.getGroupId(device);
            if (groupId == IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) {
                Log.e(TAG, "Device not a part of a group");
                return;
            }

        if (isGroupOp) {
            setGroupVolume(groupId, volume);
        } else {
            Log.i(TAG, "Setting individual device volume");
+17 −1
Original line number Diff line number Diff line
@@ -904,7 +904,23 @@ public class VolumeControlServiceTest {
    }

    @Test
    public void serviceBinderRegisterUnregisterCallback() {
    @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES)
    public void testServiceBinderSetDeviceVolumeNoGroupId() throws Exception {
        int deviceVolume = 42;
        when(mLeAudioService.getGroupId(mDevice)).thenReturn(LE_AUDIO_GROUP_ID_INVALID);

        generateDeviceAvailableMessageFromNative(mDevice, 1);
        generateConnectionMessageFromNative(
                mDevice, STATE_CONNECTED, STATE_DISCONNECTED);
        assertThat(mService.getDevices()).contains(mDevice);

        mBinder.setDeviceVolume(mDevice, deviceVolume, false, mAttributionSource);
        verify(mNativeInterface).setVolume(mDevice, deviceVolume);
        assertThat(mService.getDeviceVolume(mDevice)).isEqualTo(deviceVolume);
    }

    @Test
    public void testServiceBinderRegisterUnregisterCallback() throws Exception {
        IBluetoothVolumeControlCallback callback =
                Mockito.mock(IBluetoothVolumeControlCallback.class);
        Binder binder = Mockito.mock(Binder.class);
+70 −6
Original line number Diff line number Diff line
@@ -17,23 +17,33 @@ import threading
from mmi2grpc._helpers import assert_description, match_description
from mmi2grpc._proxy import ProfileProxy
from mmi2grpc._rootcanal import Dongle

from pandora_experimental.vcp_grpc import VCP
from pandora_experimental.gatt_grpc import GATT
from pandora.security_grpc import Security, SecurityStorage
from pandora.security_pb2 import LE_LEVEL3, PairingEventAnswer
from pandora.host_grpc import Host
from pandora.host_pb2 import PUBLIC, RANDOM
from pandora.security_grpc import Security
from pandora.security_pb2 import LE_LEVEL3, PairingEventAnswer
from pandora_experimental.le_audio_grpc import LeAudio

from time import sleep


class VCPProxy(ProfileProxy):

    def __init__(self, channel, rootcanal):
        super().__init__(channel)
        self.vcp = VCP(channel)
        self.gatt = GATT(channel)
        self.security_storage = SecurityStorage(channel)
        self.host = Host(channel)
        self.security = Security(channel)
        self.le_audio = LeAudio(channel)
        self.rootcanal = rootcanal
        self.connection = None
        self.pairing_stream = None
        self.pairing_stream = self.security.OnPairing()

    def test_started(self, test: str, description: str, pts_addr: bytes):
        self.rootcanal.select_pts_dongle(Dongle.LAIRD_BL654)
@@ -49,13 +59,18 @@ class VCPProxy(ProfileProxy):
        the Implementation Under Test (IUT) can initiate a GATT connect request
        to the PTS.
        """
        self.security_storage.DeleteBond(public=pts_addr)
        self.connection = self.host.ConnectLE(own_address_type=RANDOM, public=pts_addr).connection
        self.pairing_stream = self.security.OnPairing()

        def secure():
            self.security.Secure(connection=self.connection, le=LE_LEVEL3)

        def vcp_connect():
            self.vcp.WaitConnect(connection=self.connection)

        threading.Thread(target=secure).start()
        threading.Thread(target=vcp_connect).start()

        return "OK"

    @match_description
@@ -84,6 +99,10 @@ class VCPProxy(ProfileProxy):
        Description: Verify that the Implementation Under Test \(IUT\) can send
        Discover All Characteristics command.
        """
        # PTS expects us to do discovery after bonding, but in fact Android does it as soon as
        # encryption is completed. Invalidate GATT cache so the discovery takes place again
        self.gatt.ClearCache(connection=self.connection)

        return "OK"

    @match_description
@@ -92,13 +111,26 @@ class VCPProxy(ProfileProxy):
        Please send Read Request to read (?P<name>(Volume State|Volume Flags|Offset State)) characteristic with handle
        = (?P<handle>(0x[0-9A-Fa-f]{4})).
        """
        # After discovery Android reads these values by itself, after profile connection.
        # Although, for some tests, this is used as validation, for example for tests with invalid
        # behavior (BI tests). Just send GATT read to sattisfy this conditions, as VCP has no exposed
        # (or even existing, native) interface to trigger read on demand.
        def read():
            nonlocal handle
            self.gatt.ReadCharacteristicFromHandle(\
                    connection=self.connection, handle=int(handle, base=16))

        worker = threading.Thread(target=read)
        worker.start()
        worker.join(timeout=30)

        return "OK"

    @assert_description
    def USER_CONFIRM_SUPPORTED_CHARACTERISTIC(self, characteristics: str, **kwargs):
    @match_description
    def USER_CONFIRM_SUPPORTED_CHARACTERISTIC(self, body: str, **kwargs):
        """
        Please verify that for each supported characteristic, attribute
        handle/UUID pair(s) is returned to the upper tester.(?P<characteristics>(.|\n)*)
        handle/UUID pair\(s\) is returned to the (.*)\.(?P<body>.*)
        """

        return "OK"
@@ -109,6 +141,38 @@ class VCPProxy(ProfileProxy):
        Please write to Client Characteristic Configuration Descriptor of
        (?P<name>(Volume State|Offset State)) characteristic to enable notification.
        """

        # After discovery Android subscribes by itself, after profile connection
        return "OK"

    def IUT_SEND_WRITE_REQUEST(self, description: str, **kwargs):
        r"""
        Please send write request to handle 0xXXXX with following value.
        Characteristic name:
            Op Code: [X (0xXX)] Op code name
            Change Counter: <WildCard: Exists>
            Value: <WildCard: Exists>
        """

        # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed)
        sleep(2)

        if ("Set Absolute Volume" in description):
            self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
        elif ("Unmute" in description):
            # for now, there is no way to trigger this, and tests are skipped
            return "No"
        elif ("Set Volume Offset" in description):
            self.vcp.SetVolumeOffset(connection=self.connection, offset=42)
        elif ("Volume Control Point" in description and
              "Op Code: <WildCard: Exists>" in description):
            # Handles sending *any* OP Code on Volume Control Point
            self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
        elif ("Volume Offset Control Point" in description and
              "Op Code: <WildCard: Exists>" in description):
            self.vcp.SetVolumeOffset(connection=self.connection, offset=42)


        return "OK"

    @assert_description
+16 −15
Original line number Diff line number Diff line
@@ -686,7 +686,19 @@
    "SM/PER/SCJW/BV-03-C",
    "SM/PER/SCPK/BI-03-C",
    "SM/PER/SCPK/BV-02-C",
    "VCP/VC/CGGIT/CHA/BV-06-C"
    "VCP/VC/CGGIT/CHA/BV-01-C",
    "VCP/VC/CGGIT/CHA/BV-02-C",
    "VCP/VC/CGGIT/CHA/BV-03-C",
    "VCP/VC/CGGIT/CHA/BV-04-C",
    "VCP/VC/CGGIT/CHA/BV-06-C",
    "VCP/VC/CGGIT/SER/BV-01-C",
    "VCP/VC/CGGIT/SER/BV-02-C",
    "VCP/VC/SPE/BI-05-C",
    "VCP/VC/SPE/BI-13-C",
    "VCP/VC/SPE/BI-15-C",
    "VCP/VC/SPE/BI-16-C",
    "VCP/VC/VCCP/BV-05-C",
    "VCP/VC/VOCP/BV-01-C"
  ],
  "flaky": [
    "A2DP/SRC/SUS/BV-02-I",
@@ -1145,20 +1157,8 @@
    "SM/PER/SCPK/BV-03-C",
    "SM/PER/SIE/BV-01-C",
    "SM/PER/SIP/BV-01-C",
    "VCP/VC/CGGIT/CHA/BV-01-C",
    "VCP/VC/CGGIT/CHA/BV-02-C",
    "VCP/VC/CGGIT/CHA/BV-03-C",
    "VCP/VC/CGGIT/CHA/BV-04-C",
    "VCP/VC/CGGIT/SER/BV-01-C",
    "VCP/VC/CGGIT/SER/BV-02-C",
    "VCP/VC/SPE/BI-05-C",
    "VCP/VC/SPE/BI-06-C",
    "VCP/VC/SPE/BI-13-C",
    "VCP/VC/SPE/BI-15-C",
    "VCP/VC/SPE/BI-16-C",
    "VCP/VC/VCCP/BV-05-C",
    "VCP/VC/VCCP/BV-06-C",
    "VCP/VC/VOCP/BV-01-C"
    "VCP/VC/VCCP/BV-06-C"
  ],
  "ics": {
    "TSPC_4.0HCI_1_1": true,
@@ -3168,7 +3168,8 @@
    "SM": {},
    "SPP": {},
    "SUM ICS": {},
    "VCP": {}
    "VCP": {
    }
  },
  "flags": [
    {
+1 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ class Server(context: Context) {
                        BluetoothProfile.OPP to ::Opp,
                        BluetoothProfile.MAP to ::Map,
                        BluetoothProfile.LE_AUDIO to ::LeAudio,
                        BluetoothProfile.VOLUME_CONTROL to ::Vcp,
                    )
                    .filter { bluetoothAdapter.isEnabled }
                    .filter { bluetoothAdapter.getSupportedProfiles().contains(it.key) == true }
Loading