Loading android/pandora/mmi2grpc/mmi2grpc/__init__.py +23 −23 Original line number Diff line number Diff line Loading @@ -11,7 +11,6 @@ # 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. """Map Bluetooth PTS Man Machine Interface to Pandora gRPC calls.""" __version__ = "0.0.1" Loading @@ -23,7 +22,9 @@ import sys import grpc from mmi2grpc.a2dp import A2DPProxy from mmi2grpc.hfp import HFPProxy from mmi2grpc._helpers import format_proxy from pandora.host_grpc import Host GRPC_PORT = 8999 Loading @@ -36,8 +37,8 @@ class IUT: Handles MMI calls from the PTS and routes them to corresponding profile proxy which translates MMI calls to gRPC calls to the IUT. """ def __init__( self, test: str, args: List[str], port: int = GRPC_PORT, **kwargs): def __init__(self, test: str, args: List[str], port: int = GRPC_PORT, **kwargs): """Init IUT class for a given test. Args: Loading @@ -50,6 +51,7 @@ class IUT: # Profile proxies. self._a2dp = None self._hfp = None def __enter__(self): """Resets the IUT when starting a PTS test.""" Loading @@ -60,8 +62,10 @@ class IUT: def __exit__(self, exc_type, exc_value, exc_traceback): self._a2dp = None self._hfp = None def _retry(self, func): def wrapper(*args, **kwargs): tries = 0 while True: Loading @@ -74,22 +78,16 @@ class IUT: else: print(f'Retry {func.__name__}: {tries}/{MAX_RETRIES}') time.sleep(1) return wrapper @property def address(self) -> bytes: """Bluetooth MAC address of the IUT.""" with grpc.insecure_channel(f'localhost:{self.port}') as channel: return self._retry( Host(channel).ReadLocalAddress)(wait_for_ready=True).address def interact(self, pts_address: bytes, profile: str, test: str, interaction: str, description: str, style: str, return self._retry(Host(channel).ReadLocalAddress)(wait_for_ready=True).address def interact(self, pts_address: bytes, profile: str, test: str, interaction: str, description: str, style: str, **kwargs) -> str: """Routes MMI calls to corresponding profile proxy. Loading @@ -106,15 +104,17 @@ class IUT: # Handles A2DP and AVDTP MMIs. if profile in ('A2DP', 'AVDTP'): if not self._a2dp: self._a2dp = A2DPProxy( grpc.insecure_channel(f'localhost:{self.port}')) return self._a2dp.interact( test, interaction, description, pts_address) self._a2dp = A2DPProxy(grpc.insecure_channel(f'localhost:{self.port}')) return self._a2dp.interact(test, interaction, description, pts_address) # Handles HFP MMIs. if profile in ('HFP'): if not self._hfp: self._hfp = HFPProxy(grpc.insecure_channel(f'localhost:{self.port}')) return self._hfp.interact(test, interaction, description, pts_address) # Handles unsupported profiles. code = format_proxy(profile, interaction, description) error_msg = ( f'Missing {profile} proxy and mmi: {interaction}\n' error_msg = (f'Missing {profile} proxy and mmi: {interaction}\n' f'Create a {profile.lower()}.py in mmi2grpc/:\n\n{code}\n' f'Then, instantiate the corresponding proxy in __init__.py\n' f'Finally, create a {profile.lower()}.proto in proto/pandora/' Loading android/pandora/mmi2grpc/mmi2grpc/hfp.py 0 → 100644 +70 −0 Original line number Diff line number Diff line # Copyright 2022 Google LLC # # 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 # # https://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. """HFP proxy module.""" from mmi2grpc._helpers import assert_description from mmi2grpc._proxy import ProfileProxy from pandora.hfp_grpc import HFP from pandora.host_grpc import Host import sys import threading class HFPProxy(ProfileProxy): def __init__(self, channel): super().__init__() self.hfp = HFP(channel) self.host = Host(channel) @assert_description def TSC_delete_pairing_iut(self, pts_addr: bytes, **kwargs): """ Delete the pairing with the PTS using the Implementation Under Test (IUT), then click Ok. """ self.host.DeletePairing(address=pts_addr) return "OK" @assert_description def TSC_iut_search(self, **kwargs): """ Using the Implementation Under Test (IUT), perform a search for the PTS. If found, click OK. """ return "OK" @assert_description def TSC_iut_connect(self, pts_addr: bytes, **kwargs): """ Click Ok, then make a connection request to the PTS from the Implementation Under Test (IUT). """ self.connection = self.host.Connect(address=pts_addr).connection return "OK" @assert_description def TSC_iut_disable_slc(self, pts_addr: bytes, **kwargs): """ Click Ok, then disable the service level connection using the Implementation Under Test (IUT). """ self.hfp.DisableSlc(address=pts_addr) return "OK" android/pandora/server/Android.bp +12 −9 Original line number Diff line number Diff line Loading @@ -46,17 +46,17 @@ java_library { "proto/pandora/*.proto", ], static_libs: [ "pandora-proto-java", "grpc-java-lite", "guava", "opencensus-java-api", "libprotobuf-java-lite", "javax_annotation-api_1.3.2", "libprotobuf-java-lite", "opencensus-java-api", "pandora-proto-java", ], proto: { include_dirs: [ "packages/modules/Bluetooth/android/pandora/server/proto", "external/protobuf/src", "packages/modules/Bluetooth/android/pandora/server/proto", ], plugin: "grpc-java-plugin", output_params: [ Loading @@ -78,8 +78,8 @@ java_library { proto: { type: "lite", include_dirs: [ "packages/modules/Bluetooth/android/pandora/server/proto", "external/protobuf/src", "packages/modules/Bluetooth/android/pandora/server/proto", ], }, } Loading @@ -98,13 +98,16 @@ genrule { " --python_out=$(genDir)" + " $(in)", srcs: [ "proto/pandora/a2dp.proto", "proto/pandora/hfp.proto", "proto/pandora/host.proto", "proto/pandora/a2dp.proto" ], out: [ "pandora/host_pb2.py", "pandora/host_grpc.py", "pandora/a2dp_pb2.py", "pandora/a2dp_grpc.py", "pandora/a2dp_pb2.py", "pandora/hfp_grpc.py", "pandora/hfp_pb2.py", "pandora/host_grpc.py", "pandora/host_pb2.py", ] } android/pandora/server/configs/PtsBotTest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer"> <option name="dep-module" value="grpcio" /> <option name="dep-module" value="protobuf==3.20.1" /> <option name="dep-module" value="scipy" /> </target_preparer> <test class="com.android.tradefed.testtype.pandora.PtsBotTest" > Loading @@ -21,5 +22,6 @@ <option name="physical" value="false" /> <option name="profile" value="A2DP/SRC" /> <option name="profile" value="AVDTP/SRC" /> <option name="profile" value="HFP/AG/DIS" /> </test> </configuration> android/pandora/server/proto/pandora/hfp.proto 0 → 100644 +23 −0 Original line number Diff line number Diff line syntax = "proto3"; option java_outer_classname = "HfpProto"; package pandora; import "pandora/host.proto"; import "google/protobuf/wrappers.proto"; // Service to trigger HFP (Hands Free Profile) procedures. service HFP { // Disable Service level connection rpc DisableSlc(DisableSlcRequest) returns (DisableSlcResponse); } // Request of the `DisableSlc` method. message DisableSlcRequest { // Local Bluetooth Device Address as array of 6 bytes. bytes address = 1; } // Response of the `DisableSlc` method. message DisableSlcResponse {} Loading
android/pandora/mmi2grpc/mmi2grpc/__init__.py +23 −23 Original line number Diff line number Diff line Loading @@ -11,7 +11,6 @@ # 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. """Map Bluetooth PTS Man Machine Interface to Pandora gRPC calls.""" __version__ = "0.0.1" Loading @@ -23,7 +22,9 @@ import sys import grpc from mmi2grpc.a2dp import A2DPProxy from mmi2grpc.hfp import HFPProxy from mmi2grpc._helpers import format_proxy from pandora.host_grpc import Host GRPC_PORT = 8999 Loading @@ -36,8 +37,8 @@ class IUT: Handles MMI calls from the PTS and routes them to corresponding profile proxy which translates MMI calls to gRPC calls to the IUT. """ def __init__( self, test: str, args: List[str], port: int = GRPC_PORT, **kwargs): def __init__(self, test: str, args: List[str], port: int = GRPC_PORT, **kwargs): """Init IUT class for a given test. Args: Loading @@ -50,6 +51,7 @@ class IUT: # Profile proxies. self._a2dp = None self._hfp = None def __enter__(self): """Resets the IUT when starting a PTS test.""" Loading @@ -60,8 +62,10 @@ class IUT: def __exit__(self, exc_type, exc_value, exc_traceback): self._a2dp = None self._hfp = None def _retry(self, func): def wrapper(*args, **kwargs): tries = 0 while True: Loading @@ -74,22 +78,16 @@ class IUT: else: print(f'Retry {func.__name__}: {tries}/{MAX_RETRIES}') time.sleep(1) return wrapper @property def address(self) -> bytes: """Bluetooth MAC address of the IUT.""" with grpc.insecure_channel(f'localhost:{self.port}') as channel: return self._retry( Host(channel).ReadLocalAddress)(wait_for_ready=True).address def interact(self, pts_address: bytes, profile: str, test: str, interaction: str, description: str, style: str, return self._retry(Host(channel).ReadLocalAddress)(wait_for_ready=True).address def interact(self, pts_address: bytes, profile: str, test: str, interaction: str, description: str, style: str, **kwargs) -> str: """Routes MMI calls to corresponding profile proxy. Loading @@ -106,15 +104,17 @@ class IUT: # Handles A2DP and AVDTP MMIs. if profile in ('A2DP', 'AVDTP'): if not self._a2dp: self._a2dp = A2DPProxy( grpc.insecure_channel(f'localhost:{self.port}')) return self._a2dp.interact( test, interaction, description, pts_address) self._a2dp = A2DPProxy(grpc.insecure_channel(f'localhost:{self.port}')) return self._a2dp.interact(test, interaction, description, pts_address) # Handles HFP MMIs. if profile in ('HFP'): if not self._hfp: self._hfp = HFPProxy(grpc.insecure_channel(f'localhost:{self.port}')) return self._hfp.interact(test, interaction, description, pts_address) # Handles unsupported profiles. code = format_proxy(profile, interaction, description) error_msg = ( f'Missing {profile} proxy and mmi: {interaction}\n' error_msg = (f'Missing {profile} proxy and mmi: {interaction}\n' f'Create a {profile.lower()}.py in mmi2grpc/:\n\n{code}\n' f'Then, instantiate the corresponding proxy in __init__.py\n' f'Finally, create a {profile.lower()}.proto in proto/pandora/' Loading
android/pandora/mmi2grpc/mmi2grpc/hfp.py 0 → 100644 +70 −0 Original line number Diff line number Diff line # Copyright 2022 Google LLC # # 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 # # https://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. """HFP proxy module.""" from mmi2grpc._helpers import assert_description from mmi2grpc._proxy import ProfileProxy from pandora.hfp_grpc import HFP from pandora.host_grpc import Host import sys import threading class HFPProxy(ProfileProxy): def __init__(self, channel): super().__init__() self.hfp = HFP(channel) self.host = Host(channel) @assert_description def TSC_delete_pairing_iut(self, pts_addr: bytes, **kwargs): """ Delete the pairing with the PTS using the Implementation Under Test (IUT), then click Ok. """ self.host.DeletePairing(address=pts_addr) return "OK" @assert_description def TSC_iut_search(self, **kwargs): """ Using the Implementation Under Test (IUT), perform a search for the PTS. If found, click OK. """ return "OK" @assert_description def TSC_iut_connect(self, pts_addr: bytes, **kwargs): """ Click Ok, then make a connection request to the PTS from the Implementation Under Test (IUT). """ self.connection = self.host.Connect(address=pts_addr).connection return "OK" @assert_description def TSC_iut_disable_slc(self, pts_addr: bytes, **kwargs): """ Click Ok, then disable the service level connection using the Implementation Under Test (IUT). """ self.hfp.DisableSlc(address=pts_addr) return "OK"
android/pandora/server/Android.bp +12 −9 Original line number Diff line number Diff line Loading @@ -46,17 +46,17 @@ java_library { "proto/pandora/*.proto", ], static_libs: [ "pandora-proto-java", "grpc-java-lite", "guava", "opencensus-java-api", "libprotobuf-java-lite", "javax_annotation-api_1.3.2", "libprotobuf-java-lite", "opencensus-java-api", "pandora-proto-java", ], proto: { include_dirs: [ "packages/modules/Bluetooth/android/pandora/server/proto", "external/protobuf/src", "packages/modules/Bluetooth/android/pandora/server/proto", ], plugin: "grpc-java-plugin", output_params: [ Loading @@ -78,8 +78,8 @@ java_library { proto: { type: "lite", include_dirs: [ "packages/modules/Bluetooth/android/pandora/server/proto", "external/protobuf/src", "packages/modules/Bluetooth/android/pandora/server/proto", ], }, } Loading @@ -98,13 +98,16 @@ genrule { " --python_out=$(genDir)" + " $(in)", srcs: [ "proto/pandora/a2dp.proto", "proto/pandora/hfp.proto", "proto/pandora/host.proto", "proto/pandora/a2dp.proto" ], out: [ "pandora/host_pb2.py", "pandora/host_grpc.py", "pandora/a2dp_pb2.py", "pandora/a2dp_grpc.py", "pandora/a2dp_pb2.py", "pandora/hfp_grpc.py", "pandora/hfp_pb2.py", "pandora/host_grpc.py", "pandora/host_pb2.py", ] }
android/pandora/server/configs/PtsBotTest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer"> <option name="dep-module" value="grpcio" /> <option name="dep-module" value="protobuf==3.20.1" /> <option name="dep-module" value="scipy" /> </target_preparer> <test class="com.android.tradefed.testtype.pandora.PtsBotTest" > Loading @@ -21,5 +22,6 @@ <option name="physical" value="false" /> <option name="profile" value="A2DP/SRC" /> <option name="profile" value="AVDTP/SRC" /> <option name="profile" value="HFP/AG/DIS" /> </test> </configuration>
android/pandora/server/proto/pandora/hfp.proto 0 → 100644 +23 −0 Original line number Diff line number Diff line syntax = "proto3"; option java_outer_classname = "HfpProto"; package pandora; import "pandora/host.proto"; import "google/protobuf/wrappers.proto"; // Service to trigger HFP (Hands Free Profile) procedures. service HFP { // Disable Service level connection rpc DisableSlc(DisableSlcRequest) returns (DisableSlcResponse); } // Request of the `DisableSlc` method. message DisableSlcRequest { // Local Bluetooth Device Address as array of 6 bytes. bytes address = 1; } // Response of the `DisableSlc` method. message DisableSlcResponse {}