1#!/usr/bin/env python3
2#
3#   Copyright 2021 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import logging
18import os
19import traceback
20from functools import wraps
21
22from blueberry.tests.gd.cert.closable import safeClose
23from blueberry.tests.gd.cert.context import get_current_context
24from blueberry.tests.gd_sl4a.lib.ble_lib import BleLib
25from blueberry.tests.gd_sl4a.lib.ble_lib import disable_bluetooth
26from blueberry.tests.gd_sl4a.lib.ble_lib import enable_bluetooth
27from blueberry.tests.sl4a_sl4a.lib.le_advertiser import LeAdvertiser
28from blueberry.tests.sl4a_sl4a.lib.le_scanner import LeScanner
29from blueberry.tests.sl4a_sl4a.lib.l2cap import L2cap
30from blueberry.tests.sl4a_sl4a.lib.security import Security
31from blueberry.utils.mobly_sl4a_utils import setup_sl4a
32from blueberry.utils.mobly_sl4a_utils import teardown_sl4a
33from grpc import RpcError
34from mobly import signals
35from mobly.base_test import BaseTestClass
36from mobly.controllers import android_device
37from mobly.controllers.android_device import MOBLY_CONTROLLER_CONFIG_NAME as ANDROID_DEVICE_CONFIG_NAME
38from mobly.controllers.android_device_lib.adb import AdbError
39
40
41class Sl4aSl4aBaseTestClass(BaseTestClass):
42
43    # DUT
44    dut_advertiser_ = None
45    dut_scanner_ = None
46    dut_security_ = None
47    dut_l2cap_ = None
48
49    # CERT
50    cert_advertiser_ = None
51    cert_scanner_ = None
52    cert_security_ = None
53    cert_l2cap_ = None
54
55    SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
56
57    def setup_class(self):
58        self.log_path_base = get_current_context().get_full_output_path()
59        self.verbose_mode = bool(self.user_params.get('verbose_mode', False))
60
61        # Parse and construct Android device objects
62        self.android_devices = self.register_controller(android_device, required=True)
63
64        # Setup SL4A for dut, overriding default mobly port settings
65        self.dut = self.android_devices[0]
66        server_port = int(self.controller_configs[ANDROID_DEVICE_CONFIG_NAME][0]['server_port'])
67        forwarded_port = int(self.controller_configs[ANDROID_DEVICE_CONFIG_NAME][0]['forwarded_port'])
68        setup_sl4a(self.dut, server_port, forwarded_port)
69
70        # Setup SL4A for cert, overriding default mobly port settings
71        self.cert = self.android_devices[1]
72        server_port = int(self.controller_configs[ANDROID_DEVICE_CONFIG_NAME][1]['server_port'])
73        forwarded_port = int(self.controller_configs[ANDROID_DEVICE_CONFIG_NAME][1]['forwarded_port'])
74        setup_sl4a(self.cert, server_port, forwarded_port)
75
76        # Enable full btsnoop log
77        self.dut.adb.root()
78        self.dut.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
79        getprop_result = self.dut.adb.getprop("persist.bluetooth.btsnooplogmode")
80        if getprop_result is None or ("full" not in getprop_result.lower()):
81            self.dut.log.warning(
82                "Failed to enable Bluetooth HCI Snoop Logging on DUT, mode is {}".format(getprop_result))
83        self.cert.adb.root()
84        self.cert.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
85        getprop_result = self.cert.adb.getprop("persist.bluetooth.btsnooplogmode")
86        if getprop_result is None or ("full" not in getprop_result.lower()):
87            self.cert.log.warning(
88                "Failed to enable Bluetooth HCI Snoop Logging on CERT, mode is {}".format(getprop_result))
89
90        self.ble = BleLib(dut=self.dut)
91
92    def teardown_class(self):
93        teardown_sl4a(self.cert)
94        teardown_sl4a(self.dut)
95        super().teardown_class()
96
97    def setup_device_for_test(self, device):
98        device.ed.clear_all_events()
99        device.sl4a.setScreenTimeout(500)
100        device.sl4a.wakeUpNow()
101
102        # Always start tests with Bluetooth enabled and BLE disabled.
103        device.sl4a.bluetoothDisableBLE()
104        disable_bluetooth(device.sl4a, device.ed)
105        # Enable full verbose logging for Bluetooth
106        device.adb.shell("setprop log.tag.bluetooth VERBOSE")
107        # Then enable Bluetooth
108        enable_bluetooth(device.sl4a, device.ed)
109        device.sl4a.bluetoothDisableBLE()
110
111    def setup_test(self):
112        self.setup_device_for_test(self.dut)
113        self.setup_device_for_test(self.cert)
114        self.dut_advertiser_ = LeAdvertiser(self.dut)
115        self.dut_scanner_ = LeScanner(self.dut)
116        self.dut_security_ = Security(self.dut)
117        self.dut_l2cap_ = L2cap(self.dut)
118        self.cert_advertiser_ = LeAdvertiser(self.cert)
119        self.cert_scanner_ = LeScanner(self.cert)
120        self.cert_security_ = Security(self.cert)
121        self.cert_l2cap_ = L2cap(self.cert)
122        return True
123
124    def teardown_test(self):
125        # Go ahead and remove everything before turning off the stack
126        safeClose(self.dut_advertiser_)
127        safeClose(self.dut_scanner_)
128        safeClose(self.dut_security_)
129        safeClose(self.dut_l2cap_)
130        safeClose(self.cert_advertiser_)
131        safeClose(self.cert_scanner_)
132        safeClose(self.cert_security_)
133        safeClose(self.cert_l2cap_)
134        self.dut_advertiser_ = None
135        self.dut_scanner_ = None
136        self.dut_security_ = None
137        self.cert_advertiser_ = None
138        self.cert_l2cap_ = None
139        self.cert_scanner_ = None
140        self.cert_security_ = None
141
142        # Make sure BLE is disabled and Bluetooth is disabled after test
143        self.dut.sl4a.bluetoothDisableBLE()
144        disable_bluetooth(self.dut.sl4a, self.dut.ed)
145        self.cert.sl4a.bluetoothDisableBLE()
146        disable_bluetooth(self.cert.sl4a, self.cert.ed)
147
148        current_test_dir = get_current_context().get_full_output_path()
149
150        # Pull DUT logs
151        self.pull_logs(current_test_dir, self.dut)
152
153        # Pull CERT logs
154        self.pull_logs(current_test_dir, self.cert)
155        return True
156
157    def pull_logs(self, base_dir, device):
158        try:
159            device.adb.pull([
160                "/data/misc/bluetooth/logs/btsnoop_hci.log",
161                os.path.join(base_dir, "DUT_%s_btsnoop_hci.log" % device.serial)
162            ])
163            device.adb.pull([
164                "/data/misc/bluedroid/bt_config.conf",
165                os.path.join(base_dir, "DUT_%s_bt_config.conf" % device.serial)
166            ])
167        except AdbError as error:
168            logging.warning("Failed to pull logs from DUT: " + str(error))
169
170    def __getattribute__(self, name):
171        attr = super().__getattribute__(name)
172        if not callable(attr) or not Sl4aSl4aBaseTestClass.__is_entry_function(name):
173            return attr
174
175        @wraps(attr)
176        def __wrapped(*args, **kwargs):
177            try:
178                return attr(*args, **kwargs)
179            except RpcError as e:
180                exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__))
181                raise signals.TestFailure("RpcError during test\n\nRpcError:\n\n%s" % (exception_info))
182
183        return __wrapped
184
185    __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"}
186
187    @staticmethod
188    def __is_entry_function(name):
189        return name.startswith("test_") or name in Sl4aSl4aBaseTestClass.__ENTRY_METHODS
190