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 importlib
18import traceback
19import os
20import logging
21
22from functools import wraps
23from grpc import RpcError
24
25from acts import signals
26from acts.base_test import BaseTestClass
27from acts.context import get_current_context
28from acts.controllers.adb_lib.error import AdbCommandError
29
30from cert.ble_lib import enable_bluetooth, disable_bluetooth
31from cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME
32from cert.ble_lib import BleLib
33from facade import rootservice_pb2 as facade_rootservice
34
35
36class GdSl4aBaseTestClass(BaseTestClass):
37
38    SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
39
40    def setup_class(self, cert_module):
41        self.log_path_base = get_current_context().get_full_output_path()
42        self.verbose_mode = bool(self.user_params.get('verbose_mode', False))
43        for config in self.controller_configs[CONTROLLER_CONFIG_NAME]:
44            config['verbose_mode'] = self.verbose_mode
45        self.cert_module = cert_module
46
47        # Parse and construct GD device objects
48        self.register_controller(importlib.import_module('cert.gd_device'), builtin=True)
49        self.dut = self.android_devices[0]
50        self.cert = self.gd_devices[0]
51
52        # Enable full btsnoop log
53        self.dut.adb.shell("setprop persist.bluetooth.btsnooplogmode full")
54        getprop_result = self.dut.adb.shell("getprop persist.bluetooth.btsnooplogmode") == "full"
55        if not getprop_result:
56            self.dut.log.warning("Failed to enable Bluetooth Hci Snoop Logging.")
57
58        self.ble = BleLib(log=self.log, dut=self.dut)
59
60    def teardown_class(self):
61        pass
62
63    def setup_test(self):
64        self.cert.rootservice.StartStack(
65            facade_rootservice.StartStackRequest(
66                module_under_test=facade_rootservice.BluetoothModule.Value(self.cert_module),))
67        self.cert.wait_channel_ready()
68
69        self.timer_list = []
70        self.dut.ed.clear_all_events()
71        self.dut.droid.setScreenTimeout(500)
72        self.dut.droid.wakeUpNow()
73
74        # Always start tests with Bluetooth enabled and BLE disabled.
75        self.dut.droid.bluetoothDisableBLE()
76        disable_bluetooth(self.dut.droid, self.dut.ed)
77        # Enable full verbose logging for Bluetooth
78        self.dut.adb.shell("device_config put bluetooth INIT_logging_debug_enabled_for_all true")
79        # Then enable Bluetooth
80        enable_bluetooth(self.dut.droid, self.dut.ed)
81        self.dut.droid.bluetoothDisableBLE()
82        return True
83
84    def teardown_test(self):
85        # Make sure BLE is disabled and Bluetooth is disabled after test
86        self.dut.droid.bluetoothDisableBLE()
87        disable_bluetooth(self.dut.droid, self.dut.ed)
88        self.cert.rootservice.StopStack(facade_rootservice.StopStackRequest())
89
90        # TODO: split cert logcat logs into individual tests
91        current_test_dir = get_current_context().get_full_output_path()
92
93        # Pull DUT logs
94        self.pull_dut_logs(current_test_dir)
95
96        # Pull CERT logs
97        self.cert.pull_logs(current_test_dir)
98
99    def pull_dut_logs(self, base_dir):
100        try:
101            self.dut.pull_files("/data/misc/bluetooth/logs/btsnoop_hci.log",
102                                os.path.join(base_dir, "DUT_%s_btsnoop_hci.log" % self.dut.serial))
103            self.dut.pull_files("/data/misc/bluedroid/bt_config.conf",
104                                os.path.join(base_dir, "DUT_%s_bt_config.conf" % self.dut.serial))
105            self.dut.pull_files("/data/misc/bluedroid/bt_config.bak",
106                                os.path.join(base_dir, "DUT_%s_bt_config.bak" % self.dut.serial))
107        except AdbCommandError as error:
108            logging.warning("Failed to pull logs from DUT: " + str(error))
109
110    def __getattribute__(self, name):
111        attr = super().__getattribute__(name)
112        if not callable(attr) or not GdSl4aBaseTestClass.__is_entry_function(name):
113            return attr
114
115        @wraps(attr)
116        def __wrapped(*args, **kwargs):
117            try:
118                return attr(*args, **kwargs)
119            except RpcError as e:
120                exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__))
121                raise signals.TestFailure("RpcError during test\n\nRpcError:\n\n%s" % (exception_info))
122
123        return __wrapped
124
125    __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"}
126
127    @staticmethod
128    def __is_entry_function(name):
129        return name.startswith("test_") or name in GdSl4aBaseTestClass.__ENTRY_METHODS
130