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

Commit 264cf402 authored by Aritra Sen's avatar Aritra Sen Committed by Gerrit Code Review
Browse files

Merge changes Ib0b874ea,I6252c675,I0bdf5417,I6fd70f06,I7b044388, ...

* changes:
  Add API to create bond between two devices using existing btif API.
  Add ability to find discoverable devices.
  Add API to topshim adapter facade to toggle discovery state of the current topshim device.
  Add APIs to toggle Adapter discoverability between none, connectable and connectable/discoverable.
  Send event data over a map instead of a string for better data representation.
  Creating a Python wrapper for gRPC service for HF Client.
  Create a Rust gRPC service for HF Client.
  Create a new topshim API for the BTIF hfp client (btif_hf_client).
parents c9bda0a2 47cc2954
Loading
Loading
Loading
Loading
+56 −1
Original line number Diff line number Diff line
@@ -19,11 +19,13 @@ service AdapterService {
  rpc SetDefaultEventMaskExcept(SetDefaultEventMaskExceptRequest) returns (google.protobuf.Empty) {}
  rpc SetEventFilterInquiryResultAllDevices(google.protobuf.Empty) returns (google.protobuf.Empty) {}
  rpc SetLocalIoCaps(SetLocalIoCapsRequest) returns (SetLocalIoCapsResponse) {}
  rpc ToggleDiscovery(ToggleDiscoveryRequest) returns (ToggleDiscoveryResponse) {}
}

service SecurityService {
    rpc RemoveBond(RemoveBondRequest) returns (google.protobuf.Empty) {}
    rpc GenerateLocalOobData(GenerateOobDataRequest) returns (google.protobuf.Empty) {}
    rpc CreateBond(CreateBondRequest) returns (CreateBondResponse) {}
}

