1# Copyright (C) 2023 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import logging 16import pprint 17import time 18 19from utilities import constants 20from mobly import asserts 21from mobly.controllers import android_device 22from utilities.media_utils import MediaUtils 23 24# Number of seconds for the target to stay discoverable on Bluetooth. 25DISCOVERABLE_TIME = 60 26TIME_FOR_PROMPT_TO_LOAD = 2 27ALLOW_TEXT = "Allow" 28 29 30class BTUtils: 31 """A utility that provides access to Bluetooth connectivity controls.""" 32 33 def __init__(self, discoverer, target): 34 self.discoverer = discoverer 35 self.target = target 36 self.target_adrr = None 37 self.media_utils = MediaUtils(self.target, self.discoverer) 38 39 # Disable Android Auto pop-up on HU 40 def disable_android_auto_popup_on_hu(self): 41 logging.info('Disable Android Auto pop-up on HU with adb') 42 self.media_utils.execute_shell_on_hu_device(constants.DISABLE_ANDROID_AUTO_POP_UP) 43 44 # Skip Assistant pop-up 45 # TODO @vitalidim remove this function after b/314386661 resolved 46 def handle_assistant_pop_up(self): 47 logging.info('Checking for Assistant pop-up on HU') 48 if self.discoverer.mbs.isAssistantImprovementPopUpPresent(): 49 logging.info('Assistant pop-up is present on HU') 50 logging.info('Click on <CONTINUE> button on HU') 51 self.discoverer.mbs.skipImprovementCallingAndTextingPopUp() 52 asserts.assert_false(self.discoverer.mbs.isAssistantImprovementPopUpPresent(), 53 'Assistant pop-up should be closed') 54 else: 55 logging.info('Assistant pop-up is not present on HU') 56 57 def get_info_from_devices(self, discovered_devices): 58 discovered_names = [device['Name'] for device in discovered_devices] 59 discovered_addresses = [device['Address'] for device in discovered_devices] 60 return discovered_names, discovered_addresses 61 62 def discover_secondary_from_primary(self): 63 target_name = self.target.mbs.btGetName() 64 self.target.log.info('Become discoverable with name "%s" for %ds.', 65 target_name, DISCOVERABLE_TIME) 66 self.target.mbs.btBecomeDiscoverable(DISCOVERABLE_TIME) 67 self.discoverer.log.info('Looking for Bluetooth devices.') 68 discovered_devices = self.discoverer.mbs.btDiscoverAndGetResults() 69 self.discoverer.log.debug('Found Bluetooth devices: %s', 70 pprint.pformat(discovered_devices, indent=2)) 71 discovered_names, _ = self.get_info_from_devices(discovered_devices) 72 logging.info('Verifying the target is discovered by the discoverer.') 73 asserts.assert_true( 74 target_name in discovered_names, 75 'Failed to discover the target device %s over Bluetooth.' % 76 target_name) 77 78 def pair_primary_to_secondary(self): 79 """Enable discovery on the target so the discoverer can find it.""" 80 self.check_device_pairing_state() 81 # Turn bluetooth on in both machines 82 logging.info('Enabling Bluetooth logs') 83 self.enable_bt_logs() 84 logging.info('Enabling Bluetooth on both devices') 85 self.discoverer.mbs.btEnable() 86 self.wakeup_target_device_screen() 87 self.target.mbs.btEnable() 88 self.disable_android_auto_popup_on_hu() 89 logging.info('Setting devices to be discoverable') 90 self.target.mbs.btBecomeDiscoverable(DISCOVERABLE_TIME) 91 self.target.mbs.btStartAutoAcceptIncomingPairRequest() 92 target_address = self.target.mbs.btGetAddress() 93 logging.info('Scanning for discoverable devices') 94 # Discovery of target device is tried 5 times. 95 discovered_devices = self.discoverer.mbs.btDiscoverAndGetResults() 96 self.discoverer.mbs.btPairDevice(target_address) 97 logging.info('Allowing time for contacts to sync') 98 time.sleep(constants.SYNC_WAIT_TIME) 99 self.press_allow_on_phone() 100 paired_devices = self.discoverer.mbs.btGetPairedDevices() 101 _, paired_addresses = self.get_info_from_devices(paired_devices) 102 asserts.assert_true( 103 target_address in paired_addresses, 104 'Failed to pair the target device %s over Bluetooth.' % 105 target_address) 106 time.sleep(constants.DEFAULT_WAIT_TIME_FIVE_SECS) 107 self.handle_assistant_pop_up() 108 logging.info("BT pairing completed.") 109 110 def unpair(self): 111 # unpair Discoverer device from Target 112 logging.info("Unpair Discoverer device from Target") 113 discoverer_address = self.discoverer.mbs.btGetAddress() 114 logging.info(f"Discoverer device address: {discoverer_address}") 115 target_paired_devices = self.target.mbs.btGetPairedDevices() 116 _, target_paired_addresses = self.get_info_from_devices(target_paired_devices) 117 logging.info(f"Paired devices to Target: {target_paired_devices}") 118 if discoverer_address in target_paired_addresses: 119 logging.info(f"Forget Discoverer device <{discoverer_address}> on Target device") 120 self.target.mbs.btUnpairDevice(discoverer_address) 121 else: 122 logging.error("Discoverer device not founded on Target device") 123 # unpair Target device from Discoverer 124 logging.info("Unpair Target device from Discoverer") 125 target_address = self.target.mbs.btGetAddress() 126 logging.info(f"Target device address: {target_address}") 127 discoverer_paired_devices = self.discoverer.mbs.btGetPairedDevices() 128 _, discoverer_paired_addresses = self.get_info_from_devices( 129 discoverer_paired_devices) 130 logging.info(f"Paired devices to Discoverer: {discoverer_paired_devices}") 131 if target_address in discoverer_paired_addresses: 132 logging.info(f"Forget Target device <{target_address}> on Discoverer device") 133 self.discoverer.mbs.btUnpairDevice(target_address) 134 else: 135 logging.error("Target device not founded on Discoverer device") 136 137 def press_allow_on_phone(self): 138 """ Repeatedly presses "Allow" on prompts until no more prompts appear""" 139 logging.info('Attempting to press ALLOW') 140 while (self.target.mbs.hasUIElementWithText(ALLOW_TEXT)): 141 self.target.mbs.clickUIElementWithText(ALLOW_TEXT) 142 logging.info('ALLOW pressed!') 143 time.sleep(TIME_FOR_PROMPT_TO_LOAD) 144 145 def bt_disable(self): 146 """Disable Bluetooth on a device.""" 147 self.discoverer.mbs.btUnpairDevice(self.target_adrr) 148 self.discoverer.mbs.btDisable() 149 self.target.mbs.btDisable() 150 151 def click_on_use_bluetooth_toggle(self): 152 logging.info('Click on Use Bluetooth toggle on HU') 153 self.discoverer.mbs.clickOnBluetoothToggle() 154 155 def enable_bt_logs(self): 156 logging.info('Enable bluetooth logs') 157 self.media_utils.execute_shell_on_hu_device(constants.BLUETOOTH_TAG) 158 self.media_utils.execute_shell_on_hu_device(constants.BLUETOOTH_NOOPERABLE) 159 self.media_utils.execute_shell_on_hu_device(constants.BLUETOOTH_BTSNOOP_DEFAULT_MODE) 160 161 def check_device_pairing_state(self): 162 logging.info('Checking the bt pairing status') 163 if self.discoverer.mbs.btGetPairedDevices() : 164 logging.info('Device is still paired, unpair the device') 165 self.unpair() 166 167 def is_target_device_screen_on(self): 168 logging.info('Checking the target screen state') 169 return 'Awake' in self.media_utils.execute_shell_on_device(constants.DUMPSYS_POWER).decode('utf8') 170 171 def wakeup_target_device_screen(self): 172 if not self.is_target_device_screen_on(): 173 logging.info('Target screen is off, waking it up') 174 self.media_utils.execute_shell_on_device(constants.KEYCODE_WAKEUP) 175