blob: f4f51c1cab98b3f01222833fb3208c4973163eaf [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2021 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.
"""
PTS GAP/SEC/SEM Automation
Optional custom parameter "collect_detailed_pass_logs"
Used to collect link keys and extra logs on pass results
to be used for certification purposes.
"""
from acts import signals
from acts.base_test import BaseTestClass
from acts.test_decorators import test_tracker_info
from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
from acts_contrib.test_utils.fuchsia.bt_test_utils import get_link_keys
from acts_contrib.test_utils.fuchsia.bt_test_utils import unbond_all_known_devices
from contextlib import suppress
import inspect
class GapSecSemTest(BaseTestClass):
gatt_connect_err_message = "Gatt connection failed with: {}"
gatt_disconnect_err_message = "Gatt disconnection failed with: {}"
ble_advertise_interval = 50
scan_timeout_seconds = 60
def setup_class(self):
super().setup_class()
self.pri_dut = self.fuchsia_devices[0]
# TODO: fxb/57968 Provide Facade for setting secure connections only mode,
# for the interim set this manually in the build.
self.sec_dut = self.fuchsia_devices[1]
for fd in self.fuchsia_devices:
fd.sl4f.bts_lib.initBluetoothSys()
# Optional user param for collecting enough information for
# certification on pass results.
self.collect_detailed_pass_logs = self.user_params.get(
"collect_detailed_pass_logs", False)
def on_fail(self, test_name, begin_time):
for fd in self.fuchsia_devices:
fd.take_bug_report(test_name, begin_time)
def teardown_test(self):
# Stop scanning and advertising on all devices at the end of a test.
with suppress(Exception):
for fd in self.fuchsia_devices:
fd.sl4f.ble_lib.bleStopBleAdvertising()
fd.bleStopBleScan()
for fd in self.fuchsia_devices:
unbond_all_known_devices(fd, self.log)
def teardown_class(self):
for fd in self.fuchsia_devices:
fd.sl4f.bts_lib.requestDiscovery(False)
def on_pass(self, test_name, begin_time):
if self.collect_detailed_pass_logs == True:
for fd in self.fuchsia_devices:
fd.take_bt_snoop_log(test_name)
fd.take_bug_report(test_name, begin_time)
def _orchestrate_gatt_connection(self, central, peripheral):
""" Orchestrate a GATT connetion from the input Central
Fuchsia device to the Peripheral Fuchsia device.
Args:
central: The central Fuchsia device
peripheral: The peripheral Fuchsia device
peripheral: The peripheral role Fuchsia device
Returns:
Dictionary of device info if connection successful.
"""
adv_name = generate_id_by_size(10)
adv_data = {
"name": adv_name,
"appearance": None,
"service_data": None,
"tx_power_level": None,
"service_uuids": None,
"manufacturer_data": None,
"uris": None,
}
scan_response = None
connectable = True
peripheral.sl4f.ble_lib.bleStartBleAdvertising(
adv_data, scan_response, self.ble_advertise_interval, connectable)
scan_filter = {"name_substring": adv_name}
central.sl4f.gattc_lib.bleStartBleScan(scan_filter)
device = le_scan_for_device_by_name(central,
self.log,
adv_name,
self.scan_timeout_seconds,
partial_match=False,
self_manage_scan=False)
if device is None:
raise signals.TestFailure("Scanner unable to find advertisement.")
connect_result = central.sl4f.gattc_lib.bleConnectToPeripheral(
device["id"])
if connect_result.get("error") is not None:
raise signals.TestFailure(
self.gatt_connect_err_message.format(
connect_result.get("error")))
self.log.info("Connection Successful...")
return device
def _orchestrate_gap_sec_sem_37_to_44_test(self, test_name, central,
peripheral,
is_central_pairing_initiator,
security_level):
""" Performs GAP/SEC/SEM/BV-37 to 44 tests.
These set of tests deal with varying modes and directions of
service level connections with LE secure Connections.
Args:
test_name: The name of the test for logging purposes
central: The central role Fuchsia device
peripheral: The peripheral role Fuchsia device
is_central_pairing_initiator: True if Central should initiate
the pairing. False if Peripheral should initiate.
security_level: 1 for Encrypted, 2 for Authenticated
Orchestration Steps:
1. Perform GATT connection from Central to Peripheral
2. Pair with specified security_level in direction specified
by is_central_pairing_initiator.
3. Exchange pairing pins
4. Collect link keys if applicable
5. Disconnect device
6. Forget pairing.
"""
input_capabilities = "NONE"
output_capabilities = "NONE"
central.sl4f.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
peripheral.sl4f.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
device = self._orchestrate_gatt_connection(central, peripheral)
# TODO: fxb/71289 Change once all peer IDs are ints and not strings
identifier = int("0x{}".format(device["id"]), 0)
bondable = True
transport = 2 #LE
if is_central_pairing_initiator:
pair_result = central.sl4f.bts_lib.pair(identifier, security_level,
bondable, transport)
if not is_central_pairing_initiator:
device_list = peripheral.sl4f.bts_lib.getKnownRemoteDevices(
)['result']
print(device_list)
for id_dict in device_list:
d = device_list[id_dict]
name = None
if d['connected'] is True:
did = d['id']
pair_result = peripheral.sl4f.bts_lib.pair(did, security_level,
bondable, transport)
pins_transferred = False
pairing_pin = central.sl4f.bts_lib.getPairingPin()['result']
if pairing_pin != "0" and pairing_pin is not None:
peripheral.sl4f.bts_lib.inputPairingPin(pairing_pin)
pins_transferred = True
if not pins_transferred:
pairing_pin = peripheral.sl4f.bts_lib.getPairingPin()['result']
if pairing_pin != "0":
central.sl4f.bts_lib.inputPairingPin(pairing_pin)
if self.collect_detailed_pass_logs == True:
save_path = f"{central.log_path}/{test_name}_stash_secure.store"
self.log.info(
f"Known Link Keys: {get_link_keys(central, save_path)}")
save_path = f"{peripheral.log_path}/{test_name}_stash_secure.store"
self.log.info(
f"Known Link Keys: {get_link_keys(peripheral, save_path)}")
disconnect_result = central.sl4f.gattc_lib.bleDisconnectPeripheral(
device["id"])
if disconnect_result.get("error") is not None:
raise signals.TestFailure(
self.gatt_disconnect_err_message.format(
disconnect_result.get("error")))
self.log.info("Disconnection Successful...")
central.sl4f.bts_lib.forgetDevice(identifier)
raise signals.TestPass("Success")
def test_gap_sec_sem_bv_37_c(self):
central = self.pri_dut
peripheral = self.sec_dut
is_central_pairing_initiator = True
security_level = 1 # Encrypted
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_38_c(self):
central = self.pri_dut
peripheral = self.sec_dut
is_central_pairing_initiator = True
security_level = 2 # Authenticated
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_39_c(self):
central = self.pri_dut
peripheral = self.sec_dut
is_central_pairing_initiator = False
security_level = 1 # Encrypted
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_40_c(self):
central = self.pri_dut
peripheral = self.sec_dut
is_central_pairing_initiator = False
security_level = 2 # Authenticated
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_41_c(self):
central = self.sec_dut
peripheral = self.pri_dut
is_central_pairing_initiator = True
security_level = 1 # Encrypted
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_42_c(self):
central = self.sec_dut
peripheral = self.pri_dut
is_central_pairing_initiator = True
security_level = 2 # Authenticated
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_43_c(self):
central = self.sec_dut
peripheral = self.pri_dut
is_central_pairing_initiator = False
security_level = 1 # Encrypted
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)
def test_gap_sec_sem_bv_44_c(self):
central = self.sec_dut
peripheral = self.pri_dut
is_central_pairing_initiator = False
security_level = 2 # Authenticated
test_name = inspect.currentframe().f_code.co_name
self._orchestrate_gap_sec_sem_37_to_44_test(
test_name, central, peripheral, is_central_pairing_initiator,
security_level)