service GattService {
@@ -111,6 +113,13 @@ service HfpService {
  rpc FetchEvents(FetchEventsRequest) returns (stream FetchEventsResponse) {}
}

service HfClientService {
  rpc StartSlc(StartSlcRequest) returns (StartSlcResponse) {}
  rpc StopSlc(StopSlcRequest) returns (StopSlcResponse) {}
  rpc ConnectAudio(ConnectAudioRequest) returns (ConnectAudioResponse) {}
  rpc DisconnectAudio(DisconnectAudioRequest) returns (DisconnectAudioResponse) {}
}

enum EventType {
  ADAPTER_STATE = 0;
  SSP_REQUEST = 1;
@@ -118,12 +127,22 @@ enum EventType {
  GENERATE_LOCAL_OOB_DATA = 3;
  HFP_CONNECTION_STATE = 4;
  ADAPTER_PROPERTY = 5;
  DISCOVERY_STATE = 6;
  DEVICE_FOUND = 7;
  BOND_STATE = 8;
}

message FetchEventsRequest {}

message FetchEventsResponse {
  EventType event_type = 1;
  string data = 2;
  // Storing all event related data as a key-value pair.
  map<string, EventData> params = 3;
}

message EventData {
  repeated string data = 1;
}

message ToggleStackRequest {
@@ -134,9 +153,12 @@ message ToggleStackResponse {}

message SetDiscoveryModeRequest {
  bool enable_page_scan = 1;
  bool enable_inquiry_scan = 2;
}

message SetDiscoveryModeResponse {}
message SetDiscoveryModeResponse {
  int32 status = 1;
}

service MediaService {
  rpc StartA2dp(StartA2dpRequest) returns (StartA2dpResponse) {}
@@ -171,20 +193,36 @@ message StartSlcRequest {
  Connection connection = 1;
}

message StartSlcResponse {
  int32 status = 1;
}

message StopSlcRequest {
  Connection connection = 1;
}

message StopSlcResponse {
  int32 status = 1;
}

message ConnectAudioRequest {
  Connection connection = 1;
  bool is_sco_offload_enabled = 2;
  bool force_cvsd = 3;
}

message ConnectAudioResponse {
  int32 status = 1;
}

message DisconnectAudioRequest {
  Connection connection = 1;
}

message DisconnectAudioResponse {
  int32 status = 1;
}

message SetVolumeRequest {
  Connection connection = 1;
  int32 volume = 2;
@@ -213,3 +251,20 @@ message SetLocalIoCapsRequest {
message SetLocalIoCapsResponse {
  int32 status = 1;
}

message ToggleDiscoveryRequest {
  bool is_start = 1;
}

message ToggleDiscoveryResponse {
  int32 status = 1;
}

message CreateBondRequest {
  string address = 1;
  int32 transport = 2;
}

message CreateBondResponse {
  int32 status = 1;
}
 No newline at end of file
+38 −4
Original line number Diff line number Diff line
@@ -26,14 +26,48 @@ class AdapterTest(TopshimBaseTest):
    def test_verify_adapter_started(self):
        print("Adapter is verified when test starts")

    def test_enable_inquiry_scan(self):
        status, discovery_mode = self.dut().enable_inquiry_scan()
        assertThat(status).isEqualTo("Success")
        assertThat(discovery_mode).isEqualTo("ConnectableDiscoverable")

    def test_enable_page_scan(self):
        self.dut().enable_page_scan()
        status, discovery_mode = self.dut().enable_page_scan()
        assertThat(status).isEqualTo("Success")
        assertThat(discovery_mode).isEqualTo("Connectable")

    def test_disable_page_scan(self):
        status, discovery_mode = self.dut().disable_page_scan()
        assertThat(status).isEqualTo("Success")
        assertThat(discovery_mode).isEqualTo("None_")

    def test_set_local_io_caps(self):
        status, properties = self.dut().set_local_io_caps(3)
        status, caps = self.dut().set_local_io_caps(3)
        assertThat(status).isEqualTo("Success")
        assertThat(len(properties)).isEqualTo(1)
        assertThat(properties[0]).isEqualTo("LocalIoCaps(None_)")
        assertThat(caps).isEqualTo("None_")

    def test_start_discovery(self):
        state = self.dut().toggle_discovery(True)
        assertThat(state).isEqualTo("Started")
        # Reset device to not discovering.
        self.dut().toggle_discovery(False)

    def test_cancel_discovery(self):
        self.dut().toggle_discovery(True)
        state = self.dut().toggle_discovery(False)
        assertThat(state).isEqualTo("Stopped")

    def test_find_device_device_available(self):
        self.dut().enable_inquiry_scan()
        self.cert().enable_inquiry_scan()
        self.dut().toggle_discovery(True)
        device_addr = self.dut().find_device()
        assertThat(device_addr).isNotNone()
        # Reset DUT device discovering and scanning to None
        self.dut().disable_page_scan()
        self.dut().toggle_discovery(False)
        # Reset CERT device to not discoverable
        self.cert().disable_page_scan()


if __name__ == "__main__":
+25 −8
Original line number Diff line number Diff line
@@ -42,8 +42,9 @@ class AdapterClient(AsyncClosable):

    async def close(self):
        for task in self.__task_list:
            if task.done() or task.cancelled():
                continue
            task.cancel()
            task = None
        self.__task_list.clear()
        await self.__channel.close()

@@ -54,7 +55,7 @@ class AdapterClient(AsyncClosable):

            # Match event by some condition.
            if e.event_type == event:
                future.set_result(e.data)
                future.set_result(e.params)
                break
            else:
                print("Got '%s'; expecting '%s'" % (e.event_type, event))
@@ -63,31 +64,39 @@ class AdapterClient(AsyncClosable):
    async def _listen_for_event(self, event):
        """Start fetching events"""
        future = asyncio.get_running_loop().create_future()
        self.__task_list.append(asyncio.get_running_loop().create_task(self.__get_next_event(event, future)))
        task = asyncio.get_running_loop().create_task(self.__get_next_event(event, future))
        self.__task_list.append(task)
        try:
            await asyncio.wait_for(future, AdapterClient.DEFAULT_TIMEOUT)
        except:
            task.cancel()
            print("Failed to get event", event)
        return future

    async def _verify_adapter_started(self):
        future = await self._listen_for_event(facade_pb2.EventType.ADAPTER_STATE)
        return future.result() == "ON"
        params = future.result()
        return params["state"].data[0] == "ON"

    async def toggle_stack(self, is_start=True):
        """Enable/disable the stack"""
        await self.__adapter_stub.ToggleStack(facade_pb2.ToggleStackRequest(start_stack=is_start))
        return await self._verify_adapter_started()

    async def enable_inquiry_scan(self):
        """Enable inquiry scan (Required to make device connectable and discoverable by other devices)"""
        await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_inquiry_scan=True))
        return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY)

    async def enable_page_scan(self):
        """Enable page scan (might be used for A2dp sink to be discoverable)"""
        await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_page_scan=True))
        return await self.le_rand()
        return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY)

    async def disable_page_scan(self):
        """Enable page scan (might be used for A2dp sink to be discoverable)"""
        await self.__adapter_stub.SetDiscoveryMode(facade_pb2.SetDiscoveryModeRequest(enable_page_scan=False))
        return await self.le_rand()
        return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY)

    async def clear_event_filter(self):
        await self.__adapter_stub.ClearEventFilter(empty_proto.Empty())
