1# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module provides the utilities for bluetooth audio using chameleon."""
6
7import logging
8import time
9
10from autotest_lib.client.bin import utils
11
12
13_PIN = '0000'
14_SEARCH_TIMEOUT = 30.0
15_PAIRING_TIMEOUT = 5.0
16_CONNECT_TIMEOUT = 15.0
17
18
19class ChameleonBluetoothAudioError(Exception):
20    """Error in this module."""
21    pass
22
23
24def connect_bluetooth_module_full_flow(bt_adapter, target_mac_address,
25                             timeout=_SEARCH_TIMEOUT):
26    """Controls Cros device to connect to bluetooth module on audio board.
27
28    @param bt_adapter: A BluetoothDevice object to control bluetooth adapter
29                       on Cros device.
30    @param target_mac_address: The MAC address of bluetooth module to be
31                               connected.
32    @param timeout: Timeout in seconds to search for bluetooth module.
33
34    @raises: ChameleonBluetoothAudioError if Cros device fails to connect to
35             bluetooth module on audio board.
36
37    """
38    # Resets bluetooth adapter on Cros device.
39    if not bt_adapter.reset_on():
40        raise ChameleonBluetoothAudioError(
41                'Failed to reset bluetooth adapter on Cros host.'
42                ' You should check if controller is available on Cros host'
43                ' using bluetoothctl.')
44
45    # Starts discovery mode of bluetooth adapter.
46    if not bt_adapter.start_discovery():
47        raise ChameleonBluetoothAudioError(
48                'Failed to start discovery on bluetooth adapter on Cros host')
49
50    def _find_device():
51        """Controls bluetooth adapter to search for bluetooth module.
52
53        @returns: True if there is a bluetooth device with MAC address
54                  matches target_mac_address. False otherwise.
55
56        """
57        return bt_adapter.has_device(target_mac_address)
58
59    # Searches for bluetooth module with given MAC address.
60    found_device = utils.wait_for_value(_find_device, True, timeout_sec=timeout)
61
62    if not found_device:
63        raise ChameleonBluetoothAudioError(
64                'Can not find bluetooth module with MAC address %s' %
65                target_mac_address)
66
67    pair_legacy_bluetooth_module(bt_adapter, target_mac_address)
68
69    # Disconnects from bluetooth module to clean up the state.
70    if not bt_adapter.disconnect_device(target_mac_address):
71        raise ChameleonBluetoothAudioError(
72                'Failed to let Cros device disconnect from bluetooth module %s' %
73                target_mac_address)
74
75    # Connects to bluetooth module.
76    connect_bluetooth_module(bt_adapter, target_mac_address)
77
78    logging.info('Bluetooth module at %s is connected', target_mac_address)
79
80
81def connect_bluetooth_module(bt_adapter, target_mac_address,
82                             timeout=_CONNECT_TIMEOUT):
83    """Controls Cros device to connect to bluetooth module on audio board.
84
85    @param bt_adapter: A BluetoothDevice object to control bluetooth adapter
86                       on Cros device.
87    @param target_mac_address: The MAC address of bluetooth module to be
88                               connected.
89    @param timeout: Timeout in seconds to connect bluetooth module.
90
91    @raises: ChameleonBluetoothAudioError if Cros device fails to connect to
92             bluetooth module on audio board.
93
94    """
95    def _connect_device():
96        success = bt_adapter.connect_device(target_mac_address)
97        if not success:
98            logging.debug('Can not connect device, retry in 1 second.')
99            time.sleep(1)
100            return False
101        logging.debug('Connection established.')
102        return True
103
104    # Connects bluetooth module with given MAC address.
105    connected = utils.wait_for_value(_connect_device, True, timeout_sec=timeout)
106    if not connected:
107        raise ChameleonBluetoothAudioError(
108                'Failed to let Cros device connect to bluetooth module %s' %
109                target_mac_address)
110
111
112def pair_legacy_bluetooth_module(bt_adapter, target_mac_address, pin=_PIN,
113                                 pairing_timeout=_PAIRING_TIMEOUT, retries=3):
114    """Pairs Cros device bluetooth adapter with legacy bluetooth module.
115
116    @param bt_adapter: A BluetoothDevice object to control bluetooth adapter
117                       on Cros device.
118    @param target_mac_address: The MAC address of bluetooth module to be
119                               paired.
120    @param pin: The pin for legacy pairing.
121    @param timeout: Timeout in seconds to pair bluetooth module in a trial.
122    @param retries: Number of retries if pairing fails.
123
124    @raises: ChameleonBluetoothAudioError if Cros device fails to pair
125             bluetooth module on audio board after all the retries.
126
127    """
128    # Pairs the bluetooth adapter with bluetooth module.
129    for trial in xrange(retries):
130        if bt_adapter.pair_legacy_device(
131            target_mac_address, pin, pairing_timeout):
132                logging.debug('Pairing to %s succeeded', target_mac_address)
133                return
134        elif trial == retries - 1:
135            raise ChameleonBluetoothAudioError(
136                    'Failed to pair Cros device and bluetooth module %s' %
137                    target_mac_address)
138
139        logging.debug('Retry for pairing...')
140