blob: 856e45d99a9f2e1aa1dcd6f3830209966a2cdd7c [file] [log] [blame]
# 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.
"""A2DP proxy module."""
import time
from typing import Optional
from grpc import RpcError
from mmi2grpc._audio import AudioSignal
from mmi2grpc._helpers import assert_description
from mmi2grpc._proxy import ProfileProxy
from pandora.a2dp_grpc import A2DP
from pandora.a2dp_pb2 import Sink, Source, PlaybackAudioRequest
from pandora.host_grpc import Host
from pandora.host_pb2 import Connection
AUDIO_SIGNAL_AMPLITUDE = 0.8
AUDIO_SIGNAL_SAMPLING_RATE = 44100
class A2DPProxy(ProfileProxy):
"""A2DP proxy.
Implements A2DP and AVDTP PTS MMIs.
"""
connection: Optional[Connection] = None
sink: Optional[Sink] = None
source: Optional[Source] = None
def __init__(self, channel):
super().__init__()
self.host = Host(channel)
self.a2dp = A2DP(channel)
def convert_frame(data):
return PlaybackAudioRequest(data=data, source=self.source)
self.audio = AudioSignal(
lambda frames: self.a2dp.PlaybackAudio(map(convert_frame, frames)),
AUDIO_SIGNAL_AMPLITUDE,
AUDIO_SIGNAL_SAMPLING_RATE
)
@assert_description
def TSC_AVDTP_mmi_iut_accept_connect(
self, test: str, pts_addr: bytes, **kwargs):
"""
If necessary, take action to accept the AVDTP Signaling Channel
Connection initiated by the tester.
Description: Make sure the IUT
(Implementation Under Test) is in a state to accept incoming Bluetooth
connections. Some devices may need to be on a specific screen, like a
Bluetooth settings screen, in order to pair with PTS. If the IUT is
still having problems pairing with PTS, try running a test case where
the IUT connects to PTS to establish pairing.
"""
if "SRC" in test:
self.connection = self.host.WaitConnection(
address=pts_addr).connection
try:
if "INT" in test:
self.source = self.a2dp.OpenSource(
connection=self.connection).source
else:
self.source = self.a2dp.WaitSource(
connection=self.connection).source
except RpcError:
pass
else:
self.connection = self.host.WaitConnection(
address=pts_addr).connection
try:
self.sink = self.a2dp.WaitSink(
connection=self.connection).sink
except RpcError:
pass
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_discover(self, **kwargs):
"""
Send a discover command to PTS.
Action: If the IUT (Implementation
Under Test) is already connected to PTS, attempting to send or receive
streaming media should trigger this action. If the IUT is not connected
to PTS, attempting to connect may trigger this action.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_start(self, test: str, **kwargs):
"""
Send a start command to PTS.
Action: If the IUT (Implementation Under
Test) is already connected to PTS, attempting to send or receive
streaming media should trigger this action. If the IUT is not connected
to PTS, attempting to connect may trigger this action.
"""
if "SRC" in test:
self.a2dp.Start(source=self.source)
else:
self.a2dp.Start(sink=self.sink)
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_suspend(self, test: str, **kwargs):
"""
Suspend the streaming channel.
"""
if "SRC" in test:
self.a2dp.Suspend(source=self.source)
else:
assert False
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_close_stream(self, test: str, **kwargs):
"""
Close the streaming channel.
Action: Disconnect the streaming channel,
or close the Bluetooth connection to the PTS.
"""
if "SRC" in test:
self.a2dp.Close(source=self.source)
self.source = None
else:
self.a2dp.Close(sink=self.sink)
self.sink = None
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_out_of_range(
self, pts_addr: bytes, **kwargs):
"""
Move the IUT out of range to create a link loss scenario.
Action: This
can be also be done by placing the IUT or PTS in an RF shielded box.
"""
if self.connection is None:
self.connection = self.host.GetConnection(
address=pts_addr).connection
self.host.Disconnect(connection=self.connection)
self.connection = None
self.sink = None
self.source = None
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_begin_streaming(self, test: str, **kwargs):
"""
Begin streaming media ...
Note: If the IUT has suspended the stream
please restart the stream to begin streaming media.
"""
if test == "AVDTP/SRC/ACP/SIG/SMG/BI-29-C":
time.sleep(2) # TODO: Remove, AVRCP SegFault
if test in ("A2DP/SRC/CC/BV-09-I",
"A2DP/SRC/SET/BV-04-I",
"AVDTP/SRC/ACP/SIG/SMG/BV-18-C",
"AVDTP/SRC/ACP/SIG/SMG/BV-20-C",
"AVDTP/SRC/ACP/SIG/SMG/BV-22-C"):
time.sleep(1) # TODO: Remove, AVRCP SegFault
if test == "A2DP/SRC/SUS/BV-01-I":
# Stream is not suspended when we receive the interaction
time.sleep(1)
self.a2dp.Start(source=self.source)
self.audio.start()
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_media(self, **kwargs):
"""
Take action if necessary to start streaming media to the tester.
"""
self.audio.start()
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_stream_media(self, **kwargs):
"""
Stream media to PTS. If the IUT is a SNK, wait for PTS to start
streaming media.
Action: If the IUT (Implementation Under Test) is
already connected to PTS, attempting to send or receive streaming media
should trigger this action. If the IUT is not connected to PTS,
attempting to connect may trigger this action.
"""
self.audio.start()
return "OK"
@assert_description
def TSC_AVDTP_mmi_user_verify_media_playback(self, **kwargs):
"""
Is the test system properly playing back the media being sent by the
IUT?
"""
result = self.audio.verify()
assert result
return "Yes" if result else "No"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_get_capabilities(self, **kwargs):
"""
Send a get capabilities command to PTS.
Action: If the IUT
(Implementation Under Test) is already connected to PTS, attempting to
send or receive streaming media should trigger this action. If the IUT
is not connected to PTS, attempting to connect may trigger this action.
"""
# This will be done as part as the a2dp.OpenSource or a2dp.WaitSource
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_discover(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Discover operation
initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_set_configuration(self, **kwargs):
"""
Send a set configuration command to PTS.
Action: If the IUT
(Implementation Under Test) is already connected to PTS, attempting to
send or receive streaming media should trigger this action. If the IUT
is not connected to PTS, attempting to connect may trigger this action.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_close_stream(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Close operation initiated
by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_abort(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Abort operation initiated
by the tester..
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_get_all_capabilities(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Get All Capabilities
operation initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_get_capabilities(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Get Capabilities operation
initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_set_configuration(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Set Configuration
operation initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_get_configuration(self, **kwargs):
"""
Take action to accept the AVDTP Get Configuration command from the
tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_open_stream(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Open operation initiated
by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_start(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Start operation initiated
by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_suspend(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Suspend operation
initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_reconfigure(self, **kwargs):
"""
If necessary, take action to accept the AVDTP Reconfigure operation
initiated by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_media_transports(self, **kwargs):
"""
Take action to accept transport channels for the recently configured
media stream.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_confirm_streaming(self, **kwargs):
"""
Is the IUT (Implementation Under Test) receiving streaming media from
PTS?
Action: Press 'Yes' if the IUT is receiving streaming data from
the PTS (in some cases the sound may not be clear, this is normal).
"""
# TODO: verify
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_open_stream(self, **kwargs):
"""
Open a streaming media channel.
Action: If the IUT (Implementation
Under Test) is already connected to PTS, attempting to send or receive
streaming media should trigger this action. If the IUT is not connected
to PTS, attempting to connect may trigger this action.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_accept_reconnect(self, pts_addr: bytes, **kwargs):
"""
Press OK when the IUT (Implementation Under Test) is ready to allow the
PTS to reconnect the AVDTP signaling channel.
Action: Press OK when the
IUT is ready to accept Bluetooth connections again.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_iut_initiate_get_all_capabilities(self, **kwargs):
"""
Send a GET ALL CAPABILITIES command to PTS.
Action: If the IUT
(Implementation Under Test) is already connected to PTS, attempting to
send or receive streaming media should trigger this action. If the IUT
is not connected to PTS, attempting to connect may trigger this action.
"""
return "OK"
@assert_description
def TSC_AVDTP_mmi_tester_verifying_suspend(self, **kwargs):
"""
Please wait while the tester verifies the IUT does not send media during
suspend ...
"""
return "Yes"
@assert_description
def TSC_A2DP_mmi_user_confirm_optional_data_attribute(self, **kwargs):
"""
Tester found the optional SDP attribute named 'Supported Features'.
Press 'Yes' if the data displayed below is correct.
Value: 0x0001
"""
# TODO: Extract and verify attribute name and value from description
return "OK"
@assert_description
def TSC_A2DP_mmi_user_confirm_optional_string_attribute(self, **kwargs):
"""
Tester found the optional SDP attribute named 'Service Name'. Press
'Yes' if the string displayed below is correct.
Value: Advanced Audio
Source
"""
# TODO: Extract and verify attribute name and value from description
return "OK"
@assert_description
def TSC_A2DP_mmi_user_confirm_no_optional_attribute_support(self, **kwargs):
"""
Tester could not find the optional SDP attribute named 'Provider Name'.
Is this correct?
"""
# TODO: Extract and verify attribute name from description
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_accept_delayreport(self, **kwargs):
"""
Take action if necessary to accept the Delay Reportl command from the
tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_initiate_media_transport_connect(self, **kwargs):
"""
Take action to initiate an AVDTP media transport.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_user_confirm_SIG_SMG_BV_28_C(self, **kwargs):
"""
Were all the service capabilities reported to the upper tester valid?
"""
# TODO: verify
return "Yes"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_invalid_command(self, **kwargs):
"""
Take action to reject the invalid command sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_open(self, **kwargs):
"""
Take action to reject the invalid OPEN command sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_start(self, **kwargs):
"""
Take action to reject the invalid START command sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_suspend(self, **kwargs):
"""
Take action to reject the invalid SUSPEND command sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_reconfigure(self, **kwargs):
"""
Take action to reject the invalid or incompatible RECONFIGURE command
sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_get_all_capabilities(self, **kwargs):
"""
Take action to reject the invalid GET ALL CAPABILITIES command with the
error code BAD_LENGTH.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_get_capabilities(self, **kwargs):
"""
Take action to reject the invalid GET CAPABILITIES command with the
error code BAD_LENGTH.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_set_configuration(self, **kwargs):
"""
Take action to reject the SET CONFIGURATION sent by the tester. The IUT
is expected to respond with SEP_IN_USE because the SEP requested was
previously configured.
"""
return "OK"
def TSC_AVDTPEX_mmi_iut_reject_get_configuration(self, **kwargs):
"""
Take action to reject the GET CONFIGURATION sent by the tester. The IUT
is expected to respond with BAD_ACP_SEID because the SEID requested was
not previously configured.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_iut_reject_close(self, **kwargs):
"""
Take action to reject the invalid CLOSE command sent by the tester.
"""
return "OK"
@assert_description
def TSC_AVDTPEX_mmi_user_confirm_SIG_SMG_BV_18_C(self, **kwargs):
"""
Did the IUT receive media with the following information?
- V = RTP_Ver
- P = 0 (no padding bits)
- X = 0 (no extension)
- CC = 0 (no
contributing source)
- M = 0
"""
# TODO: verify
return "OK"