blob: ec42c76d3fcdcd484a47cfc89153b00df03f390f [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from autotest_lib.client.common_lib import error
from autotest_lib.server.cros.bluetooth import bluetooth_test
class bluetooth_SDP_ServiceAttributeRequest(bluetooth_test.BluetoothTest):
"""
Verify the correct behaviour of the device when searching for attributes of
services.
"""
version = 1
MAX_REC_CNT = 3
MAX_ATTR_BYTE_CNT = 300
SDP_SERVER_CLASS_ID = 0x1000
SERVICE_RECORD_HANDLE_ATTR_ID = 0x0000
GAP_CLASS_ID = 0x1800
BROWSE_GROUP_LIST_ATTR_ID = 0x0005
PUBLIC_BROWSE_ROOT = 0x1002
BLUEZ_URL = 'http://www.bluez.org/'
DOCUMENTATION_URL_ATTR_ID = 0x000A
CLIENT_EXECUTABLE_URL_ATTR_ID = 0x000B
ICON_URL_ATTR_ID = 0x000C
PROTOCOL_DESCRIPTOR_LIST_ATTR_ID = 0x0004
L2CAP_UUID = 0x0100
ATT_UUID = 0x0007
PNP_INFORMATION_CLASS_ID = 0x1200
MIN_ATTR_BYTE_CNT = 7
VERSION_NUMBER_LIST_ATTR_ID = 0x0200
SERVICE_DATABASE_STATE_ATTR_ID = 0x0201
AVRCP_TG_CLASS_ID = 0x110c
PROFILE_DESCRIPTOR_LIST_ATTR_ID = 0x0009
ADDITIONAL_PROTOCOLLIST_ATTR_ID = 0x000D
def get_single_handle(self, class_id):
"""Send a Service Search Request to get a handle for specific class ID.
@return -1 if request failed, record handle as int otherwise
"""
res = self.tester.service_search_request([class_id], self.MAX_REC_CNT)
if not (isinstance(res, list) and len(res) > 0):
return -1
return res[0]
def test_record_handle_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-01-C from SDP Specification.
@return True if test passes, False if test fails
"""
# Send Service Search Request to find out record handle for
# SDP Server service.
record_handle = self.get_single_handle(self.SDP_SERVER_CLASS_ID)
if record_handle == -1:
return False
# Send Service Attribute Request for Service Record Handle Attribute.
res = self.tester.service_attribute_request(
record_handle,
self.MAX_ATTR_BYTE_CNT,
[self.SERVICE_RECORD_HANDLE_ATTR_ID])
# Ensure that returned attribute is correct.
return res == [self.SERVICE_RECORD_HANDLE_ATTR_ID, record_handle]
def get_attribute(self, class_id, attr_id):
"""Get a single attribute of a single service
@param class_id: Class ID of service to check.
@param attr_id: ID of attribute to check.
@return attribute value if attribute exists, None otherwise
"""
record_handle = self.get_single_handle(class_id)
if record_handle == -1:
return False
res = self.tester.service_attribute_request(
record_handle, self.MAX_ATTR_BYTE_CNT, [attr_id])
if isinstance(res, list) and len(res) == 2 and res[0] == attr_id:
return res[1]
return None
def test_attribute(self, class_id, attr_id, attr_value):
"""Test a single attribute of a single service
@param class_id: Class ID of service to check.
@param attr_id: ID of attribute to check.
@param attr_value: expected value of the attribute
@return True if value of attribute equals to attr_value, False otherwise
"""
record_handle = self.get_single_handle(class_id)
if record_handle == -1:
return False
res = self.tester.service_attribute_request(
record_handle, self.MAX_ATTR_BYTE_CNT, [attr_id])
return res == [attr_id, attr_value]
def test_browse_group_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-08-C from SDP Specification.
@return True if test passes, False if test fails
"""
return self.test_attribute(self.GAP_CLASS_ID,
self.BROWSE_GROUP_LIST_ATTR_ID,
[self.PUBLIC_BROWSE_ROOT])
def test_icon_url_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-11-C from SDP Specification.
@return True if test passes, False if test fails
"""
return self.test_attribute(self.GAP_CLASS_ID,
self.ICON_URL_ATTR_ID,
self.BLUEZ_URL)
def test_documentation_url_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-18-C from SDP Specification.
@return True if test passes, False if test fails
"""
return self.test_attribute(self.GAP_CLASS_ID,
self.DOCUMENTATION_URL_ATTR_ID,
self.BLUEZ_URL)
def test_client_executable_url_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-19-C from SDP Specification.
@return True if test passes, False if test fails
"""
return self.test_attribute(self.GAP_CLASS_ID,
self.CLIENT_EXECUTABLE_URL_ATTR_ID,
self.BLUEZ_URL)
def test_protocol_descriptor_list_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-05-C from SDP Specification.
@return True if test passes, False if test fails
"""
return self.test_attribute(self.GAP_CLASS_ID,
self.PROTOCOL_DESCRIPTOR_LIST_ATTR_ID,
[[self.L2CAP_UUID, 31],
[self.ATT_UUID, 1, 8]])
def test_continuation_state(self):
"""Implementation of test TP/SERVER/SA/BV-03-C from SDP Specification.
@return True if test passes, False if test fails
"""
record_handle = self.get_single_handle(self.PNP_INFORMATION_CLASS_ID)
if record_handle == -1:
return False
res = self.tester.service_attribute_request(
record_handle, self.MIN_ATTR_BYTE_CNT, [[0, 0xFFFF]])
return isinstance(res, list) and res != []
def test_version_list_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-15-C from SDP Specification.
@return True if test passes, False if test fails
"""
version_list = self.get_attribute(self.SDP_SERVER_CLASS_ID,
self.VERSION_NUMBER_LIST_ATTR_ID)
return isinstance(version_list, list) and version_list != []
def test_service_database_state_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-16-C from SDP Specification.
@return True if test passes, False if test fails
"""
state = self.get_attribute(self.SDP_SERVER_CLASS_ID,
self.SERVICE_DATABASE_STATE_ATTR_ID)
return isinstance(state, int)
def test_profile_descriptor_list_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-17-C from SDP Specification.
@return True if test passes, False if test fails
"""
profile_list = self.get_attribute(self.PNP_INFORMATION_CLASS_ID,
self.PROFILE_DESCRIPTOR_LIST_ATTR_ID)
return (isinstance(profile_list, list) and len(profile_list) == 1 and
isinstance(profile_list[0], list) and
len(profile_list[0]) == 2 and
profile_list[0][0] == self.PNP_INFORMATION_CLASS_ID)
def test_additional_protocol_descriptor_list_attribute(self):
"""Implementation of test TP/SERVER/SA/BV-18-C from SDP Specification.
@return True if test passes, False if test fails
"""
protocol_list = self.get_attribute(self.AVRCP_TG_CLASS_ID,
self.ADDITIONAL_PROTOCOLLIST_ATTR_ID)
return isinstance(protocol_list, list) and protocol_list != []
def correct_request(self):
"""Run basic tests for Service Attribute Request.
@return True if all tests finishes correctly, False otherwise
"""
# Connect to the DUT via L2CAP using SDP socket.
self.tester.connect(self.adapter['Address'])
return (self.test_record_handle_attribute() and
self.test_browse_group_attribute() and
self.test_icon_url_attribute() and
self.test_documentation_url_attribute() and
self.test_client_executable_url_attribute() and
self.test_protocol_descriptor_list_attribute() and
self.test_continuation_state() and
self.test_version_list_attribute() and
self.test_service_database_state_attribute() and
self.test_profile_descriptor_list_attribute() and
self.test_additional_protocol_descriptor_list_attribute())
def run_once(self):
# Reset the adapter to the powered on, discoverable state.
if not (self.device.reset_on() and
self.device.set_discoverable(True)):
raise error.TestFail('DUT could not be reset to initial state')
self.adapter = self.device.get_adapter_properties()
# Setup the tester as a generic computer.
if not self.tester.setup('computer'):
raise error.TestFail('Tester could not be initialized')
# Since radio is involved, this test is not 100% reliable; instead we
# repeat a few times until it succeeds.
for failed_attempts in range(0, 5):
if self.correct_request():
break
else:
raise error.TestFail('Expected device was not found')
# Record how many attempts this took, hopefully we'll one day figure out
# a way to reduce this to zero and then the loop above can go away.
self.write_perf_keyval({'failed_attempts': failed_attempts })