blob: 9c84fac13dbf75406269472135ce3b58492705e8 [file] [log] [blame]
# Copyright 2016 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.
"""Station-based Bluetooth scan and pair test, using hciconfig and hcitool.
Make the host machine discoverable, scan for the host MAC address from the
DUT, and make the host non-discoverable.
"""
import unittest
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.device import component
from cros.factory.test.args import Arg
from cros.factory.test import factory
from cros.factory.utils import sync_utils
# HCI Commands. Need to fill the HCI device.
ENABLE_DEVICE_CMD = 'hciconfig %s up'
DISABLE_DEVICE_CMD = 'hciconfig %s down'
ENABLE_SCAN_CMD = 'hciconfig %s piscan'
DISABLE_SCAN_CMD = 'hciconfig %s noscan'
DEFAULT_RETRY_TIME = 3
class BluetoothScanTest(unittest.TestCase):
ARGS = [
Arg('max_retry_times', int,
'The maximum number attempts to retry scanning or pairing before '
'failure.',
default=DEFAULT_RETRY_TIME),
Arg('enable_pair', bool,
'Set to True to enable pairing test.',
optional=True, default=False),
Arg('pre_command', str,
'Command to be run before executing the test. For example, this '
'could be used to initialize Bluetooth module on the DUT. '
'Does not check output of the command.',
optional=True, default=None),
Arg('post_command', str,
'Command to be run after executing the test. For example, this '
'could be used to unload a Bluetooth module on the DUT. '
'Does not check output of the command.',
optional=True, default=None),
Arg('host_hci_device', str,
'The target hci device of the host station.',
default='hci0'),
Arg('dut_hci_device', str,
'The target hci device of the DUT.',
default='hci0'),
]
def setUp(self):
self.dut = device_utils.CreateDUTInterface()
self.host = device_utils.CreateStationInterface()
self.host_mac = None
def tearDown(self):
# Close host Bluetooth device.
self.host.Call(DISABLE_SCAN_CMD % self.args.host_hci_device)
self.host.Call(DISABLE_DEVICE_CMD % self.args.host_hci_device)
# Close DUT Bluetooth device.
self.dut.Call(DISABLE_DEVICE_CMD % self.args.dut_hci_device)
if self.args.post_command:
self.RunCommand(self.args.post_command, 'post-command')
def runTest(self):
if self.dut.link.IsLocal():
self.fail('This pytest can only be run at station-based style.')
# Setup host Bluetooth device.
self.host.CheckCall(ENABLE_DEVICE_CMD % self.args.host_hci_device)
self.host.CheckCall(ENABLE_SCAN_CMD % self.args.host_hci_device)
self.host_mac = self.GetHostMAC()
# Setup DUT Bluetooth device
if self.args.pre_command:
self.RunCommand(self.args.pre_command, 'pre-command')
self.dut.CheckCall(ENABLE_DEVICE_CMD % self.args.dut_hci_device)
# DUT scans the host station.
self.assertTrue(
sync_utils.Retry(self.args.max_retry_times, 0, None, self.ScanTask))
if self.args.enable_pair:
self.assertTrue(
sync_utils.Retry(self.args.max_retry_times, 0, None, self.PairTask))
def ScanTask(self):
"""Scans the Bluetooth devices and checks the host station is found."""
scanned_macs = self.ScanDevicesFromDUT()
factory.console.info('DUT scan results: %s', scanned_macs)
return self.host_mac in scanned_macs
def PairTask(self):
"""Connects with the Bluetooth devices of the host station."""
CONNECT_CMD = 'hcitool cc --role=m %s' % self.host_mac
DISCONNECT_CMD = 'hcitool dc %s' % self.host_mac
CHECK_CONNECTION_CMD = 'hcitool con'
self.dut.CheckCall(CONNECT_CMD)
output = self.dut.CheckOutput(CHECK_CONNECTION_CMD).lower()
factory.console.info('DUT connection: %s', output)
ret = self.host_mac in output
if ret:
self.dut.Call(DISCONNECT_CMD)
return ret
def RunCommand(self, cmd, cmd_name):
"""Logs and runs the command."""
factory.console.info('Running %s: %s', cmd_name, cmd)
try:
output = self.dut.CheckOutput(cmd)
except component.CalledProcessError as e:
factory.console.info('Exit code: %d', e.returncode)
else:
factory.console.info('Success. Output: %s', output)
def ScanDevicesFromDUT(self):
"""Scans for nearby BT devices from the DUT.
Returns:
A list of MAC addresses.
"""
# The output of the scan command:
# Scanning ...
# 01:02:03:04:05:06 Chromebook_0123
# 01:02:03:04:05:07 Chromebook_4567
SCAN_COMMAND = 'hcitool scan'
output = self.dut.CheckOutput(SCAN_COMMAND)
lines = output.splitlines()[1:] # Skip the first line "Scanning ...".
return [line.split()[0].lower() for line in lines]
def GetHostMAC(self):
"""Gets the MAC address of the host station."""
# Devices:
# hci0 01:02:03:04:05:06
host_mac = self.host.CheckOutput(
'hcitool dev | grep hci0').split()[1].lower()
factory.console.info('Host MAC: %s', host_mac)
return host_mac