@@ -104,7 +113,8 @@ class AdapterClient(AsyncClosable):
    async def le_rand(self):
        await self.__adapter_stub.LeRand(empty_proto.Empty())
        future = await self._listen_for_event(facade_pb2.EventType.LE_RAND)
        return future.result()
        params = future.result()
        return params["data"].data[0]

    async def restore_filter_accept_list(self):
        await self.__adapter_stub.RestoreFilterAcceptList(empty_proto.Empty())
@@ -124,9 +134,16 @@ class AdapterClient(AsyncClosable):

    async def set_local_io_caps(self, io_capability):
        await self.__adapter_stub.SetLocalIoCaps(facade_pb2.SetLocalIoCapsRequest(io_capability=io_capability))
        future = await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY)
        return await self._listen_for_event(facade_pb2.EventType.ADAPTER_PROPERTY)

    async def toggle_discovery(self, is_start):
        await self.__adapter_stub.ToggleDiscovery(facade_pb2.ToggleDiscoveryRequest(is_start=is_start))
        future = await self._listen_for_event(facade_pb2.EventType.DISCOVERY_STATE)
        return future

    async def find_device(self):
        return await self._listen_for_event(facade_pb2.EventType.DEVICE_FOUND)


class A2dpAutomationHelper():
    """Invoke gRPC on topshim for A2DP testing"""
+60 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
#
#   Copyright 2022 - The Android Open Source Project
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
import asyncio
import grpc

from blueberry.facade.topshim import facade_pb2
from blueberry.facade.topshim import facade_pb2_grpc
from blueberry.tests.topshim.lib.async_closable import AsyncClosable


class HfClientClient(AsyncClosable):
    """
    Wrapper gRPC interface to the HF Client Service
    """
    __channel = None
    __hf_client_stub = None

    def __init__(self, port=8999):
        self.__channel = grpc.aio.insecure_channel("localhost:%d" % port)
        self.__hf_client_stub = facade_pb2_grpc.HfClientServiceStub(self.__channel)

    async def close(self):
        await self.__channel.close()

    async def start_slc(self, address):
        """
        """
        await self.__hf_client_stub.StartSlc(
            facade_pb2.StartSlcRequest(connection=facade_pb2.Connection(cookie=address.encode())))

    async def stop_slc(self, address):
        """
        """
        await self.__hf_client_stub.StopSlc(
            facade_pb2.StopSlcRequest(connection=facade_pb2.Connection(cookie=address.encode())))

    async def connect_audio(self, address):
        """
        """
        await self.__hf_client_stub.ConnectAudio(
            facade_pb2.ConnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode())))

    async def disconnect_audio(self, address):
        """
        """
        await self.__hf_client_stub.DisconnectAudio(
            facade_pb2.DisconnectAudioRequest(connection=facade_pb2.Connection(cookie=address.encode())))
+10 −0
Original line number Diff line number Diff line
@@ -49,6 +49,16 @@ class SecurityClient(AsyncClosable):
        self.__task_list.clear()
        await self.__channel.close()

    async def create_bond(self, address, transport):
        """
        Create a bonding entry for a given address with a particular transport type
        """
        await self.__security.CreateBond(facade_pb2.CreateBondRequest(address=address, transport=transport))
        # State change to Bonding
        await self.__adapter._listen_for_event(facade_pb2.EventType.BOND_STATE)
        # State change to Bonded or None (based on success and failure)
        return await self.__adapter._listen_for_event(facade_pb2.EventType.BOND_STATE)

    async def remove_bond(self, address):
        """
        Removes a bonding entry for a given address
Loading